OpenFeign

2024-01-03 18:43:10

OpenFeign

一、基本使用

1、引入依赖

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

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

对于loadbalancer做一个解释,开始用的是Ribbon,2020年之后开始使用loadbalancer代替它,两者最主要的区别是,Ribbon是一个较为底层的负载均衡器,需要开发人员手动配置均衡策略和服务发现机制,但是loadbalancer提供了更高层次的抽象,将负载均衡策略和服务发现机制实现了封装,更加容易使用。

2、启动类上添加@EnableFeignClients注解,表示开启OpenFeign

3、编写接口,看代码:

@FeignClient(value = "item-service")
public interface ItemClient {

    @GetMapping("item/{id}")
    ResponseEntity<Item> queryById(@PathVariable("id") Integer id);
}

@FeignClient括号里面value对应的值是被请求的服务的名称,接口中的方法就跟在controller里面写的一样,只是不需要实现,需要提供请求方式和路径,例如:@GetMapping("item/{id}"),还需要提供返回值,请求参数,请求参数的注解,都不能少,例如:ResponseEntity<Item> queryById(@PathVariable("id") Integer id),需要将返回值的类从其他服务复制一份过来。

二、原理

1、OpenFeign调用接口没有具体实现,是因为使用了代理,代理类是FeignInvocationHandler实现了InvocationHandler

static class FeignInvocationHandler implements InvocationHandler 

2、FeignInvocationHandler类的主要方法是invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 

?下面是方法的一部分:

     if (!"equals".equals(method.getName())) {
                if ("hashCode".equals(method.getName())) {
                    return this.hashCode();
                } else {
                    return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
                }
            } else {
                try {
                    Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                    return this.equals(otherHandler);
                } catch (IllegalArgumentException var5) {
                    return false;
                }
            }
        }

除了基本的hashCode这样的方法之外,都要走代理invoke方法。代理的过程:

(1)首先根据接口定义的信息创建一个RequestTemplate对象,这个对象从接口获取到请求方法,请求路径,请求参数,服务名称,模板创建完成之后是这样:GET /item/2 HTTP/1.1

 RequestTemplate template = this.buildTemplateFromArgs.create(argv);

(2)根据模板创建一个请求对象Request,此时请求的路径基本成型,只是还有获取到服务的ip和端口,暂时用服务名代替,例如:GET http://item-service/item/2,通过Request对象可以获取到被请求服务的服务列表

Request request = this.targetRequest(template);

(3)通过loadBalancerClient.choose负载均衡,从实例列表中选出一个实例,获取该实例的ip和端口

ServiceInstance instance = this.loadBalancerClient.choose(serviceId, lbRequest);
ServiceInstance是一个实例,大概信息是:

最后拼接成url:

?(4)使用client 类型的delegate发送http请求,但是这里的发送可以优化,使用连接池技术,避免每次创建和销毁链接导致的系统性能开销,推荐使用OKHttp。

优化:

添加依赖

<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>

配置applicaton.yml

feign:
  okhttp:
    enabled: true

避坑:

遇到过两个错误

(1)feign/Request$ProtocolVersion

(2)feign.Request$HttpMethod.isWithBody()Z

原因是因为OKHttp依赖的版本和cloud不匹配导致的,我这里用的是这一组,可以参考下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>10.12</version>
</dependency>

?springboot的版本是:

<version>2.6.13</version>

?三、最佳实践

如果说大量使用到服务之间的通信调用,那么可以将Client单独抽离到一个模块,实现统一维护。

四、OpenFeign日志开启

1、首先是client所在的包的日志级别必须是debug,yml文件的配置如下,包名自己换:

logging:
  level:
    com.cart.cartservice: debug
  pattern:
    dateformat: HH:mm:ss:SSS

2、编写配置类

public class DefaultFeignConfig {

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

Logger.Level.BASIC是日志级别:

  • NONE:不记录,这是默认值

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间。

  • HEADERS:在BASIC的基础上额外的记录了请求和响应的头

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

?3、在启动类的注解上添加扫描的包和配置类

@EnableFeignClients(basePackages = "com.cart.cartservice",defaultConfiguration = DefaultFeignConfig.class)

文章来源:https://blog.csdn.net/qq_42251944/article/details/135368604
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。