优雅编码之——传统项目中,使用openfeign替换掉项目中的httpclient(代码片段)

水中加点糖 水中加点糖     2022-12-08     266

关键词:

使用spring cloud时,当遇到服务与其他服务调用的场景,一般都会使用spring cloud openfeign来进行调用。

通过@feign注解让代码变得非常简单而又优雅,即便是跨服务的调用,写起来就像项目内部自己调自己的方法一样,之顺畅~

但当项目是非spring cloud项目,在服务内调用外部的http服务时,可能首先想到的就是httpclient或okhttp

将httpclient封装为一个工具类,哪里用到就拿出来调哪里,将请求url和参数信息传入,再将请求和响应的前后加上编解码的处理代码

如果项目中又遇到需要调很多这样接口的地方,又遇到各种请求头的适配、请求method的适配、参数类型的适配……,工具类的代码可能都能封装出好几十个方法来,代码看起来可能还是会有点别扭丑丑的~

那么有没有什么更好的方法来解决上面这一问题呢,答案是必须有的:feign就是专门干这个事的

正如feign官网所说的那样:Feign makes writing java http clients easier


为了能让大家在传统项目中一起了解下feign这个组件,这里特意将feign快速引入到传统项目中的流程进行简单记录一下。


引入方式

以maven项目为例,添加pom依赖

    <properties>
        <openfeign.version>11.9.1</openfeign.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
        </dependency>
        <!--序列化方式支持gson、Jackson、fastjson、Sax等-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-gson</artifactId>
        </dependency>
         <!--可选项,slf4j日志支持-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-bom</artifactId>
                <version>$openfeign.version</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

其中openfeign.version为选择的feign版本,最新的版本可直接从feign的github中获取

请求配置

普通请求

import com.haiyang.javastu.springtransactionmanager.service.TestApiService;
import feign.Feign;
import feign.Logger;
import feign.Request;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.slf4j.Slf4jLogger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
public class ApiRegisterConf 
    private final String testApiUrl = "http://127.0.0.1:8080";

    @Bean
    public TestApiService testApiService() 
        return Feign.builder()
                .decoder(new GsonDecoder())
                .encoder(new GsonEncoder())
                .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
                //需要引入feign-slf4j依赖
                .logger(new Slf4jLogger())
                .logLevel(Logger.Level.FULL)
                .target(TestApiService.class, testApiUrl);
    

其中TestApiService为:

import com.haiyang.javastu.springtransactionmanager.model.JsonResult;
import com.haiyang.javastu.springtransactionmanager.model.TestInfo;
import com.haiyang.javastu.springtransactionmanager.model.TestInfoResult;
import feign.Headers;
import feign.Param;
import feign.RequestLine;


public interface TestApiService 

    @Headers("Content-Type: application/json")
    @RequestLine("POST /hello/test.do")
    JsonResult<TestInfoResult> helloTest(TestInfo testInfo);

写个controller测试一下:

import com.haiyang.javastu.springtransactionmanager.model.JsonResult;
import com.haiyang.javastu.springtransactionmanager.model.TestInfo;
import com.haiyang.javastu.springtransactionmanager.model.TestInfoResult;
import com.haiyang.javastu.springtransactionmanager.service.TestApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping(value = "test/feign/")
@RestController
public class TestFeignController 
    private final TestApiService testApiService;

    @Autowired
    public TestFeignController(TestApiService testApiService) 
        this.testApiService = testApiService;
    

    @PostMapping(value = "helloTest")
    public JsonResult<TestInfoResult> helloTest(@RequestBody TestInfo testInfo) 
        return testApiService.helloTest(testInfo);
    


发个请求测试一下

POST http://localhost:8080/test/feign/helloTest
Content-Type: application/json

"id": 123

输出结果:


  "msg": null,
  "code": 200,
  "data": 
    "id": 123,
    "name": "zhangsan",
    "age": 27,
    "hobbys": [
      "programing",
      "reading"
    ]
  

如果想要输出feign的请求详细日志,记得需要将feign包的日志级别设为DEBUG级别

logging:
  level:
      feign: debug

请求携带自定义header

feign提供了requestInterceptor来实现对请求的拦截处理,如果遇到需要进行token之类的header透传的场景,用它来实现就可以了。

示例:

import com.haiyang.javastu.springtransactionmanager.service.TestApiService;
import feign.*;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.slf4j.Slf4jLogger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Configuration
public class ApiRegisterConf 
    private final String testApiUrl = "http://127.0.0.1:8080";

    @Bean
    public TestApiService testApiService() 
        return Feign.builder()
                .decoder(new GsonDecoder())
                .encoder(new GsonEncoder())
                .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
                .logger(new Slf4jLogger())
                .logLevel(Logger.Level.FULL)
                //自定义请求头
                .requestInterceptor(new MyHeaderRequestInterceptor())
                .target(TestApiService.class, testApiUrl);
    

    public static class MyHeaderRequestInterceptor implements RequestInterceptor 

        @Override
        public void apply(RequestTemplate template) 
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //添加token,设置进请求头
            template.header("token", request.getHeader("token"));
        
    

写个测试请求测试一下

动态请求host

有时需要根据业务需要,可能需要向不同host发起请求。在feign中也提供了动态切换host的方式。

Overriding the Request Line

If there is a need to target a request to a different host then the one supplied when the Feign client was created, or you want to supply a target host for each request, include a java.net.URI parameter and Feign will use that value as the request target.

@RequestLine("POST /repos/owner/repo/issues")
void createIssue(URI host, Issue issue, @Param("owner") String owner, >@Param("repo") String repo);

只需要在请求的参数中包含URI参数就可以进行host的自定义了。

如上面的示例中,改成这样:

public interface TestApiService 

    @Headers("Content-Type: application/json")
    @RequestLine("POST /hello/test.do")
    JsonResult<TestInfoResult> helloTest(URI hostUri, TestInfo testInfo);

调用的地方将uri传入即可:

    @PostMapping(value = "helloTest")
    public JsonResult<TestInfoResult> helloTest(@RequestBody TestInfo testInfo) 
        URI uri = URI.create("http://192.168.1.4:8080");
        return testApiService.helloTest(uri, testInfo);
    

其调用效果也是一样的。

spring cloud openfeign、openfeign、feign的区别

spring cloud openfeign

This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.

OpenFeign

Ultimately, Netflix decided to stop using Feign internally and ceased its development. As a result of this decision, Netflix fully transferred Feign to the open-source community under a new project named OpenFeign.

feign

Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign’s first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.

优雅编码之——传统项目中,使用openfeign替换掉项目中的httpclient(代码片段)

...服务与其他服务调用的场景,一般都会使用springcloudopenfeign来进行调用。通过@feign注解让代码变得非常简单而又优雅,即便是跨服务的调用,写起来就像项目内部自己调自己的方法一样,之顺畅~但当项目是非spr... 查看详情

优雅编码之——传统项目中,使用openfeign替换掉项目中的httpclient(代码片段)

...服务与其他服务调用的场景,一般都会使用springcloudopenfeign来进行调用。通过@feign注解让代码变得非常简单而又优雅,即便是跨服务的调用,写起来就像项目内部自己调自己的方法一样,之顺畅~但当项目是非spr... 查看详情

springcloud之openfeign

SpringCloud之openFeignSpringCloud的子项目之一,SpringCloudOpenFeign以将OpenFeign集成到SpringBoot应用中的方式,为微服务架构下服务之间的调用提供了解决方案。首先,利用了OpenFeign的声明式方式定义Web服务客户端;其次还更进一步,通过... 查看详情

jenkins插件之如何优雅的生成版本号

...常重要的一部分,本章将介绍如何VersionNumberPlug插件生成优雅的版本号。 二、安装系统管理--》插件管理搜索VersionNumber--》直接安装三、构建中使用一、配置找到项目--》配置--》构建环境变量说明:BUILD_VERSION:变量名${JOB_name}... 查看详情

springbootspringboot之openfeign服务调用

OpenFeign介绍  前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,对于服务依赖的调用可能不止一处,往往一个接口会被多处调用。所有Feign在此基础上做了... 查看详情

微服务架构整理-(十一springcloud实战之openfeign)(代码片段)

SpringCloud实战之OpenFeignOpenFeign介绍实现消费者功能创建SpringBoot工程添加依赖添加注解声明服务在Controller中调用服务添加配置文件结果实现负载均衡功能实现熔断功能配置熔断开关实现回调函数获取异常信息总结OpenFeign介绍OpenFeig... 查看详情

springcloud原理:openfeign之feignclient动态代理生成原理

今天我们深入剖析一下SpringCloud中OpenFeign组件的源码,来聊一聊OpenFeign是如何工作的。一、@EnableFeignClinets作用源码剖析我们都知道,要使用feign,必须要使用@EnableFeignClinets来激活,这个注解其实就是整个feign... 查看详情

优雅编程|7个你应该掌握的javascript编码技巧

...分享7个日常开发中可以用到的JavaScript编码技巧,享受其优雅编程的快感。更多的编码技巧可以参阅《18个JavaScript代码的小技巧》和《24个Javascript代码优化技巧》。1.有条件地的扩展对象属性扩展运算符...是现在项目中最喜欢使... 查看详情

全面吃透javastream流操作,让代码更加的优雅

笔者结合在团队中多年的代码检视遇到的情况,结合平时项目编码实践经验,对Stream的核心要点与易混淆用法、典型使用场景等进行了详细的梳理总结,希望可以帮助大家对Stream有个更全面的认知,也可以更加高效的应用到项目... 查看详情

使用 javascript 的动态表单提交 - 如何优雅的代码?

】使用javascript的动态表单提交-如何优雅的代码?【英文标题】:Dynamicformsubmissionusingjavascript-howtoelegantcode?【发布时间】:2009-05-2615:57:18【问题描述】:问题来了:我的页面显示一组项目。每个项目都有一个与之关联的复选框(... 查看详情

springcloud源码解读之如何配置好openfeign的各种超时时间!

...时详解:在SpringCloud微服务架构中,大部分公司都是利用OpenFeign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现ReadTim... 查看详情

如何在nodejs项目中优雅的使用es6

如何在NodeJS项目中优雅的使用ES6NodeJs最近的版本都开始支持ES6(ES2015)的新特性了,设置已经支持了async/await这样的更高级的特性。只是在使用的时候需要在node后面加上参数:--harmony。但是,即使如此node也还是没有支持全部的... 查看详情

openfeign:声明式服务调用

SpringCloudOpenFeign:声明式服务调用一、OpenFeign简介1.什么是OpenFeign​OpenFeign目前是SpringCloud二级子项目。平时说的Feign指的是Netflix下的Feign,现在我们学习的是OpenFeign,是Spring提供的。​OpenFeign是一种声明式、模板化的HTTP客户端(... 查看详情

openfeign:声明式服务调用

SpringCloudOpenFeign:声明式服务调用一、OpenFeign简介1.什么是OpenFeign​OpenFeign目前是SpringCloud二级子项目。平时说的Feign指的是Netflix下的Feign,现在我们学习的是OpenFeign,是Spring提供的。​OpenFeign是一种声明式、模板化的HTTP客户端(... 查看详情

微服务实践之通信(openfeign)详解-springcloud(2021.0.x)-6(代码片段)

...出自:shusheng007首发于:shusheng007.top文章目录概述OpenFeign简介基本使用新建provider与consumer两个服务provider服务consumer服务引入依赖声明OpenFeign接口开启OpenFeign原理配置日志配置更换Http客户端负载均衡spring-cloud-loadbalancer源... 查看详情

springcloud中openfeign止hystrix的几个问题(代码片段)

...个小项目,使用了微服务的架构,当然就少不了openfeign和hystrix。在使用过程中可谓是问题频频,而且遇到问题不好解决。新版本的hystrix不起作用首先是在使用openfeign的过程中发现fallback不起作用。左思右想,查了... 查看详情

springcloud中openfeign止hystrix的几个问题(代码片段)

...个小项目,使用了微服务的架构,当然就少不了openfeign和hystrix。在使用过程中可谓是问题频频,而且遇到问题不好解决。新版本的hystrix不起作用首先是在使用openfeign的过程中发现fallback不起作用。左思右想,查了... 查看详情

springcloud中openfeign止hystrix的几个问题(代码片段)

...个小项目,使用了微服务的架构,当然就少不了openfeign和hystrix。在使用过程中可谓是问题频频,而且遇到问题不好解决。新版本的hystrix不起作用首先是在使用openfeign的过程中发现fallback不起作用。左思右想,查了... 查看详情