SpringCloudAlibaba-OpenFeign使用

版本说明
SpringCloudAlibaba 2021.1
OpenFeign 3.0.8
spring-cloud-starter-loadbalancer 3.0.1
SpringLoadBalancer官网
OpenFeign官网

前言

本文章将会介绍基于以上版本 SpringCloudAlibaba集成OpenFeign的使用,包括OpenFein配置、负载均衡策略更换、自定义负载均衡等。
OpenFeign介绍(翻译自官网):声明式 REST 客户端:Feign 通过使用 JAX-RS(Java Api eXtensions of RESTful web
Servivces)或 SpringMVC 注解的修饰方式,生成接口的动态实现。


一、基础使用

1、引入依赖

之前老版本 Spring Cloud 所集成的 OpenFeign 默认采用了 Ribbon 负载均衡器。但由于 Netflix 已不再维护
Ribbon,所以从 Spring Cloud 2021.x 开始集成的 OpenFeign 中已彻底丢弃 Ribbon,而是采用
Spring Cloud 自行研发的 Spring Cloud Loadbalancer 作为负载均衡器,所以还需引入Loadbalancer依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>${open-feign.version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>${loadbalancer.version}</version>
</dependency>

2、在consumer服务创建调用Provider接口,使用@FeignClient注解

@FeignClient(name = "provider2221", path = "/provider/label")
public interface LabelFeignService {
    @PostMapping("/add")
    String add(@RequestParam("name") String name);

    @GetMapping("/test")
    String getName();
}

3、provider的controller代码

@RestController
@RequestMapping("/provider/label")
public class LabelController {
    @Resource
    private ILabelService labelService;
    
	@Value("${server.port}")
    private String port;

    @PostMapping("/add")
    public boolean addLabel(String name){
        Label label = new Label();
        label.setLabelName(name);
        label.setCreateBy("0");
        return labelService.save(label);
    }
    
    @GetMapping("/test")
    public String test(){
        return port;
    }
}

4、consumer的controller使用feign接口示例代码

@RestController
@RequestMapping("/consumer/label")
public class LabelController {
    @Resource
    private LabelFeignService labelFeignService;

    @PostMapping("/add")
    public String add(String name){
        // 直接像接口一样调用就可以了
        return labelFeignService.add(name);
    }

    @GetMapping("/checkLoadBalance")
    public String checkLoadBalance(){
        return labelFeignService.getName();
    }
}

二、@FeignClient注解属性

1、name(或value): 定义 Feign 客户端的名称。通常是一个唯一的标识符,用于区分不同的 Feign 客户端。这是一个必需属性。这里的值要跟被调用的服务名称一样,可以查看nacos的服务名或者配置文件的spring.application.name

  • 以上示例的@FeignClient(name = “provider2221”, path = “/provider/label”)
    provider2221为被调用者provider的服务名
    nacos服务列表

2、path:指定 Feign 客户端的基础路径,用于与每个请求的相对路径组合形成完整的请求 URL。该值与被调用者的Controller类上的@RequestMapping请求路径一致

  • 以上示例的@FeignClient(name = “provider2221”, path = “/provider/label”)
    /provider/label为被调用者的LabelController请求路径
    在这里插入图片描述
    如果path不配置的话,LabelFeignService 接口的路径需改成以下这样
    @FeignClient(name = "provider2221")
    public interface LabelFeignService {
        @PostMapping("/provider/label/add")
        String add(@RequestParam("name") String name);
    
        @GetMapping("/provider/label/test")
        String getName();
    }
    

3、url: 指定目标服务的 URL 地址,用于直接连接到目标服务而不通过服务注册中心。当设置了 url 属性时,name 属性通常会被忽略。一般用于测试。
4、configuration: 指定一个自定义的 Feign 配置类,用于配置 Feign 客户端的行为。配置类必须实现 FeignConfigurer 接口。一般都是在配置文件里面配置。
5、decode404:当设置为 false 时,Feign 客户端将不会将 HTTP 404 响应解码为返回类型的空对象(默认情况下会解码为空对象)。
6、primary:当有多个 Feign 客户端的实例时,将其中一个标记为主要的。通常用于避免 No qualifying bean of type 错误。
7、fallback:指定一个容错处理的实现类,当 Feign 客户端的请求失败时,会执行容错降级处理的逻辑。
8、fallbackFactory::与 fallback 类似,但是它指定了一个工厂类,并且可以捕获异常信息用于创建容错降级处理的实例,推荐使用。

  • 创建容错降级类
import com.quan.service.LabelFeignService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;

/**
 * label远程调用容错处理类
 *
 * @author quan
 * @date 2023/9/8 16:50
 */
@Slf4j
@Component
public class LabelFeignFallBackFactory implements FallbackFactory<LabelFeignService> {
    @Override
    public LabelFeignService create(Throwable cause) {
        log.error("远程调用provider服务的label请求异常", cause);
        return new LabelFeignService() {
            @Override
            public String add(String name) {
                // 对具体的接口做处理
                return "添加失败";
            }

            @Override
            public String getName() {
                // 对具体的接口做处理
                return "查询失败";
            }
        };
    }
}
  • 在之前的@FeignClient注解补充fallbackFactory = LabelFeignFallBackFactory.class即可
@FeignClient(name = "provider2221", path = "/provider/label", fallbackFactory = LabelFeignFallBackFactory.class)
  • 配置文件开启熔断
feign:
  circuitbreaker:
    enabled: true

大坑:我这里用的是3.0.8的openFeign,已经自身不带hystrix依赖,需要再单独引入依赖,要不然降级不生效

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.10.RELEASE</version>
</dependency>

三、Feign常用配置

1、超时配置

官网图

feign:
  client:
    config:
      default:
        # 请求处理超时时间(单位ms)
        connect-timeout: 1000
        # 连接建立超时时间(单位ms)
        read-timeout: 1000

2、日志开启

日志配置

feign:
  client:
    config:
      default:
        # 开启日志
        loggerLevel: BASIC

3、请求/响应压缩

请求/压缩配置

feign:
  # 请求/响应压缩开启
  compression:
    # 请求
    request:
      enabled: true
      # 哪些类型会被压缩
      mime-types: text/xml,application/xml,application/json
      # 最小阈值长度 2048字节
      min-request-size: 1024
    # 响应
    response:
      enabled: true

四、负载均衡

Spring Cloud 2021.x以后不再集成ribbon,而是使用了spring cloud自研的LoadBalancer,默认的轮询算法

1、 替换成随机算法

官方文档
直接将官网的代码拷贝下来,切记不需要使用@Configuration注入,创建以上配置类以后,在LabelFeignService的类上使用@LoadBalancerClients即可,也可在Application启动上使用,代表全局
在这里插入图片描述

2、自定义均衡算法

这里是参考RoundRobinLoadBalancer的源码写的一个简单示例

  • 创建实现类实现ReactorServiceInstanceLoadBalancer
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;

import java.util.List;
/**
 * 自定义负载均衡策略
 * 参考 RoundRobinLoadBalancer
 *
 * @author quan
 */
@Slf4j
public class MyLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private final String serviceId;
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public MyLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        // 从服务提供者获取实例列表,根据自定义均衡算法返回ServiceInstance
        return supplier.get(request).next().map((serviceInstances) -> this.processInstanceResponse(supplier, serviceInstances));
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {
        Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);
        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
            ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
        }
        return serviceInstanceResponse;
    }

    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("没有可供服务的服务: " + this.serviceId);
            }
            return new EmptyResponse();
        } else {
            // 随机的策略源码
            // int index = ThreadLocalRandom.current().nextInt(instances.size());
            // 返回对应的实例(这里默认只取第一个)
            ServiceInstance instance = instances.get(0);
            return new DefaultResponse(instance);
        }
    }
}

  • 然后只需在官网上面提供的示例代码中将RandomLoadBalancer换成自己的即可
    在这里插入图片描述

3、实例健康检查

如果不开启的话,集群的某个服务挂了,刚好调用到就会出现报错或者降级的信息,开启以后不会再将请求转发到宕机的服务。
在这里插入图片描述

spring:
  cloud:
    loadbalancer:
      health-check:
        # 开启健康检测
        refetch-instances: true
        # 检测实例间隔(单位s)
        refetch-instances-interval: 10
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值