Nacos整合OpenFeign实现RPC调用

2,096 阅读8分钟

往期回顾

Nacos的安装与配置

Spring Cloud集成Nacos作为注册中心

LoadBalacer集成Nacos实现负载均衡

常见的负载均衡策略分析

Spring Cloud集成Dubbo实现RPC调用

SpringCloud集成Nacos作为配置中心

前面我们已经介绍了Nacos 的安装与配置,Spring Cloud 集成Nacos 作为服务的注册中心和配置中心,集成Nacos 实现服务的负载均衡和一些常见的负载均衡策略以及使用Dubbo进行RPC调用

接下来,将介绍如何使用OpenFeign 进行RPC调用

关于RPC的一些相关概念前面的文章已经介绍过,这里不再赘述。如果还有疑问的同学可以参见

Spring Cloud集成Dubbo实现RPC调用

OpenFeign介绍

Spring Cloud OpenFeign 是声明式的服务调用工具,它整合了RibbonHystrix ,拥有负载均衡和服务容错功能

Feign 是声明式的服务调用工具,我们只需创建一个接口并用注解的方式来配置它,就可以实现对某个服务接口的调用,简化了直接使用RestTemplate 来调用服务接口的开发量。Feign 具备可插拔的注解支持,同时支持Feign 注解、JAX-RS注解及SpringMvc注解。当使用Feign时,Spring Cloud集成了RibbonEureka 以提供负载均衡的服务调用及基于Hystrix 的服务容错保护功能。

大家经常听到有人说OpenFeign,有人说Feign,给人一种好像是两个东西的错觉。其实是因为Feign本身也是Netflix的开源项目,后面独立出来单独做了开源项目,改名为OpenFeign。这种情况其实很常见,比如鸿蒙-HarmonyOS就有Open HarmonyOS。

快速开始

引入依赖

我们先在原有的项目工程上创建一个新的消费者模块

引入对应依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <!-- 将ribbon排除 -->
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>  
		        <!--添加loadbalancer依赖
            由于 Netflix Ribbon 进入停更维护阶段,因此 SpringCloud 2020.0.1 版本之后 删除了eureka中的ribbon,
            替代ribbon的是spring cloud自带的LoadBalancer,默认使用的是轮询的方式
            新版本的 Nacos discovery 都已经移除了 Ribbon ,此时我们需要引入 loadbalancer 代替,才能调用服务提供者提供的服务
        -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

添加相关配置

Nacos配置

详见SpringCloud集成Nacos作为配置中心

新建bootstrap.yml

spring:
  application:
    name: user-openfeign-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.199.128:8848 #Nacos地址
      config:
        server-addr: 192.168.199.128:8848 #Nacos地址
        file-extension: yaml #这里我们获取的yaml格式的配置

添加application.yml

server:
  port: 7002
spring:
  profiles:
    active: dev

配置OpenFeign 日志级别

日志的级别分为四种:

  • NONE :不记录任何日志信息,这是默认值。
  • BASIC :仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS :在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL :记录所有请求和响应的明细,包括头信息、请求体、元数据

日志的级别如果没有特殊要求,一般使用 NONE、BASIC 两个就好了,默认是NONE

注意:因为feign调试日志是debug级别输出,springboot默认的日志级别是info,所以feign的debug日志级别就不会输出,所以需要对Feign接口单独配置日志输出级别

# 因为feign调试日志是debug级别输出,springboot默认的日志级别是info,所以feign的debug日志级别就不会输出
# logging.level=debug这样配置是对所有的日志级别进行配置
# 该场景只需要对feign接口进行debug配置,所以是这样配置logging.level.com.example.order.feign=debug
logging:
  level:
    cuit.epoch.pymjl.service.UserFeignClient: debug
  1. Java代码的方式

可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level 的对象

先创建一个配置类

package cuit.epoch.pymjl.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;


/**
 * 在启动类的注解@EnableFeignClients上指定
 * 局部生效就在@FeignClient中指定,不能加@Configuration注解
 *
 * @author Pymjl
 * @version 1.0
 * @date 2022/9/1 13:17
 **/
public class OpenFeignConfig {
    @Bean
    public Logger.Level feignLogLevel() {
        // 日志级别为BASIC
        return Logger.Level.FULL;
    }
}

如果要全局生效,将其放到 启动类的@EnableFeignClients 这个注解中

@SpringBootApplication
@EnableFeignClients(defaultConfiguration = OpenFeignConfig.class)
public class OpenFeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(OpenFeignApplication.class, args);
    }
}

如果是局部生效,则把它放到对应的@FeignClient 这个注解中

@FeignClient(value = "user-service",configuration = OpenFeignConfig.class)
  1. 配置文件的方式
# 针对某个微服务的配置  userservice:微服务名称  FULL:日志级别
feign.client.config.userservice.logger-level=FULL
# 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
#feign.client.config.default.logger-level=FULL

配置LoadBalancer

详见LoadBalacer集成Nacos实现负载均衡

  1. 配置负载均衡策略
/**
 * 注意不要加@Configuration
 *
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/26 13:05
 **/
public class MyLoadBalancerConfig {
    @Resource
    NacosDiscoveryProperties nacosDiscoveryProperties;

    @Bean
    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        // Nacos权重轮询
        return new NacosLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class), name, nacosDiscoveryProperties);
    }
}

  1. 配置RestTemplate
/**
 * 在这里配置我们自定义的LoadBalancer策略 如果想自己扩展算法 需要实现ReactorServiceInstanceLoadBalancer接口
 * @LoadBalancerClients(defaultConfiguration = {name = "CLOUD-PAYMENT-SERVICE", configuration = MyLoadBalancerConfig.class})
 * 注意这里的name属性 需要和Nacos页面中的服务提供者名字一致
 *
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/26 12:20
 **/
@Configuration
@LoadBalancerClient(name = "user-service", configuration = MyLoadBalancerConfig.class)
public class RestTemplateConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

超时配置

我们在通过 Feign 去调用接口,难免会遇到超时的问题,我们可以在 yml 文件设置超时属性,防止系统抛出超时异常

feign:
    client:
        config:
		   # 全局配置(default 默认就是适用于全部微服务)
            default:
                connectTimeout: 100000
                readTimeout: 100000
            # 单独配置
            user-service:
                connectTimeout: 300000
                readTimeout: 300000

OpenFeign的其他配置

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

一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可

常用的配置如下:

feign:
  hystrix:
    enabled: true #在Feign中开启Hystrix
  compression:
    request:
      enabled: false #是否对请求进行GZIP压缩
      mime-types: text/xml,application/xml,application/json #指定压缩的请求数据类型
      min-request-size: 2048 #超过该大小的请求会被压缩
    response:
      enabled: false #是否对响应进行GZIP压缩
logging:
  level: #修改日志级别
    com.macro.cloud.service.UserService: debug

OpenFeign的优化

默认情况下,Feign 使用的是 UrlConnetcion 去请求,这种原生的请求方式一旦遇到高并发的情况下,响应会变得很慢,所以我们可以考虑加入连接池技术来优化性能, 比如常用的Ok HttpApache Http Client

在HTTP 通信的过程中,建立连接是一个很复杂的过程,涉及到多个数据包的交换,很耗时间,而且HTTP连接需要3次握手和4次挥手开销都很大。

这时可以采用HTTP连接池,节约大量的3次握手4次挥手时间,提升吞吐量。

默认的HttpURLConnection是JDK自带的,并不支持连接池,如果要实现连接池的机制,还需要自己来管理连接对象。

HttpClient 相比传统JDK自带的HttpURLConnection,它封装了访问HTTP的请求头,参数,内容体,响应等等。它不仅使客户端发送HTTP请求变得容易,而且也方便了开发人员测试接口(基于HTTP协议的),既提高了开发的效率,又提高了代码的健壮性。另外高并发大量的请求网络的时候,也是用"连接池"提升吞吐量。

OkHttp作为后期之秀,功能和性能上,可能稍优于HttpClient ,但是几乎没多大区别,实际使用时,都是可以的,不过HttpClient集成起来更方便

下面介绍下如何集成 Apache 下的 HttpClient 的连接池

  1. 先引入依赖
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
  1. 编写对应的yml文件
feign:
  httpclient:
    enabled: true
    max-connections: 200
    max-connections-per-route: 50

编写对应的接口

  1. 先在服务提供者正常的编写service 代码,然后将service 注入进controller 完成controller 的编写
/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/8/25 12:48
 **/
@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    UserService userService;

    @GetMapping("/register")
    public CommonResult<String> register() {
        userService.register();
        return ResultUtils.success();
    }

    @GetMapping("/get/{id}")
    public CommonResult<User> get(@PathVariable("id") Long id) {
        return ResultUtils.success(userService.get(id));
    }

}

  1. 在消费者端编写对应的FeignClient ,和我们平常的service接口没太大的区别,只是在上面加上对应的SpringMVC注解
/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/9/1 0:03
 **/
@FeignClient(value = "user-service")
public interface UserFeignClient {
    /**
     * 注册
     *
     * @return {@code CommonResult<String>}
     */
    @GetMapping("/user/register")
    CommonResult<String> register();

    /**
     * 得到
     *
     * @param id id
     * @return {@code CommonResult<User>}
     */
    @GetMapping("/user/get/{id}")
    CommonResult<User> get(@PathVariable("id") Long id);
}

注意value 的值要和nacos 中的服务提供者的名字相同

然后编写对应的消费者controller

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/9/1 0:09
 **/
@RestController
@RequestMapping("/consumer")
public class TestController {
    @Resource
    UserFeignClient userFeignClient;

    @GetMapping("/register")
    public CommonResult<String> register() {
        return userFeignClient.register();
    }

    @GetMapping("/get/{id}")
    public CommonResult<User> get(@PathVariable Long id) {
        return userFeignClient.get(id);
    }
}

测试

  1. 先启动服务提供者user-service,再启动消费者

image-20220901140039863

  1. 访问对应接口

image-20220901140307734

  1. 查看消费者后台日志输出

image-20220901140333458

从上面可以看见,因为我们对OpenFegin 配置了日志的输出级别,控制台在访问对应的请求时打印了对应的HTTP接口的详情

  1. 查看提供者的日志输出

image-20220901140446778

至此,整合NacosOpenFegin 就完成了,至于OpenFegin 整合Hystrix 进行服务的降级与熔断这里不做赘述,后面考虑整合Sentinel

Spring Cloud 之前使用的断路器是 Netfilx 开源的 Hystrix 。被很多开发人员作为默认的断路器来使用。2018 年 11 月,当 Netflix 宣布将这个项目置于维护模式时(不再开发新特性,只进行例行维护),Spring Cloud 官方也不得不跟进了 Netfix ,在 SpringOne 2019中,Spring 宣布将从 Spring Cloud 3.1 版本中删除 Hystrix 仪表板。要不了多长时间 Spring Cloud Netfix 将结束生命周期。

项目源码:gitee github