1. 项目整体架构

微服务项目实战pdf 简单的微服务项目_java

2. 项目编码实现

2.1. 创建department-service (微服务)

直接浏览器访问:https://start.spring.io/,通过勾选填写项目配置信息,并在线搜索需要的第三方依赖,生成springboot项目源码

微服务项目实战pdf 简单的微服务项目_spring_02


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可视化监控界面:

微服务项目实战pdf 简单的微服务项目_微服务_03

现在,可以将user-service中的RMI调用换成:

Department department = restTemplate.getForObject("http://DEPARTMENT-SERVICE/departments/"+user.getDepartmentId(),
        Department.class);

请求的ip和port换成了应用名,这样请求还不能生效,需要在restTemplete的方法上添加一个注解**@LoadBalanced **** **,表示通过restTemplate发起的RMI请求会通过应用名进行请求的负载均衡。

修改这两处之后,先调用department的添加接口:

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_04

再调用user的添加接口:

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_05

最后调用user的查询接口:

微服务项目实战pdf 简单的微服务项目_spring_06

请求响应正常,而且返回了user和department的数据。

2.4. 创建cloud-gateway(网关)

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_07


注意: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。

微服务项目实战pdf 简单的微服务项目_spring_08


微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_09


微服务项目实战pdf 简单的微服务项目_spring boot_10

为了防止某个微服务挂掉,导致网关请求失败,可以在网关服务中添加熔断配置。

  1. 首先需引入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>
  1. 然后添加熔断相关的配置:
hystrix:
  command:
    fallbackcmd:
      execution:
        isolation:
          thread:
            timeoutInMillseconds: 4000

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
  1. 需要在启动类头部添加一个注解 **@EnableHystrix **** **。
  2. 创建一个熔断回调方法的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 可以进入流量监控主页

微服务项目实战pdf 简单的微服务项目_spring boot_11

浏览器访问一个网关的端点:http://localhost:9191/actuator/hystrix.stream

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_12

然后将端点地址输入到 hystrix dashboard中,点击下方按钮进行流监控。之后使用postman通过网关访问微服务接口,再去流监控面板,可以看到请求的流监控信息:

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_13

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包:

微服务项目实战pdf 简单的微服务项目_微服务_14

然后通过 java -jar zipkin.jar 启动,启动成功后会出现一个访问地址:http://127.0.0.1:9411/

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_15

接下来,就是在微服务项目中进行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

微服务项目实战pdf 简单的微服务项目_微服务项目实战pdf_16

前面的序号id不同代表是两次不同的链路请求。而且在zipkin可视化界面中通过填写指定的服务名称,也可以可视化追踪请求调用的链路,全过程。

微服务项目实战pdf 简单的微服务项目_spring boot_17


微服务项目实战pdf 简单的微服务项目_spring boot_18


微服务项目实战pdf 简单的微服务项目_spring boot_19

3. 总结

整个通过springboot开发微服务demo项目的过程其实并不复杂,每个模块主要的操作都是引入starter依赖和添加配置和注解,编码的过程并不多。主要是上层的组织,如何进行合理的配置将多个微服务模块组织起来,管理起来。