OpenFeign框架相关

OpenFeign是什么?

OpenFeign是一个声明式的WebService客户端。使用OpenFeign能让编写Web Service客户端更加简单。使用时只需定义服务接口,然后在上面添加注解。OpenFeign也支持可拔插式的编码和解码器。spring cloud对feign进行了封装,使其支持MVC注解和HttpMessageConverts。和 eureka(服务注册中心)及 ribbon 组合可以实现负载均衡。在Spring Cloud中使用OpenFeign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样简单,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求,非常的方便。

OpenFeign能干啥?

  1. OpenFeign的设计宗旨是简化Java Http客户端的开发。Feign 在 RestTemplate 的基础上做了进一步的封装,由其来帮助我们定义和实现依赖服务接口。在OpenFeign的协助下,我们只需创建一个接口并使用注解的方式进行配置(类似于Dao接口上面的Mapper注解)即可完成对服务提供方的接口绑定,大大简化了调用客户端的开发量。
  2. OpenFeign集成了Ribbon,利用ribbon维护了服务列表,并且通过ribbon实现了客户端的负载均衡。与ribbon不同的是,通过OpenFeign只需要定义服务绑定接口,且以声明式的方法,优雅而简单的实现了服务调用。

客户端使用

添加 maven 依赖

复制<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-openfeign-core</artifactId>
  <version>2.2.10.RELEASE</version>
</dependency>

代码示例

复制import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Feign;
import feign.FeignException;
import feign.RequestLine;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.Data;
import org.springframework.core.ResolvableType;

import java.io.IOException;
import java.lang.reflect.Type;

/**
 * 测试Feign发送HTTP请求
 */
public class TestFeignClient {
    public static void main(String[] args) {
        DujitangClient client = Feign.builder()
                .decoder(new MyDecoder())
                .target(DujitangClient.class, "https://api.nextrt.com");
        TauntResponse response = client.queryDujitang();
        System.out.println(response);
    }

    static class MyDecoder implements Decoder {

        @Override
        public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
            return new ObjectMapper().readValue(response.body().asInputStream(), ResolvableType.forType(type).getRawClass());
        }
    }

    interface DujitangClient {
        @RequestLine("GET /api/dutang")
        TauntResponse queryDujitang();
    }

    @Data
    public static class TauntResponse {

        private String status;
        private String msg;
        private TauntInfo data;
        private Long timestamp;
    }

    @Data
    public static class TauntInfo {

        private Integer id;
        private String content;
    }
}

默认使用 feign.Client.Default 客户端,底层为 java 中的 HttpURLConnection。

整合SpringMVC

复制import com.fasterxml.jackson.databind.ObjectMapper;
import feign.Feign;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.Data;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.core.ResolvableType;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.IOException;
import java.lang.reflect.Type;

/**
 * 测试Feign发送HTTP请求
 */
public class TestSpringFeignClient {
    public static void main(String[] args) {
        DujitangClient client = Feign.builder()
                .decoder(new MyDecoder())
                .contract(new SpringMvcContract())
                .target(DujitangClient.class, "https://api.nextrt.com");
        TauntResponse response = client.queryDujitang();
        System.out.println(response);
    }

    static class MyDecoder implements Decoder {

        @Override
        public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
            return new ObjectMapper().readValue(response.body().asInputStream(), ResolvableType.forType(type).getRawClass());
        }
    }

    interface DujitangClient {
        @RequestMapping("/api/dutang")
        TauntResponse queryDujitang();
    }

    @Data
    public static class TauntResponse {

        private String status;
        private String msg;
        private TauntInfo data;
        private Long timestamp;
    }

    @Data
    public static class TauntInfo {

        private Integer id;
        private String content;
    }
}

SpringMvcContract 可以支持 @RequestMapping,@PathVariable 等注解。

整合SpringBoot及OkHttp

复制<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
  <exclusions>
    <exclusion>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-netflix-ribbon</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.11.0</version>
</dependency>
复制@EnableFeignClients
@SpringBootApplication
public class CnblogsBackUpApplication {

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

}
复制public class FeignDefaultConfiguration {

  @Bean
  public Client feignClient() {
    return new OkHttpClient(OkHttpUtility.getOkHttpClientInstance());
  }

  private static class OkHttpUtility {

    private static final okhttp3.OkHttpClient okHttpClient;
    public static okhttp3.OkHttpClient getOkHttpClientInstance() {
      return okHttpClient;
    }

    static {
      okHttpClient = new okhttp3.OkHttpClient.Builder()
          .connectionPool(new ConnectionPool(50, 5L, TimeUnit.MINUTES))
          .connectTimeout(30L, TimeUnit.SECONDS).readTimeout(30L, TimeUnit.SECONDS)
          .build();
    }
  }
}
复制@FeignClient(url = "${baidu.url}", name = "baidu",
    configuration = FeignDefaultConfiguration.class)
public interface BaiduService {

  @GetMapping(value = "/s")
  String baiduSearch(@RequestParam String wd);
}

将 默认的客户端实现 feign.Client.Default 替换为了 okhttp3.OkHttpClient。

原理分析

  1. @EnableFeignClients导入FeignClientsRegistrar,扫描包含@FeignClient注解的类
  2. 接口底层实现类为FeignClientFactoryBean
  3. 最终的方法执行拦截器为SynchronousMethodHandler

Feign组件分析

整合负载均衡

  1. FeignClientFactoryBean的getTarget()方法获取到类型为LoadBalancerFeignClient的Client
  2. 进入LoadBalancerFeignClient的execute()方法
  3. 进入LoadBalancerFeignClient的lbClient(),根据clientName获取
  4. 进入CachingSpringLoadBalancerFactory的create()方法
  5. SpringClientFactory的getLoadBalancer()方法,获取到ZoneAwareLoadBalancer类型的ILoadBalancer
  6. ZoneAwareLoadBalancer的balancers字段中已经包含了该clientName在注册中心对应的多个实例地址
  7. PollingServerListUpdater每隔30秒都会从注册中心拉取最新的数据并更新到ILoadBalancer中
  8. 通过DiscoveryEnabledNIWSServerList的obtainServersViaDiscovery()方法来从注册中心获取实例列表

参考

Spring Cloud OpenFeign源码分析

posted @   strongmore  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?
点击右上角即可分享
微信分享提示