微服务入门(RestTemplateEurekaNacosFeignGateway)

Posted 吞吞吐吐大魔王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务入门(RestTemplateEurekaNacosFeignGateway)相关的知识,希望对你有一定的参考价值。

文章目录

1. 框架介绍

1.1 单体架构

基本介绍: 将业务的所有功能集中在一个项目中开发,打成一个包部署。

优点: 架构简单、部署成本低

缺点: 耦合度高

1.2 分布式架构

基本介绍: 根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,称为一个服务。

优点: 降低服务耦合、有利于服务升级拓展

缺点: 架构复杂、难度大

分布式架构的要考虑的问题:

  • 服务拆分粒度如何?
  • 服务集群地址如何维护?
  • 服务之间如何实现远程调用?
  • 服务健康状态如何感知?

1.3 微服务架构

基本介绍: 微服务是一种经过良好架构设计的分布式架构方案。

特征:

  • 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
  • 面向服务:微服务对外暴露业务接口
  • 自治:团队独立、技术独立、数据独立、部署独立
  • 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题。

优点: 拆分粒度更小、服务更独立、耦合度更低

缺点: 架构非常复杂,运维、监控、部署难度提高

2. 认识微服务

2.1 SpringCloud

  • SpringCloud 是目前国内使用最广泛的微服务框架。官网地址:https://spring.io/projects/spring-cloud

  • SpringCloud 集成了各种微服务功能组件,并基于 SpringBoot 实现了这些组件的自动装配,从而提供了良好的开箱即用体验

    • 服务注册发现:Eureka、Nacos、Consul
    • 统一配置管理:SpringCloudConfig、Nacos
    • 服务远程调用:OpenFegin、Dubbo
    • 统一网关路由:SpringCloudGateway、zuul
    • 服务链路监控:Zipkin、Sleuth
    • 流程、降级、保护:Hystix、Sentinel
  • SpringCloud 与 SpringBoot 的版本兼容关系:

    Realesase Train(版本序列)Boot Version
    2020.0.x aka Ilford2.4.x
    Hoxton2.2.x, 2.3.x
    Greenwich2.1.x
    Finchley2.0.x
    Edgware1.5.x
    Dalston1.5.x

2.2 微服务拆分注意事项

  • 不同微服务,不要重复开发相同业务
  • 微服务数据独立,不要访问其它微服务的数据库
  • 微服务可以将自己的业务暴露为接口,供其它微服务调用

2.3 微服务远程调用方式

2.3.1 提供者与消费者

  • 服务提供者:一次业务中,被其他微服务调用的服务。(提供接口给其它微服务)
  • 服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
  • 一个服务既可以是提供者又可以是消费者。

2.3.2 RestTemplate

基本介绍:

当我们需要远程调用一个 HTTP 接口时,我们经常会用到 RestTemplate 这个类,这个类是 Spring 框架提供的一个工具类。它提供了常见的 REST 请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求以及一些通用的请求执行方法 exchange 以及 execute。

常用方法:

方法描述
getForObject通过 GET 请求获得响应结果
getForEntity通过 GET 请求获取 ResponseEntity 对象,包括状态码、响应头和响应数据等
headForHeaders以 HEAD 请求资源返回所有响应头信息
postForLocation用 POST 请求创建资源,并返回响应数据中响应的字段 Location 的数据
postForObject通过 POST 请求创建资源,获得响应结果
put通过 PUT 请求来创建或更新资源
patchForObject通过 PATH 请求来更新资源,并获得响应结果
delete通过 DELETE 请求来删除资源
optionsForAllow通过 ALLOW 请求来获得资源所允许访问的所有 HTTP 方法,可用来查看某个请求支持哪些请求方式
exchange更通过版本的请求处理方法,接受一个 RequestEntity 对象,可以设置路径、请求头、请求信息等,最后返回一个 ResponseEntity 实体
execute最通用的执行 HTTP 请求方法,上面所有方法都是基于 execute 的封装,可以全面控制请求信息,并通过回调接口获得响应数据

简单使用:

  • 首先将 RestTemplate 在配置类中进行注入(启动类也属于配置类,可以直接在启动类中使用 @Bean 进行注入)

  • 通过 RestTemplate 对象的 getForObject(url, Class<T> responseType) 方法,就能够通过 get 方法向其它微服务发送对应的请求去获取数据。

RestTamplate 远程调用出现的问题:

  • 服务消费者该如何获取服务提供者的地址信息?
  • 如果有多个服务提供者,消费者该如何选择?
  • 消费者如何得知服务提供者的健康状态?
  • 代码可读性差,编程体验不统一
  • 参数复杂 URL 难以维护

3. Eureka 注册中心

3.1 基本介绍

在 Eureka 框架中,微服务角色有两类:

  • EurekaServer(服务端,注册中心):用于记录服务信息,进行心跳监控

  • EurekaClient(客户端):包括服务提供者和消费者

    • 服务提供者能够将注册自己的信息到 EurekaServer 中,并且每隔 30s 向 EurekaServer 发送心跳
    • 服务消费者能够根据服务名称从 EurekaServer 中拉取服务列表,并基于服务列表做负载均衡,选中一个微服务后发起远程调用

在进行之后的步骤之前,需要在父工程中引入 spring-cloud-dependencies 依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>Hoxton.SR10</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

3.2 搭建 EurekaServer

  1. 创建项目,引入 spring-cloud-starter-netflix-eureka-server eureka 服务端依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    
  2. 编辑启动类,添加 @EnableEurekaServer 注解

    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaApplication 
        public static void main(String[] args) 
            SpringApplication.run(EurekaApplication.class, args);
        
    
    
  3. 添加 application.yml 文件,编写下面的配置:

    server:
      port: 10086 # 服务端口
    spring:
      application:
        name: eurekaserver # eureka 的服务名称
    eureka:
      client:
        service-url: # eureka 的地址信息
          defaultZone: http://127.0.0.1:10086/eureka
    
  4. 通过配置的 eureka 的地址信息就能够访问到 eureka 注册中心的状况

3.3 服务注册

  1. 在要注册的服务中引入 spring-cloud-starter-netflix-eureka-server eureka 客户端依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  2. 在 application.yml 文件中编写下面的配置:

    server:
      port: 8080 # 服务端口
    spring:
      application:
        name: aservice # 注册到 eureka 中的服务名称
    eureka:
      client:
        service-url: # 配置 eureka 的地址信息
          defaultZone: http://127.0.0.1:10086/eureka
    
  3. 通过配置的 eureka 的地址信息就能够查看注册到 eureka 中的实例

  4. 以上是对某个服务进行一个实例的部署,如果要部署多个实例,则为了避免端口冲突,需要修改端口配置

    • 右击要部署多个的服务,点击 Copy Configuration…

    • 修改配置的名字,并在 VM options 这栏加上 -Dserver.port=新的端口号 却修改配置的端口号

    • 启动项目后,在 eureka 的注册中心就能看到 aservice 服务注册了两个实例

3.4 服务拉取(含负载均衡)

  1. 修改 RestTemplate 的 url 路径,用服务名代替 ip 和端口号

  2. 在 RestTemplate 注册到 Spring 容器的位置加上 @LoadBalanced 负载均衡注解

  3. 启动项目,通过 bservice 服务访问 aservice 服务中的方法

4. Ribbon 负载均衡

4.1 负载均衡流程

通过浏览器直接访问 http://aservice/say/dog 是访问不到的,因为 aservice 不是正确的 ip 和端口号,而加上负载均衡的注解 @LoadBalanced 注解就能够对这个请求进行拦截,在 eureka-server 中找到 aservice 对应的 ip 和端口号,最终返回请求的数据。

加上 @LoadBalanced 就能就行拦截,那么拦截的动作是谁处理的呢?

请求会被一个叫 LoadBalancerInterceptor 的负载均衡拦截器给拦住,它能获取拦截请求中的主机名,即上述请求中的 aservice,并把它交给 RibbonLoadBalancerClient。RibbonLoadBalancerClient 又会把服务名交给 DynamicServerListLoadBalancer。DynamicServerListLoadBalancer 就会到 eureka-server 中获取到对应服务的服务列表并交给 IRule。IRule 会从服务列表中基于规则选取其中一个服务,将 ip 和端口号返回给 RibbonLoadBalancerClient。RibbonLoadBalancerClient 就会将请求修改成正确的地址并发送出去,最终获取到数据。

4.2 负载均衡策略

Ribbon 的负载均衡规则是一个叫做 IRule 的接口来定义的,它的每一个子接口都是一种规则

内置负载均衡规则类规则描述
RoundRobinRule简单轮询服务列表来选择服务器。它是 Ribbon 默认的负载均衡规则。
WeightedResponseTimeRule为每一个服务器赋予一个权重。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,但权重值会影响服务器的选择。
RandomRule随机选择一个可用的服务器。
RetryRule重试机制的选择逻辑。
ZoneAvoidanceRule以区域可用的服务器为基础进行服务器的选择。使用 Zone 对服务器进行分类,这个 Zone 可以理解为一个机房、一个机架。而后再对 Zone 内的的多个服务做轮询。
AvailabilityFilteringRule1. 在默认情况下,这台服务器如果3次连接失败,就会被设置为“短路”状态。短路状态将持续30s,如果再次连接失败,短路的时间就会几何级地增加。2. 并发数过高的服务器,如果配置了 AvailabilityFilteringRule 规则,客户端就会将其忽略。

修改负载均衡规则方式:

  • 直接在配置类或启动类中定义一个新的 IRule(定义后,该服务调用其它微服务都会使用如下规则)

    // 将规则修改成随机选择
    @Bean
    public IRule randomRule()
        return new RandomRule();
    
    
  • 在 application.yml 文件中,添加新的配置(配置后,该服务会对指定要调度的服务进行规则配置)

    aservice: # 要调度的服务名
      ribbon: # 负载均衡规则,修改为要使用的负载均衡的类的名称
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
    

4.3 饥饿加载

Ribbon 默认是采用懒加载,即第一次访问时会去创建 LoadBalanceCilent,请求时间很长。而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients:  # 指定饥饿加载的服务名称
    - aservice # 指定对 aservice 服务开启饥饿加载

5. Nacos 注册中心

5.1 安装 Nacos

Nacos 是阿里巴巴的产品,现在是 SpringCloud 中的一个组件,相比于 Eureka 功能更加丰富。

Nacos 的安装方式如下:

  1. 下载 Nacos 的安装包,并解压好(下载地址为:https://github.com/alibaba/nacos/tags

  2. 启动 Nacos(进入解压好的 bin 目录,通过控制台执行下面的命令)

    # 单机启动 Nacos
    startup.cmd -m standalone
    

  3. Console 后面的地址就是 Nacos 的控制台地址,Ctrl + 鼠标左键就能够直接打开

  4. 登录 Nacos(默认账号和密码都是 nacos),登录成功后就会跳转到控制台页面

5.2 服务注册

  1. 在父工程中添加 spring-cloud-alilbaba 的管理依赖:

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>2.2.5.RELEASE</version>
        <type>pom</type>
        <scope>import</scope>
    </dependency>
    
  2. 在指定的服务中添加 nacos 的客户端依赖:

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  3. 配置 application.yml 文件

    spring:
      application:
      	name: aservice # 服务名
      cloud:
        nacos:
          server-addr: localhost:8848 # nacos 服务地址
    
  4. 运行项目后,指定服务就会注册到 nacos 中。在 nacos 的控制台就可以看到具体的信息。

  5. 通过 RestTemplate 就可以对注册到 nacos 上的服务进行调度。

5.3 Nacos 服务分级存储模型

一个服务可以分成多个集群,每个集群中又可以包括多个实例。

服务跨集群调用问题:

服务调用尽可能选择本地集群的服务,跨集群调用延迟较高。当本地集群不可访问时,再去访问其它集群。

集群的配置方式:

当我们不配置集群时,Nacos 中集群显示为 DEFAULT,即表示没有集群

可以在 application.yml 文件中编辑如下配置,去配置集群

spring:
  cloud:
    nacos:
      discovery:
        cluster-name: CS # 集群名称

在 aservice 服务中设置好了集群名为 CS 后启动 aservice 的 8080 实例。然后再修改 aservice 的集群名为 HZ 并启动 aservice 的 8082 实例,Nacos 中的集群就会显示下面结果:

5.4 根据集群负载均衡

上面已经修改了 aservice 服务的配置,将 aservice 的两个实例分别配置到了两个集群中。不过将 bservice 设置集群名为 CS,当调用 asevice 实例时并不是优先调用的同集群中的 aservice 实例。这是因为服务在选择实例时是通过负载均衡的规则去选择的,而负载均衡的默认规则是轮询调度。因此想要优先调度同集群的实例,就要修改负载均衡的规则。

修改方式如下:

  1. 先设置好调度者的服务的集群名

  2. 在该服务中设置负载均衡的 IRule 为 NacosRule,这个规则优先会寻找与自己同集群的服务(不过是同集群中随机调度)

    aservice: # 要调度的服务名
      ribbon:
        NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
    

5.5 根据权重负载均衡

Nacos 提供了权重配置来控制实例的访问频率,权重越大者访问频率越大。

应用场景:

将性能好的机器设置权重多一点,将性能差的机器设置权重少一点。

修改方式如下:

  1. 在 Nacos 控制台就可以设置实例的权重值,首先点击实例的编辑按钮

  2. 将权重修改为你要的值即可(权重值在 0~1 之间,0 表示不会被访问到)

5.6 环境隔离 namespace

Nacos 中服务存储和数据存储的最外层都是一个名为 namespace 的东西,用来做最外层的隔离。每个 namespace 都有唯一 ID,不同 namespace 下的服务是不可见的。

在 Nacos 控制台我们可以看到,我们注册的实例都默认存放在一个叫 public 的命名空间中

配置新的命名空间的方式:

  1. 在 Nacos 的控制台新建一个命名空间

  2. 创建好了之后,在服务列表就能看到新创建的命名空间了。

  3. 修改服务的 application.yml 文件,添加如下配置:

    spring:
      cloud:
        nacos:
        discovery:
         namespace: a6fedd4e-c8fc-4695-b5d1-60bf72205819 # 命名空间的ID
    
  4. 重启服务

5.7 临时实例和非临时实例

Nacos 支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式。默认采用临时实例。

配置非临时实例方式:

spring:
  cloud:
    nacos:
      discovery:
        ephemeral: false # 设置非临时实例

5.8 Nacos 和 Eureka 的差别

共同点:

  • 都支持服务注册和服务拉取
  • 都支持服务提供者心跳方式做健康检测

区别:

  • Nacos 支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式。
  • 临时心跳不正常会被剔除,非临时实例则不会被剔除。
  • Nacos 支持服务列表变更的消息推送模式,服务列表更新更及时。
  • Nacos 集群默认采用 AP 模式,当集群中存在非临时实例时,采用 CP 模式;Eureka 采用 AP 模式

6. Nacos 配置管理

6.1 Nacos 进行配置管理

  1. 在 Nacos 控制台中添加配置信息

  2. 在弹出的表单中填写配置信息

6.2 微服务获取 Nacos 中的配置

当只读取本地配置文件时,步骤如下:

当需要读取 nacos 控制台中的配置文件时,步骤如下:

  1. 引入 Nacos 的配置管理客户端依赖

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    
  2. 在服务中的 resources 目录中添加一个 bootstrap.yml 文件(这个文件是引导文件,优先级高于 application.yml)

    spring:
      application:
        name: aservice # 服务名称(与 nacos 控制台配置文件名对应)
      profiles:
        active: dev # 开发环境,这里是 dev(与 nacos 控制台配置文件名对应)
      cloud:
        nacos:
          server-addr: localhost:8848 # Nacos 地址
          config:
            file-extension: yaml # 文件后缀名(与 nacos 控制台配置文件名对应)
    

6.3 配置热更新

Nacos 中的配置文件变更后,微服务无需重启就可以感知。不过需要通过下面两种配置实现:

  • 方式一:在 @Value 注入的变量所在类上添加注解 @RefreshScope

    @Value 该注解的作用是将配置文件的属性读出来,有 @Value("$")@Value("#") 两种方式

  • 方式二:使用 @ConfigurationProperties 注解

    上面的例子将会读取配置文件中所有以 pattern 开头的属性,并和 bean 中的字段进行匹配。

6.4 多种配置的优先级

微服务从 nacos 读取配置文件时会读取两个配置:

  • [服务名]-[spring.profile.active].yaml 环境配置
  • [服务名].yaml 默认配置,多环境共享

优先级:

nacos 中的当前环境配置(服务名-环境.yaml)> nacos 中共享配置(服务名.yaml)> 本地配置

7. HTTP 客户端 Feign

7.1 定义和使用 Feign

基本介绍:

Feign 是一个声明式的 HTTP 客户端,能够帮助我们优雅地实现 HTTP 请求的发送。官方地址:https://github.com/OpenFeign/feign

使用 Feign 步骤如下:

  1. 引入依赖:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    
  2. 在消费者的启动类中添加 @EnableFeignClients 注解开启 Feign 的功能:

    @SpringBootApplication
    @EnableFeignClients
    public class BApplication 
        public static void main(String[] args) 
            SpringApplication.run(BApplication.class, args);
        
    
    
  3. 编写 FeignClient 接口:

    @FeignClient("aservice") // 要调度的微服务
    public interface AClient  // 该接口中包含和 aservice 服务中的多有需要调度的方法
    
        @GetMapping("say/dog")
        public String dogSay();
    
    
  4. 使用编写好的 FeignClient 中定义的方法

    @RestController
    @RequestMapping("b")
    public class BController 
    
        @Autowired
        private AClient aClient;
    
        @GetMapping("say")
        public String bSay()
            return aClient.dogSay();
        
    
    

7.2 自定义 Feign 的配置

Feign 运行自定义配置来覆盖默认配置,可以修改的配置如下:

类型作用说明
feign.Logger.Level修改日志级别包含四种不同的级别:NONE(默认)、BASIC、HEADERS、FULL
feign.coder.Decoder响应结果的解析器HTTP 远程调用的结果做解析,例如解析 json 字符串为 java 对象
feign.coder.Encoder请求参数编码将请求参数编码,便于通过 HTTP 请求发送
feign.Contract支持的注解格式默认是 SpringMVC 的注解
feign.Retryer失败重试机制请求失败的重试机制,默认是没有,不过会使用 Ribbon 的重试

配置 Feign 日志有两种方式:

  • 方式一:配置文件方式

    • 全局生效

      feign:
        client:
          config:
            default: # default 表示全局配置,即对调度的所有微服务都作如下配置
              loggerLevel: Full # 日志级别
      
    • 局部生效

      feign:
        client:
          config:
            aservice: # 具体服务名称,针对某个要被调度的微服务进行配置
              loggerLevel: Full # 日志级别
      
  • 方式二:java 代码方式,需要先声明一个 Bean

    public class FeignClientConfiguration
        @Bean
        public Logger.Level feignLogLevel()
            return Logger.Level.BASIC;
        
    
    
    • 而后如果是全局配置,则把它放到 @EnableFeignClients 这个注解中

      @EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class);
      
    • 如果是局部配置,则把它放到 @FeignClient 这个注解中

      @FeignClient(value = "aservice", configuration = FeignClientConfiguration.class);
      

7.3 Feign 的性能优化

Feign 底层的客户端实现:

  • URLConnection:默认实现,不支持连接池
  • Apache HttpClient:支持连接池
  • OKHttp:支持连接池

优化 Feign 的性能主要包括:

  • 使用连接池代替默认的 URLConnection,可以减少连接创建和销毁的性能损耗
  • 日志级别最好使用 basic 或 none,级别低的话性能更高

Feign 添加 HttpClient 的支持方式:

  1. 引入依赖

    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
    </dependency>
    
  2. 配置连接池:

    feign:
      httpclient:
        enabled: true # 开启 feign 对 httpClient 的支持
        max-connections: 200 # 最大连接数
        max-connections-per-route: 50 # 每个路径的最大连接数
    

7.4 Feign 的最佳实现

方式一(继承):给消费者的 FeignClient 和提供者的 controller 定义统一的父接口作为标准

方式二(抽取):

  1. 首先创建一个 module,命名为 feign-api,然后引入 feign 的 starter 依赖

  2. 在 feign-api 中编写提供者的 FeignClient、POJO 和 Feign 的默认配置

  3. 在消费者中引入 feign-api 的依赖

  4. 重启项目

  5. 当定义的 FeignClient 不在 SpringBootApplication 的扫描包范围时,这些 FeignClient 无法使用。有两种方式解决:

    • 方式一:指定 FeignClient 所在包

      @EnableFeignClients(basePackages = "com.mmr.feign.clients")
      
    • 方式二:指定 FeignClient 字节码

      @EnableFeignClients(clients = AClient.class)
      

8. 统一网关 Gateway

8.1 认识网关

网关的

以上是关于微服务入门(RestTemplateEurekaNacosFeignGateway)的主要内容,如果未能解决你的问题,请参考以下文章

架构设计之「 微服务入门 」

SpringCloud微服务开发快速入门

SpringCloud微服务开发快速入门

企业微信应用和行业方案服务商入门考试答案?

使用istio治理微服务入门

SpringCloud 入门理论知识