1. 项目整体架构
2. 项目编码实现
2.1. 创建department-service (微服务)
直接浏览器访问:https://start.spring.io/,通过勾选填写项目配置信息,并在线搜索需要的第三方依赖,生成springboot项目源码
department-service微服务项目选用h2内存数据库,持久层框架选用JPA。
添加配置文件application.yml设置服务启动端口。
简单创建web项目的几个目录结构 controller、entity、repository、service。
创建两个rest api,分别是saveDepartment 和 findDepartmentById:
@RestController
@RequestMapping("/departments")
@Slf4j
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
@PostMapping("/")
public Department saveDepartment(@RequestBody Department department){
log.info("Inside saveDepartment method of DepartmentController");
return departmentService.saveDepartment(department);
}
@GetMapping("/{id}")
public Department findDepartmentById(@PathVariable("id") Long departmentId){
log.info("Inside findDepartmentById method of DepartmentController");
return departmentService.findDepartmentById(departmentId);
}
通过postman测试api访问正常😀
2.2. 创建user-service (微服务)
创建的步骤与上述2.1基本一致。
差异:
多创建了一个VO的目录,一个是Department的实体类,一个是包含Users和Department两个的实体类ResponseTemplateVO。
同样创建两个rest api,分别是saveUser和getUserWithDepartment:
@RestController
@RequestMapping("/users")
@Slf4j
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/")
public Users saveUser(@RequestBody Users user){
log.info("Inside saveUser of UserController");
return userService.saveUser(user);
}
@GetMapping("/{id}")
public ResponseTemplateVO getUserWithDepartment(@PathVariable("id") Long userId){
log.info("Inside getUserWithDepartment of UserController");
return userService.getUserWithDepartment(userId);
}
}
其中,getUserWithDepartment接口是通过userId查询user和department的组合数据,因为user表中有外键departmentId,可以关联查询到用户的所属部门信息。但是由于查询部门信息的接口在department-service中,所以user-service中注册了一个restTemplete的bean,通过restTemplate.getForObject()方法请求远程department-service的接口,获取数据。
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
// 向Spring容器中注册restTemplate的bean
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
}
public ResponseTemplateVO getUserWithDepartment(Long userId) {
log.info("Inside getUserWithDepartment of UserService");
ResponseTemplateVO vo = new ResponseTemplateVO();
Users user = userRepository.findByUserId(userId);
// 通过RestTemplate请求远程服务上的get接口,获取用户的部门信息 [同步的RMI调用]
Department department = restTemplate.getForObject("http://localhost:9001/departments/"+user.getDepartmentId(),
Department.class);
vo.setUser(user);
vo.setDepartment(department);
return vo;
}
⚠ 注意:新版本的springBoot中,启动会报错。因为创建了一个名为User的实体,可能跟springBoot内部的实体类名称冲突了,之后改成了Users,启动成功。
2.3. 创建service-registry服务(注册中心)
service-registry即eureka服务端,是微服务系统中的注册。本质是一个普通的springboot-web项目。引入了eureka-server的依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
而且需要在配置文件中添加eureka server相关的配置:
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
还需要在启动类头部添加注解 **@EnableEurekaServer **** **,项目启动时会激活eureka-server相关的配置。
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.class, args);
}
}
还需在department-service和user-service中添加eureka client相关依赖,并在各自的application.yml中添加eureka client的相关配置。然后启动两个服务,它们会作为服务提供者,将服务的名称,访问地址等信息注册到service-registry中。
先启动service-registry服务,再启动department-service和user-service,浏览器访问:http://localhost:8761/, 可以打开默认的Eureka可视化监控界面:
现在,可以将user-service中的RMI调用换成:
Department department = restTemplate.getForObject("http://DEPARTMENT-SERVICE/departments/"+user.getDepartmentId(),
Department.class);
请求的ip和port换成了应用名,这样请求还不能生效,需要在restTemplete的方法上添加一个注解**@LoadBalanced **** **,表示通过restTemplate发起的RMI请求会通过应用名进行请求的负载均衡。
修改这两处之后,先调用department的添加接口:
再调用user的添加接口:
最后调用user的查询接口:
请求响应正常,而且返回了user和department的数据。
2.4. 创建cloud-gateway(网关)
注意:pom文件种引入的spring-cloud依赖,需要注意springboot版本和springcloud版本的匹配
修改application.yml文件,添加路由配置:
server:
port: 9191
spring:
application:
name: API-GATEWAY
cloud:
gateway:
routes:
- id: USER-SERVICE
uri: lb://USER-SERVICE
predicates:
- Path=/users/**
- id: DEPARTMENT-SERVICE
uri: lb://DEPARTMENT-SERVICE
predicates:
- Path=/departments/**
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
hostname: localhost
启动类头部添加**@EnableEurekaClient **** **注解
启动后,cloud-gateway也会作为一个微服务注册到eureka-server中。
由于在cloud-gateway中配置了department和user微服务的访问路由。客户端对于各个微服务接口的请求都通过网关服务请求,即可以通过网关服务的ip和port以及路由 ==> localhost:9191/xxx/xx 访问department或user服务的api。
为了防止某个微服务挂掉,导致网关请求失败,可以在网关服务中添加熔断配置。
- 首先需引入hystrix依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
<version>2.1.4</version>
</dependency>
- 然后添加熔断相关的配置:
hystrix:
command:
fallbackcmd:
execution:
isolation:
thread:
timeoutInMillseconds: 4000
management:
endpoints:
web:
exposure:
include: hystrix.stream
- 需要在启动类头部添加一个注解 **@EnableHystrix **** **。
- 创建一个熔断回调方法的controller,简单定义两个请求department-service和user-service服务失败的回调方法:
/**
* 定义两个微服务Department-Service和User-Service的熔断方法
* @author lixing
* @since 2022/10/10
*/
@RestController
public class FallBackMethodController {
@GetMapping("/userServiceFallBack")
public String userServiceFallBackMethod(){
return "User Service is taking longer than Expected. Please try again later";
}
@GetMapping("/departmentServiceFallBack")
public String departmentServiceFallBackMethod(){
return "Department Service is taking longer than Expected. Please try again later";
}
}
如关闭department-service服务,然后在postman中通过GET请求 http://localhost:9191/departments/1 ,会发现响应内容为 “Department Service is taking longer than Expected. Please try again later”,即调用了熔断回调方法
2.5. 创建hystrix-dashboard (流量监控)
该项目核心依赖有两个:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
启动类上需要添加两个注解:@EnableEurekaClient 和 @EnableHystrixDashboard
指定一个端口9295,启动成功后在浏览器访问:http://localhost:9295/hystrix 可以进入流量监控主页
浏览器访问一个网关的端点:http://localhost:9191/actuator/hystrix.stream
然后将端点地址输入到 hystrix dashboard中,点击下方按钮进行流监控。之后使用postman通过网关访问微服务接口,再去流监控面板,可以看到请求的流监控信息:
2.6. 创建cloud-server (分布式配置中心)
分布式配置中心是为了将多个微服务的配置进行集中管理,集中管理到github或其他仓库中。
可以在程序运行过程中,对配置进行动态调整。
可以关闭不同环境下的不同配置,对配置进行更新和部署。
如果系统配置文件发生了变化,无需重启服务器,配置中心就能够自动感知到变化,并将新的变化统一发送到相应程序上。
cloud-server项目核心配置有两个:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
启动类头部添加两个注解:@EnableEurekaClient 和 @EnableConfigServer 指定启动端口9296,和应用名称 CONFIG-SERVER
然后在github上创建一个仓库,添加一个公共配置文件 aplication.yml
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
hostname: localhost
将仓库的地址配置到cloud-server项目的配置文件中:
server:
port: 9296
spring:
application:
name: CONFIG-SERVER
cloud:
config:
server:
git:
uri: https://github.com/paopaolx/config-server
clone-on-start: true
然后在使用了公共配置的几个服务 [department-service,user-service,cloud-gateway,hystrix-dashboard] 中添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
并创建一个新的配置文件 bootstrap.yml
spring:
cloud:
config:
enabled: true
uri: http://localhost:9296
即使用cloud-service中指定仓库中的公共配置
later~~~
再次启动所有服务,通过网关访问微服务的接口,依然正常
2.7. 使用zipkin(分布式链路追踪)
zipkin服务,直接在官网下载最新稳定版本的jar包:
然后通过 java -jar zipkin.jar 启动,启动成功后会出现一个访问地址:http://127.0.0.1:9411/
接下来,就是在微服务项目中进行zipkin相关的配置了。
在department-service和user-service中添加两个依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>3.1.4</version>
</dependency>
然后在配置文件中配置zipkin的地址:
spring:
zipkin:
base-url: http://127.0.0.1:9411/
重新启动,然后通过网关访问这两个微服务中的接口,观察微服务中的日志打印,其中会有链路请求的id
前面的序号id不同代表是两次不同的链路请求。而且在zipkin可视化界面中通过填写指定的服务名称,也可以可视化追踪请求调用的链路,全过程。
3. 总结
整个通过springboot开发微服务demo项目的过程其实并不复杂,每个模块主要的操作都是引入starter依赖和添加配置和注解,编码的过程并不多。主要是上层的组织,如何进行合理的配置将多个微服务模块组织起来,管理起来。