spring boot Spring使用FeignClient组件

一、什么是Feign

它是Netflix开发的一个声明式、模板化的HTTP客户端, Feign的目标是帮助Java工程师更快捷、优雅地调用HTTP API//RESTful api。

OpenFeign是Java应用中编写Web服务的客户端的利器,基于第三方HTTP客户端类库,声明式地构建Web服务的客户端应用,以调用Web服务。OpenFeign在Java应用中,负责处理与远程Web服务的请求响应,最大限度降低编码复杂性。

另外,Feign被无缝集成到了SpringCloud微服务框架,使用Feign后,可以非常方便项目SpringCloud微服务技术。

如果项目使用了SpringCloud技术,那就就可以更加方便的声明式使用Feign。如果没有使用SpringCloud,使用Feign也非常之简单。Netflix Feign目前改名为OpenFeign(即:OpenFeign曾用名Netflix Feign)。

Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit、JAXRS-2.0和WebSocket。Feign最初是为了降低统一绑定Denominator到HTTP API的复杂度,不区分是否支持Restful。Feign旨在通过最少的资源和代码来实现和HTTP API的连接。通过可定制的解码器和错误处理,可以编写任意的HTTP API。

Feign的中文名称翻译过来是伪装
那么Feign伪装的是什么呢?答案很明确,Feign伪装的是服务提供者。

二、为什么使用Feign

在Feign之前,我们在进行HTTP调用的时候,更多的是选择使用原生的Apache HTTP Client 库来调用。所以在项目中会存在诸如HtppUtil之类的公共方法。

如果有很多系统,那么系统间的交互就会很多,那么这种HTTP调用就会非常多,那么你就会发现其实很多的调用方做了很多重复的事情。

     Map<String, Object> resMap = HttpUtil.sendPostRequest("url", JsonUtil.toJSONString(resMap));
        String content = (String) resMap.get("content");
        JSONObject resJson = JSONObject.parseObject(content);
        if (JsonUtil.CODE_SUCCESS.equals(resJson.getString("code")))
        {
            // Do Something
            return resultMap;
        }
        else
        {
            return null;
        }

有已下重复工作:

  • 如果是POST请求,那么POST请求的序列化需要调用方来做
  • 如果是GET请求,那么如果在URL中有参数,需要调用方手动拼凑
  • HTTP请求的返回结果需要调用方来序列化
  • 如果是POST请求,那么还需要区分请求是application/x-www-form-
  • urlencoded形式的还是application/json格式的

针对以上的问题,其实都是可以通过优化HttpUtil中的提供的方法来实现的。但是也会导致方法签名很复杂,而且会同时存在各种重载的静态方法,搞得调用者该一脸懵逼。

  1. 如上所述,优化去除一些重复工作,使代码简洁。

三、Spring Cloud Feign设计原理

Spring Cloud Feign设计原理
参考URL: https://www.jianshu.com/p/8c7b92b4396c

四、Feign性能优化

OpenFeign默认使用Java的HttpURLConnection作为HTTP请求客户端。
OpenFeign也可以直接使用已有公共第三方HTTP客户端类库,如Apache HttpComponents, OKHttp,编写Java客户端以访问HTTP服务。

默认情况下Feign底层是使用HttpURLConnection发送请求的,众所周知HttpURLConnection是没有使用连接池的,所以可以针对这点进行优化。例如,将底层的http请求客户端为更换为Apache的HttpClient或者OkHttp等使用了连接池的http客户端,据测试使用了连接池后可以提升15%左右的性能。

Feign 默认底层通过JDK 的 java.net.HttpURLConnection 实现了feign.Client接口类,在每次发送请求的时候,都会创建新的HttpURLConnection 链接,这也就是为什么默认情况下Feign的性能很差的原因。可以通过拓展该接口,使用Apache HttpClient 或者OkHttp3等基于连接池的高性能Http客户端,我们项目内部使用的就是OkHttp3作为Http 客户端。

使用 OkHttp 来发送 Feign 的请求

OkHttpClient 使用 OkHttp 来发送 Feign 的请求,OkHttp 支持 SPDY (SPDY是Google开发的基于TCP的传输层协议,用以最小化网络延迟,提升网络速度,优化用户的网络使用体验),并有更好的控制http请求。

要让 Feign 使用 OkHttp ,你需要将 OkHttp 加入到你的环境变量中区,然后配置 Feign 使用 OkHttpClient,如下:

GitHub github = Feign.builder()
           .client(new OkHttpClient())
           .target(GitHub.class, "https://api.github.com");
Maven依赖:
<!-- https://mvnrepository.com/artifact/com.netflix.feign/feign-gson -->
<dependency>
  <groupId>com.netflix.feign</groupId>
  <artifactId>feign-okhttp</artifactId>
  <version>8.18.0</version>
</dependency>

五、 传统Spring项目使用Feign声明式服务调用思路

在纯spring环境中,网上有2中思路,一个中直接原生使用,一种是模仿spring boot 写自定义注解修改 接口类,让动态代理,让spring实例化管理该接口类,在其他服务中可利用@Autowire注入 该接口即可使用。

这里我暂时采用思路1,感觉原生够用。后一种改造比较多,还不如切换spring boot框架。

思路1:原生feign使用即可

Spring MVC项目使用Feign声明式服务调用
参考URL: https://www.liangzl.com/get-article-detail-137662.html
[推荐]Feign-独立使用-实战
参考URL: https://www.cnblogs.com/crazymakercircle/p/10243749.html

思路2:自定义类似@FeignClient注解

[强烈推荐-可完全参考]在纯Spring环境中使用Feign来进行声明式HTTP调用
参考URL: jianshu.com/p/48ba120641fb

之前,公司使用spring cloud,记得使用@feign http访问其它微服务非常好用。但是传统的spring项目如何使用该组件呢?

这里的传统 Spring项目指的是没有使用 spring boot的 spring项目,例如 ssm。

经过网上信息分析总结,Spring 不能像我们在spring boot中那样很直接的使用
@FeignClient的接口运行时都会生成代理类,由代理类去调用远程服务。

如果想spring boot中那样,使用注解,业务直接注入那种方式使用,在Spring环境下,我们还需要做一些工作。
否则查看官方demo https://github.com/OpenFeign/feign
类似如下,这个 Feign.builder()这种,在使用时实例化你的feign接口实例化一个其对象,来调用。这种方式可以用来也可以用来单元测试。

六、独立使用(原生)Feign具体过程

官网: https://github.com/OpenFeign/feign
Spring MVC项目使用Feign声明式服务调用
参考URL: https://www.liangzl.com/get-article-detail-137662.html

1. maven引入Feign

Spring整合Feign的具体实现
需要的maven引入

        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>10.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>8.18.0</version>
        </dependency>
        <dependency>
            <groupId>com.netflix.feign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>8.18.0</version>
        </dependency>

	<dependency>
	    <groupId>org.reflections</groupId>
	    <artifactId>reflections</artifactId>
	    <version>0.9.10</version>
	</dependency>

2. 封装返回实体

@Data
public class Response<T> {
    private Integer code;
    private String msg;
    private T data;
}

3. 创建接口,声明接口方法

import feign.Param;
import feign.RequestLine;

public interface RemoteService {
    
    @RequestLine("GET /users/list?name={name}")
    String getOwner(@Param(value = "name") String name);
}

通过@RequestLine指定HTTP协议及URL地址

4. 原生feign调用

        RemoteService remoute = Feign.builder().decoder(new JacksonDecoder())
                .requestInterceptor(template -> template.header("appid", appid))
                .target(RemoteService.class, “http://www.baidu.com/”);

        Response<String> response = remote.getOwner(body.toJSONString());

七、feign使用总结

1. 使用 feign时设置header信息

方法一:

@FeignClient(url = "XX_url", value = "XXService")
public interface XXService {
 
    @RequestMapping(value = "/xx", method = RequestMethod.POST)
    @Headers({"Content-Type: application/json","Accept: application/json"})
    String sendDing(String params);
}

方法二:通过实现RequestInterceptor接口,完成对所有的Feign请求,设置Header
demo1

@Component
public class FeginClientConfig {
    @Bean
    public RequestInterceptor headerInterceptor() {
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate requestTemplate) {
                    // 示例
                    requestTemplate.header("Content-Type", "application/json");
            }
        };
    }
 
    @Bean
    public Logger.Level level() {
        return Logger.Level.FULL;
    }
 
}

demo2

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;

@Component
public class FeignInterceptor implements RequestInterceptor{

    public void apply(RequestTemplate requestTemplate){
        requestTemplate.header("hotelId", "111111");
    }
}

这种方式,是针对所有feign请求进行拦截,设置Header

方法3: Feign实例的 .requestInterceptor方法
网上demo

/**
 * @author Liangzhifeng
 * date: 2018/9/13
 */
public interface UserInfoFeignClient {

    /**
     * 根据token获取用户信息
     * @param token
     * @return
     */
    @RequestMapping(value = "/user/info/1.0", method = RequestMethod.POST)
    Object getUserInfoByToken(@RequestParam("token") String token);
}

/**
 * @author Liangzhifeng
 * date: 2018/9/15
 */
@Component
public class AuthorityConfig {

    /**
     * 授权信息Header的key
     */
    public static final String OAUTH_KEY = "token";

    /**
     * 授权信息Header的值的前缀
     */
    public static final String OAUTH_VALUE_PREFIX = "Bearer ";

    @Autowired
    private Client client;

    public UserInfoFeignClient userInfoFeignClient(String token) {
        UserInfoFeignClient authorityServiceLoginInvoker = Feign.builder().client(client)
                .encoder(new GsonEncoder())
                .decoder(new GsonDecoder())
                .contract(new SpringMvcContract())
                .requestInterceptor(template -> template.header(OAUTH_KEY, OAUTH_VALUE_PREFIX + token))
                .target(UserInfoFeignClient.class, GlobalConstant.AUTHORITY_SERVICE_LINK);
        return authorityServiceLoginInvoker;
    }
}

总结:经过测试 方法一 @Headers 有一定要求限制,你给的参数必须是常量,如果你想用变量拼接,IDEA 会提醒报错。
方法二:比较适用全局统一的处理情况。
方法三:灵活,可以自由定制。

根据项目情况,大家选择即可。

八、参考

Spring Cloud Alibaba基础教程:支持的几种服务消费方式(RestTemplate、WebClient、Feign)
参考URL: http://blog.didispace.com/spring-cloud-alibaba-2/
Spring之FactoryBean
参考URL: https://www.jianshu.com/p/f4dca40c55c1
spring中BeanDefinitionRegistryPostProcessor的作用
参考URL: https://blog.csdn.net/u013905744/article/details/93635846
【推荐学习一下-作者有demo测试】fastJson和Jackson对比
参考URL: https://www.jianshu.com/p/a3ebb54445be

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西京刀客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值