三、Spring Cloud Alibaba实战


本文是 SpringCloud 实战教程第三篇, Spring Cloud 基于 Hoxton.SR8 版本,Spring Boot 基于2.3.5.RELEASE,使用Spring Cloud Alibaba相关组件。

一、组件选型

模块 组件 版本
服务注册与发现 Spring Cloud Nacos
声明式服务调用 OpenFeign
服务网关 Spring Cloud GateWay
负载均衡 Spring Cloud Loadbalancer
服务保护(限流降级) Sentienl
配置中心 Spring Cloud Nacos
链路跟踪 Spring Cloud Sleuth + Zipkin
分布式事务 Seata

总体架构示意

二、服务注册和发现

1、安装并运行 Nacos

获取 Docker 镜像。当前最新版本1.8.5。

$ docker pull consul:1.8.5

通过列出匹配的Docker镜像来检查映像是否已下载 consul

$ docker images -f 'reference=consul'
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
consul              1.8.5               4f7b214361a7        3 weeks ago         122MB

配置并运行 Consul 服务器:

docker run -d \
  --name ConsulDiscovery \
  -p 8500:8500 \
  -p 8600:8600/udp \
  consul:1.8.5 agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0

由于以分离模式 -d 启动了容器,因此该进程将在后台运行。Consul Docker 镜像设置 /consul/config 为 Consul 的默认配置目录,代理将加载该目录中放置的所有配置文件。

在命令行查看版本号:

$ docker exec ConsulDiscovery consul -v
Consul v1.8.5
Revision 1e03567d3
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)

服务已经启动,在浏览器中访问:http://localhost:8050,显示的界面如下:

Consul 服务首页

2、服务注册到 Nacos

首先创建一个主 Maven 工程,在其 pom 文件引入依赖,Spring Boot版本为 2.3.5.RELEASE,Spring Cloud版本为 Hoxton.SR8。这个pom文件作为父 pom 文件,起到依赖版本控制的作用,其他 module 工程继承该pom。

<?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>cn.lixl</groupId>
    <artifactId>SpringCloudDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>SpringCloudDemo</name>
    <description>Demo project for Spring Cloud</description>

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.3.5.RELEASE</version>
        <relativePath/>
    </parent>

    <modules>
        <module>eureka-server</module>
        <module>eureka-client</module>
        <module>consul-provider</module>
    </modules>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <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>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

创建 model 工程作为服务提供者,即: consul-provider。以 IDEA 工具为例:

创建 consul-provider 模块

编辑新创建工程的 pom.xml 文件,继承父 pom 文件,引入 spring-cloud-starter-netflix-eureka-server 的依赖:

  <parent>
        <groupId>cn.lixl</groupId>
        <artifactId>SpringCloudDemo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <artifactId>consul-provider2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>consul-provider2</name>
    <description>Demo project for Spring Cloud</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

通过在 application 启动类上加注解 @EnableDiscoveryClient 表明自己是一个 Discovery client:

@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class ConsulProviderApplication {

    @Value("${server.port}")
    String port;

    @Value("${spring.application.name}")
    String appName;

    @RequestMapping("/")
    public String home(){
        return "Consul service name: " + appName +", port: " + port;
    }

    public static void main(String[] args) {
        SpringApplication.run(ConsulProviderApplication.class, args);
    }
}

在配置文件中注明服务注册中心地址,application.yml 配置文件如下:

server:
  port: 8763
spring:
  application:
    name: consul-provider
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: consul-provider #注册到 Consul 的服务名称,客户端根据这个名称来进行服务调用。

为了模拟注册负载均衡,需要启动 2 个 consul-producer 模块。在 IDEA 工具中,复制 consul-producer 启动配置,覆盖运行端口为: 8764。如图:

复制启动配置并覆盖端口

依次启动两个服务提供者项目,在浏览器上访问 http://localhost:8500,可以看到 consul-provider 服务已经注册到 Consul 上了。

Consul 服务首页

点进去后可以看到有 2 个服务提供实例。这样服务提供者就准备好了。

Consul 服务实例列表页

三、基于 OpenFeign 调用服务

1、OpenFeign 介绍

Spring Cloud OpenFeign 是一种声明式、模板化的服务调用组件。简化了 RestTemplate 代码,实现了 Ribbon 负载均衡,使代码变得更加简洁。

OpenFeign 的使用主要分为以下几个步骤:

  • 服务消费者添加 Feign 依赖;
  • 创建业务层接口,添加 @FeignClient 注解声明需要调用的服务;
  • 业务层抽象方法使用 SpringMVC 注解配置服务地址及参数;
  • 启动类添加 @EnableFeignClients 注解激活 Feign 组件。

2、声明式服务调用

与创建服务提供者方法一致,创建服务消费者 model 工程,即: consul-consumer。编辑 pom.xml 文件,引入 spring-cloud-starter-openfeign 依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

application.yml 配置文件如下:

server:
  port: 8765
spring:
  application:
    name: consul-consumer
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: consul-consumer
        register: true       # 是否将自己注册到注册中心,默认为 true

定义业务层接口,并通过注解 @FeignClient(value = "服务名") 绑定到服务提供方,在抽象方法上使用 SpringMVC 注解配置服务地址及参数:

@FeignClient(value = "consul-provider")  
public interface ConsumerService {

    @RequestMapping(value = "/",method = RequestMethod.GET)
    String callConsulProvider();
}

在 application 启动类上加注解 @EnableFeignClients ,启用 Feign 进行远程调用:

@SpringBootApplication
@EnableDiscoveryClient
@RestController
@EnableFeignClients
public class ConsulConsumerApplication {

    @Autowired
    ConsumerService consumerService;

    @Value("${server.port}")
    String port;

    @Value("${spring.application.name}")
    String appName;

    @RequestMapping("/")
    public String home(){
        return "Consul service name: " + appName +", port: " + port;
    }

    @RequestMapping("/call")
    public String cllConsulProvider(){
        return "Call consul provider response: "+ consumerService.cllConsulProvider();
    }

    public static void main(String[] args) {
        SpringApplication.run(ConsulConsumerApplication.class, args);
    }

}

启动程序,然后多次访问: http://localhost:8765/call,可以看到消费者在交替请求 2 个服务提供者:

Call consul provider response: Consul service name: consul-provider, port: 8763
Call consul provider response: Consul service name: consul-provider, port: 8764

到此,Feign 消费者验证成功。

3、负载均衡配置

由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 客户端的方式来定义各个服务客户端调用的参数。

ribbon:
  ConnectTimeout: 600   # 连接超时时间
  ReadTimeout: 5000     # 调用超时时间

我们需要让 Hystrix 的超时时间大于 Ribbon 的超时时间,否则 Hystrix 命令超时后,该命令直接熔断,重试机制就没有任何意义了。

4、服务断路保护

Feign 中的服务降级使用起来非常方便,只需要为 Feign 客户端定义的接口添加一个服务降级处理的实现类即可。下面我们为 ConsumerService 接口添加一个服务降级实现类,并对接口中的每个方法定义服务降级处理逻辑。

@Component
public class ConsumerServiceFallback implements ConsumerService{

    @Override
    public String callConsulProvider() {
        return "调用失败,服务被降级。";
    }
}

修改 ConsumerService 接口,设置服务降级处理类为 ConsumerServiceFallback.class

@FeignClient(value = "consul-provider", fallback = ConsumerServiceFallback.class)  // 声明需要调用的服务
public interface ConsumerService {

修改 application.yml,开启 Hystrix 功能 :

feign:
  hystrix:
    enabled: true  # 开启 Hystrix 服务降级功能

关闭 2 个 consul-provider 服务,重启 consul-consumer 服务,访问 http://localhost:8765/call,可以看到页面显示服务降级信息。

服务降级示例

5、调整日志级别

Spring Cloud Feign 在构建被 @FeignClient 注解修饰的服务客户端时,会为每一个客户端都创建一个 Logger.Level 实例,我们可以利用该日志对象的 DEBUG 模式来帮助分析 Feign 的请求细节。日志级别:

  • NONE:默认的,不显示任何日志;
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间;
  • HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
  • FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。

通过 Java 配置类将 Feign 日志设定为 FULL

@Configuration
public class FeignConfig {

    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

在 application.yml 中配置日志级别为:debug :

logging:
  level:
    cn.lixl.consulconsumer: debug

访问 http://localhost:8765/call ,可以看到控制台显示出详细的日志:

2020-11-21 16:36:10.240 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] ---> GET http://consul-provider/ HTTP/1.1
2020-11-21 16:36:10.240 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] ---> END HTTP (0-byte body)
2020-11-21 16:36:10.252 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] <--- HTTP/1.1 200 (12ms)
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] connection: keep-alive
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] content-length: 48
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] content-type: text/plain;charset=UTF-8
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] date: Sat, 21 Nov 2020 08:36:10 GMT
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] keep-alive: timeout=4
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] proxy-connection: keep-alive
2020-11-21 16:36:10.253 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] 
2020-11-21 16:36:10.255 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] Consul service name: consul-provider, port: 8764
2020-11-21 16:36:10.255 DEBUG 52581 --- [nio-8765-exec-2] cn.lixl.consulconsumer.ConsumerService   : [ConsumerService#callConsulProvider] <--- END HTTP (48-byte body)

四、使用 Nacos作为配置中心

Consul 支持 Key/Value 键值对的存储,可以用来做配置中心。Spring Cloud 提供 Spring Cloud Consul Config 依赖和 Consul 集成。

参照 consul-provider 创建配置中心模块:consul-config-client,在工程的 pom 文件中添加相关依赖:

  <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-config</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

新建配置文件 bootstrap.yml,对 Consul 进行配置:

spring:
  cloud:
    consul:
      config:
        enabled: true   # 是否启用配置中心功能
        format: yaml # 配置值格式
        prefix: SpringCloudDemo # 配置数据所在目录
        data-key: consul-config-data  # 配置key的名字

application.yml 配置文件如下:

server:
  port: 8766
spring:
  application:
    name: consul-config-client
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: consul-config-client
        register: false

创建 ConfigClientController,从 Consul 配置中心中获取配置信息:

@RestController
@RefreshScope
public class ConfigClientController {

    @Value("${config.project.name}")
    private String projectName;

    @GetMapping("/project-name")
    public String getProjectName() {
        return projectName;
    }
}

在 Consul 中添加如下配置:

在 Consul 中添加配置

启动 consul-config-client,调用接口 http://localhost:8766 查看配置信息,页面会显示配置值:SpringCloudDemo。只要修改下 Consul 中的配置信息,再次调用此接口,就会发现配置已经刷新。

参考


文章作者: 李小龙
版权声明: 本博客文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议,转载请注明来源 悟尘记 - 李小龙的博客网站 !
评论
 上一篇
PMBOK知识体系 PMBOK知识体系
一、基础理论在PMP的理论体系中,项目管理包含五大过程组:启动→规划→执行→监控→收尾。各用一句话概括项目管理知识体系五大过程组: 启动过程组:作用是设...
2021-08-22 李小龙
下一篇 
SSL/TLS协议运行机制 SSL/TLS协议运行机制
SSL/TLS是一种密码通信框架,他是世界上使用最广泛的密码通信方法。SSL/TLS综合运用了密码学中的对称密码,消息认证码,公钥密码,数字签名,伪随机数生成器等,可以说是密码学中的集大成者。
2021-06-20
  目录