真烦人,怎么老要我增加新业务,幸好我用了微服务SpringCloud,轻松搞定
微服务的概念
所谓微服务,它其实就是一种架构方式。
我举个例子,我们以前是经常以SpringBoot项目进行创建对吧,在这个SpringBoot项目,我们常常写了大量的业务功能,如我们需要创建映射大量数据表的实体类,接着创建关于这些实体类的数据访问层(dao)的类用来封装它们的CRUD等功能。然后创建有关实体类的业务逻辑层(service)的类用来实现它们具体的业务逻辑方法等等,我们把所有要创建的业务都放在一个项目中进行实现,这就是一种集中式架构方式,是一种高耦合、开发效率低的一种架构方式。
当然如果业务量少的话,采用集中式架构方式没有任何问题。可一旦业务量骤增,功能之间耦合性也越来越高,这样不利于后期维护。所以微服务就此应运而生。
微服务架构,顾名思义,就是每个服务基于单一业务进行构建,直接点说就是一个项目中就一个业务,运行在自己独立的进程中。每个服务都可以使用不同的编程语言实现以及不同数据存储技术。
微服务的特点
单一职责:微服务中每一个服务都对应唯一的业务,做到单一职责。
面向服务:每个服务都要对外暴露服务接口API,并不关心服务的技术实现和编程语言,只要使用REST的接口即可。
自治独立:服务之间互不干扰,互不影响。
SpringCloud与微服务的关系
SpringCloud目前是微服务架构中最主流的一项技术。
至于为什么主流?在我之前的博客中也提到过,因为SpringCloud也属于Spring旗下的项目之一,而Spring旗下还有SpringBoot,SpringBoot正是凭借着它能够快速构建起一个Sping应用程序的优点,故而成就了SpringCloud,而SpringCloud也带动了SpringBoot。同时SpringCloud也擅长集成,可以把适合的框架都拿过来,集成到自己的项目中,可以把非常流行的技术整合到一起。比如:配置管理、服务发现、智能路由、负载均衡、熔断器、控制总线、集群状态等等一些功能。
SpringCloud如何使用
一、创建父项目,并添加依赖
微服务中是需要创建多个项目,所以在此基础上先创建一个父项目,后续的项目将以这个项目为父,使用Maven的聚合和继承,统一管理子项目的版本和配置。
创建父项目完成后,添加以下依赖:
<groupId>org.example</groupId>
<artifactId>SpringCloudA</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>UserService</module>
<module>consumerDemo</module>
<module>eurekaServer</module>
<module>lxsGateway</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
<mapper.starter.version>2.1.5</mapper.starter.version>
<mysql.version>5.1.46</mysql.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>
<!-- 通用Mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mapper.starter.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
这里需要注意SpringCloud和SpringBoot的版本一定要相对应。
SpringCloud和SoringBoot版本对应关系如下:
Hoxton 2.2.x.RELEASE
Greenwich 2.1.x.RELEASE
Finchley 2.0.x.RELEASE
Edgware 1.5.x.RELEASE
Dalston 1.5.x.RELEASE
我们现在使用的是Greenwich版本的SpringCloud和2.1.5.RELEASE版本的SpringBoot。
二、创建一个子项目:服务提供者
服务提供者,就是用来对外提供服务。
我们现在创建一个提供查询用户的服务,选中父项目,创建SpringBoot子项目。
添加以下依赖:
<parent>
<artifactId>SpringCloudA</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>UserService</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 通用Mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencys>
编写配置文件
添加完成后,编写配置文件,配置好数据源、端口、Mybatis扫描包等信息。application.yml如下:
server:
port: 9091
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud
username: root
password: 1234
application:
name: userService
mybatis:
type-aliases-package: com.xx.user.pojo
User业务功能的代码编写
创建映射数据表的实体类User,这步我省略了,大家按照自己的数据表去创建就行了。
接着就是Mapper映射接口,这步我也省略了,大家看看我前面写Mybatis-plus就知道啦。
然后就是创建Service类,代码如下:
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public User queryById(Long id){
return userMapper.selectByPrimaryKey(id);
}
}
最后就是Controller类了,注意要对外提供REST接口。
代码如下:
@RestController
@RequestMapping("user")
public class UserController {
@Resource
private UserService userService;
@GetMapping("{id}")
public User queryById(@PathVariable Long id){
return userService.queryById(id);
}
}
好了,一个服务提供者的项目业务功能就写到这里了,就一个业务,是提供查询用户的业务。
三、创建一个子项目:服务消费者
服务消费者,顾名思义,它的业务就是用来访问服务提供者提供的服务的。
首先也是创建一个项目,然后添加依赖。
添加以下依赖
<parent>
<artifactId>SpringCloudA</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumerDemo</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependencies>
在依赖添加完成后。就该创建访问业务了。
消费服务的代码编写
首先需要在启动器类中注入RestTemplate类,此类对基于Http的客户端进行了封装,实现了对象与json的序列化和反序列化,非常方便。注入代码如下:
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
然后创建实体类,这个实体类无需映射数据表,只是用来实现对象序列化的。这步省略。
然后创建controller类,以此访问服务提供者提供的服务。
代码如下:
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/{id}")
public User queryById(@PathVariable Long id){
String url = "Http://localhost:9091/user/" + id;
return restTemplate.getForObject(url, User.class);
}
}
测试
先启动服务提供者子项目,再启动服务消费者子项目,然后访问服务消费者的地址:http://localhost:8080/consume/1,由此访问到服务提供者地址http://localhost:9091/user/1提供的数据。
四、创建一个子项目:Eureka服务注册
服务注册的作用
在以上服务提供者和服务消费者创建完成并测试后,大家有没有发现什么不妥的地方啊!
不妥之处在于服务消费者需要精准地知道服务提供者提供的服务地址,如若服务提供者提供的服务将来地址需要改变,那么服务消费者也需要及时跟着改变,否则就容易报错。这在服务较少的时候不觉得有什么,可一旦多了起来,一个项目拆分出数十个、数百个微服务,此时如果依然使用这种方式,那么以后维护起来将会非常麻烦。
此时需要Eureka注册中心,对所有服务提供者提供的服务进行注册,那么服务消费者就无需自己再寻找服务,而是把自己的需求直接告诉Eureka,然后Eureka根据所提供的需求直接把服务告诉服务消费者。同时Eureka与服务提供者之间还会通过心跳机制进行监控,一旦某个服务提供方出现问题,Eureka会把它从服务列表中剔除掉。
由此Eureka服务注册为我们实现了服务的注册、发现、监控等功能。
创建Eureka子项目,添加依赖如下:
<parent>
<artifactId>SpringCloudA</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eurekaServer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
编写配置文件
主要编写端口、Eureka服务地址。
server:
port: 10086
spring:
application:
name: eurekaServer
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
server:
# 不注册自己
register-with-eureka: false
# 不拉取服务
fetch-registry: false
服务提供者如何把提供的服务注册到Eureka
添加Eureka客户端依赖
首先给服务提供者子项目添加依赖,主要是添加Eureka客户端依赖;自动将服务注册到EurekaServer服务地址列表。
添加依赖如下:
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
添加@EnableDiscoveryClient来开启Eureka客户端发现功能
在服务提供者子项目的启动类上添加即可。
编写配置文件
一、添加好Spring.application.name属性来指定应用名称,将来会作为应用的id使用。
二、添加eureka.client.service-url.defaultZone属性来指定注册的Eureka服务地址。
在服务提供者子项目的配置文件添加如下:
server:
port: 9091
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud
username: root
password: 1234
application:
name: userService
mybatis:
type-aliases-package: com.xx.user.pojo
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
好了,到此服务提供者就把服务已经注册成功了。
服务消费者如何把需要的服务告诉给Eureka
添加Eureak客户端依赖,添加@EnableDiscoveryClient注解
这两步和上述的步骤一样,不过是在服务消费者子项目进行操作。
修改配置
对服务消费者子项目的配置文件进行配置,配置如下:
spring:
application:
name: consumerDemo
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
使用DiscoveryClient类的方法,根据服务名称,获取服务实例。
在服务消费者子项目的controller类中,创建DiscoveryClient类,根据服务名称,获取服务实例。
修改代码如下:
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id) {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("userService");
ServiceInstance serviceInstance = serviceInstances.get(0);
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/user/"
+ id;
return restTemplate.getForObject(url, User.class);
}
}
好了,到此服务消费者就把需要的服务告诉给Eureka,通过服务名称,获取到最终的服务地址。
当然也无需用手动获取ip和端口,如果觉得很麻烦,也可以直接通过服务名称调用。如:
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id) {
String url = "http://userService/user/" + id;
return restTemplate.getForObject(url, User.class);
}
后续
SpringCloud还有许多组件,如:负载均衡Ribbon、熔断器Hystrix、服务调用Feign等等
由于篇幅有限,留待下期。