一.Spring Cloud 框架概述
1.基本介绍
SpringCloud是目前国内使用最广泛的微服务框架之一。官网地址:https://spring.io/projects/spring-cloud。
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
如下图:
Spring Boot最擅长的事情就是自动装配,而Spring Cloud就是把那些官方原生开源的一些组件给整合进来了,并且基于Spring Boot做了自动装配。那你只要拿过来就能用,而无需复杂的配置。之后我会逐一地去学习这些组件。
2.Spring Cloud与Spring Boot的版本兼容
下图就是我刚刚在官网截的一张版本兼容的示意图,也可以到官网自己去查看:
左边的每一个Spring Cloud版本,右边都有一个对应的Spring Boot版本。比如说,你们公司用了Spring Cloud的Greenwich版本,那你的Spring Boot就必须是2.1.x的版本。如果你用了其他的版本,你也要选择右边对应的Spring Boot版本号。如果使用的版本不一致,将来运行时可能会报错。
我接下来学习的使用的版本是Hoxton的版本,所以使用的Spring Boot版本就要用2.2.x或者时2.3.x的版本。
二.Spring Cloud 框架入门使用
前言:
从这里开始,会理论与操作相结合来学习Spring Cloud框架。我本篇用到的案例代码我会上传到CSDN的资源中去,我已经开启了免费下载,欢迎大家下载学习。
1.服务拆分
先来了解一下服务拆分的细节和注意事项,服务拆分说起来很简单。一个单体架构,我们按照功能模块进行拆分,变成多个服务就行了。比如下图的4个模块,我们就拆分为4个服务。当然。我们在实际生产中,单个模块的功能可能会越来越多,我们还会继续拆分。
但是单体应用开发的多了,很多人可能会产生一种思维定势,容易犯一些错。这里总结一下:比如说,我现在有一个需求,是查询订单,同时把订单里面关联的用户信息,商品信息都给它查出来。如果是以前的开发模式,我们肯定是写一个方法去查询订单,在订单的查询过程中得到了用户Id,然后去数据库里面把用户查出来,得到了商品Id我再去把商品查出来。那么这个功能全部写到了订单的模块里面。这种写法是完全违背了我们的微服务的原则的。微服务拆分的目的就是单一职责,只做与自己相关的事情。订单模块就做订单业务,就不要去做用户查询等非订单模块的操作。我们的每一个微服务都不能去开发重复的业务,如果在你的微服务中出现了重复的业务,这就证明你的某些地方可能做得有问题。
为了做好这些,我们还会有一些要求。比如说我们微服务的数据要独立,一个微服务不要访问其他的微服务的数据库 。每个微服务都会有自己的数据库,用户功能的数据库里就存放的是用户相关的信息,别的都不存。订单模块的数据库里面存放的自然就是订单信息。这个时候你在做订单相关的业务时,如果要查用户信息,它自己的数据库里面没有。这就降低了业务的耦合。
微服务在拆分的时候还要注意一些事情,就是微服务可以将自己的业务暴露为接口,供其他的微服务使用。比方说我的用户有用户查询功能,如果订单需要,那我就暴露成一个接口,需要的时候发请求就可以访问其他微服务了。
2.案例准备
下面我们就通过一个案例来具体学习:
大家提前在下载的资料里找到cloud-demo(资料文件夹里面14k大小的那个压缩包,不是代码文件夹的那个完整版哦!),解压后会得到一个cloud-demo的项目。我们把它用idea打开就行了。
下面我先来介绍一下这个项目的结构:
我们这个项目会有上图的结构,父工程叫做cloud-demo,负责管理整个项目的依赖,下面有两个模块,分别是order-service和user-service。order-service里面做订单相关的内容(比如根据id查询订单),user-service里做用户相关的功能(比如根据id查询用户),这两个模块就是将来我们的两个微服务。并且我还为这两个服务准备了各自的表。(将来我们生产环境时,一定会把他们部署到不同的数据库服务器里面,只是这里我们学习,就在同一台数据库里了。)
大家的资源里面会有如下的俩个sql文件:
下面我们先来做一些项目开始的准备工作:
(1)随便打开一个你安装的数据库可视化工具,然后分别创建两个数据库cloud_order和cloud_user。
(2)两个数据库分别运行导入上面的两个sql,把表导入进去。
我们来看一下这个cloud_demo父工程的pom.xml文件。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.haiexijun.demo</groupId> <artifactId>cloud-demo</artifactId> <version>1.0</version> <modules> <module>user-service</module> <module>order-service</module> </modules> <packaging>pom</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.9.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>11</java.version> <spring-cloud.version>Hoxton.SR10</spring-cloud.version> <mysql.version>8.0.25</mysql.version> <mybatis.version>2.1.1</mybatis.version> </properties> <dependencyManagement> <dependencies> <!-- springCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> </project>
这里有两点要注意的就是你拿到项目之后要根据自己电脑里面装的java版本和mysql的版本在pom.xml文件里面的properties里更改对应的版本号。我的电脑的java版本时jdk11,mysql的版本是8.0.25。
下面是子工程里的pom.xml ,我这里也列在下面给大家看看:
user-service子项目:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud-demo</artifactId> <groupId>com.haiexijun.demo</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>user-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> </dependencies> <build> <finalName>app</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
order-service子项目:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud-demo</artifactId> <groupId>com.haiexijun.demo</groupId> <version>1.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>order-service</artifactId> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
然后我们来看一下这俩个子项目的具体内容:
先从来看user-service的yml配置文件:
server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false username: root password: zc20020106 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: type-aliases-package: com.haiexijun.user.pojo configuration: map-underscore-to-camel-case: true logging: level: com.haiexijun: debug pattern: dateformat: MM-dd HH:mm:ss:SSS
启动类上要加上@MapperScan注解(日常mybatis研发,需要在每个interface配置@Mapper,为了开发简便使用@MapperScan可以指定要扫描的Mapper类的包的路径)
package com.haiexijun.user; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.mybatis.spring.annotation.MapperScan; @MapperScan("com.haiexijun.user.mapper") @SpringBootApplication public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
然后下面是web包下的controller类的相关代码:
package com.haiexijun.user.web; import com.haiexijun.user.pojo.User; import com.haiexijun.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * 路径: /user/110 * * @param id 用户id * @return 用户 */ @GetMapping("/{id}") public User queryById(@PathVariable("id") Long id) { return userService.queryById(id); } }
service层的代码:
package com.haiexijun.user.service; import com.haiexijun.user.mapper.UserMapper; import com.haiexijun.user.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserService { @Autowired private UserMapper userMapper; public User queryById(Long id) { return userMapper.findById(id); } }
pojo和mapper:
package com.haiexijun.user.pojo; import lombok.Data; @Data public class User { private Long id; private String username; private String address; }
package com.haiexijun.user.mapper; import com.haiexijun.user.pojo.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import org.springframework.stereotype.Repository; @Repository public interface UserMapper { @Select("select * from tb_user where id = #{id}") User findById(@Param("id") Long id); }