springcloud--微服务架构与单体应用架构的区别---以及微服务工程的基础搭建

445 阅读7分钟

单体应用架构

将项目所有模块(功能)打成jar或者war,然后部署一个进程

image.png

优点: 1:部署简单:由于是完整的结构体,可以直接部署在一个服务器上即可。 2:技术单一:项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发。

缺点: 1:系统启动慢,一个进程包含了所有的业务逻辑,涉及到的启动模块过多,导致系统的启动、重启时间周期过长;

2:系统错误隔离性差、可用性差,任何一个模块的错误均可能造成整个系统的宕机;

3:可伸缩性差:系统的扩容只能只对这个应用进行扩容,无法结合业务模块的特点进行伸缩。

4: 线上问题修复周期长:任何一个线上问题修复需要对整个应用系统进行全面升级。

5: 跨语言程度差

6: 不利于安全管理,所有开发人员都拥有全量代码。

小型项目适合单体应用架构. 比如:OA办公系统。管理类的项目 仓库管理系统。

微服务应用架构

微服务架构论文: martinfowler.com/articles/mi…

译文: blog.csdn.net/qq_42261668…

简单来说,微服务架构风格[1]是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。

一个项目被拆分若干个项目

每个项目可以独立运行

每个项目可以使用不同的语言开发

每个项目可以拥有独立的数据库

项目与项目之间可以通信[http协议]

image.png

解读微服务特点:

1:微服务是一种==项目架构思想==(风格)

2:微服务架构是一系列小服务的组合(组件化与多服务)

3:任何一个微服务,都是一个独立的进程(独立开发、独立维护、独立部署)

4:轻量级通信http协议(跨语言,跨平台)

5:服务粒度(围绕业务功能拆分---模块拆分【系统管理服务】【日志服务】【焦虑测试】【抑郁测试系统】)

1.微服务架构的优势

1.易于开发和维护 一个微服务只关注一个特定的业务功能,所以它的业务清晰、代码量较少。开发和维护单个微服务相对比较简单,整个应用是由若干个微服务构建而成,所以整个应用也会维持在可控状态;

⒉.单个微服务启动较快 单个微服务代码量较少,所以启动会比较快;

3.局部修改容易部署 单体应用只要有修改,就要重新部署整个应用,微服务解决了这样的问题。一般来说,对某个微服务进行修改,只需要重新部署这个服务即可;

4.技术栈不受限 在微服务中,我们可以结合项目业务及团队的特点,合理地选择技术栈

5.按需伸缩焦虑系统访问量大,只需要对焦虑系统进行扩展

微服务架构的缺点(挑战)

1、服务太多,导致服务间的依赖错综复杂,运维难度大

2、微服务放大了分布式架构的系列问题

  • 分布式事务(seata)
  • 分布式锁怎么处理(redisson) ,
  • 服务注册与发现(nacos) .
  • 依赖服务不稳定(sentinel)导致服务雪崩怎么办?

3、运维复杂度陡增,部署数量多、监控进程多导致整体运维复杂度提升。

如何解决上面的挑战----使用springcloud

SpringCloud与微服务关系

Springcloud为微服务思想提供了完美的解决方案

Springcloud是一系列框架的集合体(不同的框架解决微服务面临的不同的挑战)

springcloud提供了两款模式:

第一款:netflix公司

第二款:alibaba-阿里巴巴

SpringBoot和SpringCloud关系

SpringBoot专注于快速方便的开发单个个体微服务。

SpringCloud是关注全局的微服务协调、整理、治理的框架,它将SpringBoot开发的单体整合并管理起来。

SpringBoot可以离开SpringCloud独立使用开发项目,但是SpringCloud离不开SpringBoot,属于依赖关系。

创建微服务工程

image.png

技术栈:

mysql+mybatis[mp]+springboot+springcloud alibaba

订单数据库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for shop_order
-- ----------------------------
DROP TABLE IF EXISTS `shop_order`;
CREATE TABLE `shop_order`  (
  `oid` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '订单id',
  `uid` int(0) NULL DEFAULT NULL COMMENT '用户id',
  `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `pid` bigint(0) NULL DEFAULT NULL COMMENT '商品id',
  `pname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品名称',
  `pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格',
  `number` int(0) NULL DEFAULT NULL COMMENT '购买数量',
  PRIMARY KEY (`oid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 44956 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of shop_order
-- ----------------------------

SET FOREIGN_KEY_CHECKS = 1;

商品数据库

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for shop_product
-- ----------------------------
DROP TABLE IF EXISTS `shop_product`;
CREATE TABLE `shop_product`  (
  `pid` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '商品id',
  `pname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品名',
  `pprice` decimal(10, 2) NULL DEFAULT NULL COMMENT '商品价格',
  `stock` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '商品库存',
  PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of shop_product
-- ----------------------------
INSERT INTO `shop_product` VALUES (1, '华为手机', 1999.00, '100');
INSERT INTO `shop_product` VALUES (2, 'vivo手机', 2999.00, '100');
INSERT INTO `shop_product` VALUES (3, '小米', 2222.00, '1000');

SET FOREIGN_KEY_CHECKS = 1;

1.搭建父工程

1.创建一个普通的maven工程

pom文件内容

<packaging>pom</packaging>

<!--继承springboot父工程-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.2.RELEASE</version>
</parent>
<!--定义版本号-->
<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF- 8</project.reporting.outputEncoding>
    <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
</properties>
<!--dependencyManagement: 它只负责jar的管理 不负责jar的下载 子类使用该类型的jar包时,直接引用而无需写版本-->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>${spring-cloud-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

springboot 和 springcloud 以及springcloud alibaba他们的版本必须匹配

image.png

2.创建公共模块

image.png

1.依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

    </dependencies>

2.创建相应的实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "shop_product")
public class Product {

   @TableId(type = IdType.AUTO)
   private Integer pid;

   private String pname;

   private BigDecimal pprice;

   private String stock;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "shop_order")
public class Order {

    @TableId(type = IdType.AUTO)
   private Integer oid;

   private Integer uid;

   private String username;

   private Integer pid;

   private String pname;

   private BigDecimal pprice;

   private Integer number;

}

3.商品系统微服务

image.png

1.依赖


<dependencies>
    <!--公共模块引入-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>aaa-springcloud-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

2.配置文件

#端口号8001~8008
server.port=8001
#数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///cjj-shop-product?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
#sql日志打印控制台
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis映射文件
mybatis-plus.mapper-locations=classpath:/mapper/*.xml

3.注意启动类

@SpringBootApplication
@MapperScan("com.cjj.product.dao")
public class SpringcloudProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudProductApplication.class, args);
    }

}

4.dao接口

public interface ProductDao extends BaseMapper<Product> {
}

5.servcie层

@Service
public class ProductServerImpl implements ProductServer {

    @Autowired
    private ProductDao productDao;

    /**
     * 根据pid查询商品表
     * @param pid 商品id
     * @return  返回指定id的商品信息
     */
    @Override
    public Product findProductById(Integer pid) {
        final Product product = productDao.selectById(pid);
        return product;
    }
}

6.controller层

//返回的数据都是json格式
@RestController
//接收所有的http请求
@RequestMapping("product")
public class ProductController {

    @Autowired
    private ProductServer productServer;
    //地址栏传参,需要使用@PathVariable接收
    @GetMapping("/findProductById/{pid}")
    public Product findProductById(@PathVariable Integer pid){

        Product productById = productServer.findProductById(pid);
        return productById;

    }

}

4.订单系统微服务

image.png

1.依赖

<dependencies>
    <!--公共模块引入-->
    <dependency>
        <groupId>org.example</groupId>
        <artifactId>aaa-springcloud-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

2.配置文件

#端口号9001~9008
server.port=9002
#数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///cjj-shop-order?serverTimezone=Asia/Shanghai&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
#sql日志打印控制台
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#mybatis映射文件
mybatis-plus.mapper-locations=classpath:/mapper/*.xml

3.dao层

public interface OrderDao extends BaseMapper<Order> {
}

4.service层

@Service
public class OrderServerImpl implements OrderServer {

    @Autowired
    private OrderDao orderDao;

    @Override
    public Integer insertOrder(Order order) {
        final int insert = orderDao.insert(order);
        return insert;
    }
}

5.controller层

@RestController
@RequestMapping("order")
public class OrderController {

    @Autowired
    private OrderServer orderServer;
    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("insertOrder")
    public String insertOrder(Integer pid,Integer number){
        //构建一个订单对象
        final Order order = new Order();
        order.setUid(1);//用户id,这里只是作为测试,所以给个假数据,实际应用中,登录的session或者shiro安全框架中一定会有
        order.setUsername("cjj");//用户名同用户id同样的逻辑
        order.setNumber(number); //购买的数量
        order.setPid(pid);
        //这个时候我们需要获取用户购买的商品信息,如何获取?
        //使用http远程调用【在java端模拟浏览器调用】,
        //spring封装了http远程调用RestTemplate。默认该类没有交于spring容器管理,这个时候我们需要自己创建配置类,
        // 并且交给spring容器管理@Bean注解就可以做到

        /**
         *String url, 远程调用的服务器地址
         * Class<T> responseType, 远程调用的接口返回类型的反射类
         * Object... uriVariables 远程接口需要传递的参数
         */

        final Product product = restTemplate.getForObject("http://localhost:8001/product/findProductById/"+pid, Product.class);
        order.setPid(product.getPid());
        order.setPname(product.getPname());
        order.setPprice(product.getPprice());

        final Integer integer = orderServer.insertOrder(order);
        return integer>0?"下单成功":"下单失败";
    }
}

6.启动类


@SpringBootApplication
@MapperScan("com.cjj.order.dao")
public class SpringcloudOrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringcloudOrderApplication.class, args);
    }

    /**
     * 注入RestTemplate用来实现http远程调用
     * @return
     */
    @Bean
    public RestTemplate restTemplate(){
        final RestTemplate restTemplate = new RestTemplate();
        return  restTemplate;
    }
}

案例序章springcloud--nacos注册中心--Ribbon负载均衡--openfeign服务调用

springcloud--nacos注册中心--Ribbon负载均衡--openfeign服务调用 - 掘金 (juejin.cn)