OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

这篇具有很好参考价值的文章主要介绍了OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

OpenFeign 是 Spring Cloud 家族的一个成员, 它最核心的作用是为 HTTP 形式的 Rest API 提供了非常简洁高效的 RPC 调用方式。支持Hystrix 、Ribbon和SpringMVC 注解。

Feign和OpenFeign的区别?

1、Feign:

Feign是Netflix公司(第一代SpringCloud)研发的一个轻量级RESTful的伪HTTP服务客户端。

Feign内置了Ribbon逻辑,通过负载均衡算法可以从注册中心中寻找服务。

2、OpenFeign:

OpenFeign是SpringCloud自己研发的,在Feign的基础上做了增强。

OpenFeign除了原有Ribbon逻辑外,还支持了Hystrix和Spring MVC注解。

一、服务端Provider

注意: 当前是springboot整合OpenFegin 服务端没有配置注册中心, 如分布式 微服务请自行配置

  1. 服务端Controller层

@CrossOrigin
@RestController
@RequestMapping("/customer/information")
public class CrmCustomersController {
   
    @Autowired
    private CrmCustomersService crmCustomersService;

    /**
     * 服务数据
     */
    @RequestMapping(value = "customerlist", method = {RequestMethod.GET,RequestMethod.POST})
    public PmpResult  queryCustomerList (@RequestParam(value="pageNum",defaultValue="1") int pageNum, @RequestParam(value="pageSize",defaultValue="10") int pageSize,String customersno, String customersname,String daogouid){
        try {

            PageInfo<CustomerInformationVo> pageInfo = crmCustomersService.queryCustomerList(pageNum,pageSize,customersno,customersname,daogouid);

            logger.info("查询成功");
            return PmpResult.success(pageInfo);
        }catch (Exception e) {
            logger.error(e.getMessage(), e);
            String errorMessage = "查询异常";
            if (isDev){
                errorMessage = e.getMessage();
            }
            return PmpResult.paramError(errorMessage);
        }

    }

 }

二、消费端Consumer

  1. 消费端~例图

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)
  1. Pom.xml

  1. <!-- openFeing --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>

  1. Api

  1. @FeignClient注解

@FeignClient参数:

name: 指定FeignClient的(服务)名称,在Eureka/Nacos/其他注册中心 中对应的服务名称。

url: 指定远程调用地址。

configuration: 指定配置类,可以自定义FeignClient的配置。

fallback: 指定降级类,在服务调用失败时返回降级类中的内容。

fallbackFactory: 指定降级工厂类,在服务调用失败时返回降级工厂类中的内容。

decode404: 指定404响应是否解码,默认为true。

path: 指定服务请求的基础路径。

contextId: 当一个服务有多个接口时,我们又不想把所有接口都写到一个类中是时候就用到了 contextId为当前类设置一个唯一ID。不然就回报如下错误 。

  1. [扩展]@FeignClient注解 通过属性 contextId 解决名称重复无法启动的问题 错误如下:

Description:
The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

导致 名称重复问题 复现:

/*
*Consumer1
*/
@FeignClient(name = "xj-user")
public interface UserRemoteConsumer1 {
    @GetMapping("/user1/get")
    public User getUser(@RequestParam("id") int id);
}

/*
*Consumer2
*/
@FeignClient(name = "xj-user")
public interface UserRemoteConsumer2 {
    @GetMapping("/user2/get")
    public User getUser(@RequestParam("id") int id);
}

这种情况下启动就会报错了,因Bean的名称冲突。

原因:由于name重复,而又不允许BeanDefinition重复,所以导致在进行注册时报错。

解决方案一:

增加下面的配置,作用允许出现beanName一样的BeanDefinition。

 spring.main.allow-bean-definition-overriding=true

解释:

spring.main.allow-bean-definition-overriding=true应用程序的一个配置属性,它允许在应用程序上下文中覆盖bean定义。如果设置为true,则可以在应用程序上下文中定义多个具有相同名称的bean,后定义的bean将覆盖先前定义的bean。但是,这可能会导致不可预测的行为和错误,因此应该谨慎使用。

解决方案二(推荐):

每个FeignClient手动指定不同的contextId,这样就不会冲突了。

@FeignClient添加contextId属性

/*
*Consumer1
*/
@FeignClient(name = "xj-user" contextId = UserRemoteConsumer1)
public interface UserRemoteConsumer1 {
    @GetMapping("/user1/get")
    public User getUser(@RequestParam("id") int id);
}

/*
*Consumer2
*/
@FeignClient(name = "xj-user" contextId = UserRemoteConsumer2)
public interface UserRemoteConsumer2 {
    @GetMapping("/user2/get")
    public User getUser(@RequestParam("id") int id);
}

总结:想创建多个具有相同名称或url的外部客户端,以便它们指向同一台服务器,但每个服务器都有不同的自定义配置,那么可以使用@FeignClient的contextId属性,以避免这些配置bean的名称冲突。

  1. @RequestMapping(value = "/customer/information/customerlist", method = RequestMethod.GET)是要调的接口名

openFegin调用如下:

import com.alibaba.fastjson.JSONObject;
import com.lt.crm.service.fallback.TransactionFallBackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;


@Repository
@FeignClient(name = "service-client", url = "http://10.1.8.22:9001", contextId = "ConsumerService", fallbackFactory = TransactionFallBackFactory.class)
public interface ConsumerService {

    @RequestMapping(value = "/customer/information/customerlist", method = RequestMethod.GET)
    JSONObject queryCustomerList(@RequestParam(value = "pageNum") final int pageNum, @RequestParam(value = "pageSize") final int pageSize);


}
  1. Hystrix 服务降级处理

说明:

Hystrix配合OpenFeign进行降级,可以对应接口中定义的远程调用单独进行降级操作。

直白的说: 远程调用失败,我们添加一个替代方案,我们知道OpenFegin是以接口的形式来声明远程调用,只要是远程调用失效超时,就执行替代方案。创建一个实现类,对原有的接口方法进行替代方案实现。

  1. 使用@Component注解 交给String管理 注入IOC容器

  1. 对Api接口进行进行实现、重写调用的Api 远程接口 代码如下:

  1. @Component 注解是 Spring 框架中的注解,用于将一个类标记为 Spring 容器中的一个组件,让 Spring 自动扫描并管理这个组件的生命周期和依赖注入。

  1. 实现implements FallbackFactory原因:用于在使用 Feign 进行服务调用时,定义一个 FallbackFactory<> 来处理服务调用失败的情况的<ConsumerService>。[ import feign.hystrix.FallbackFactory; ]

  1. 以下码为例:服务降级内容我只是返回一段话,根据自己业务定性, 可判断参数是否为空等 返回具体错误信息。

import com.alibaba.fastjson.JSONObject;
import com.lt.crm.service.api.ConsumerService;
import feign.hystrix.FallbackFactory;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


@Component
public class TransactionFallBackFactory implements FallbackFactory<ConsumerService>  {

    private Logger logger = LoggerFactory.getLogger(getClass());
    @Override
    public ConsumerService create(Throwable throwable) {

        return new ConsumerService() {
            @Override
            public JSONObject queryCustomerList(int pageNum, int pageSize) {
                logger.error("TransactionMsgWaitingConfirm Error : {}" , ExceptionUtils.getFullStackTrace(throwable));
                String  fallbackMessage = "被Hystrix熔断,已作降级处理!";
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("fallbackMessage",fallbackMessage);
                return jsonObject;
            }
        };
    }



}
  1. 消费端Controller层

@Autowired

private ConsumerService consumerService;

在消费端Controller注入Api调用 ConsumerService


import com.alibaba.fastjson.JSONObject;
import com.lt.crm.common.PmpResult;
import com.lt.crm.service.api.ConsumerService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
@RequestMapping("/customer/information")
@Api(tags = "跨服务调用数据")
public class ConsumerController {
    /**
     * 日志
     */
    private final static Logger logger = (Logger) LoggerFactory.getLogger(ConsumerController.class);

    @Autowired
    private ConsumerService consumerService;

    @ResponseBody
    @ApiOperation(value = "公共开放的端口")
    @RequestMapping(value = "customerlist", method = RequestMethod.GET)
    public PmpResult queryCustomerList(@RequestParam(value = "pageNum", defaultValue = "1") int pageNum, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize) {

        try {
             // String类型 转换成json 接收
            JSONObject GetJsonObject = consumerService.queryCustomerList(pageNum, pageSize);   
            if (null != GetJsonObject) {
                logger.info("调用数据成功");
                return PmpResult.success("数据服务", GetJsonObject);
            }

        } catch (Exception e) {
            e.printStackTrace();
            return PmpResult.serviceError("服务异常");
        }
        return null;
    }


}
  1. application.properties配置超时时间

说明: OpenFegin 默认1ms 未响应会抛出异常 , 往往在调用远程接口的时候 、业务逻辑复杂、 没等业务处理完毕就抛出异常 、我们可以针对这类问题 可以配置延长时长。

  1. 配置 Feign 超时问题

  1. 配置hystrix 超时时间

######################################openFegin######################################
# 默认开启
feign.httpclient.enabled=true
# 默认关闭
feign.okhttp.enabled=false
# 默认关闭
feign.hystrix.enabled=true
# 默认关闭
feign.sentinel.enabled=false

# default context 连接超时时间 5000ms = 5秒
feign.client.config.default.connectTimeout = 5000
# default context 读超时时间
feign.client.config.default.readTimeout = 5000

######################################Hystrix Config######################################
#开启hystrix超时管理
hystrix.command.default.execution.timeout.enabled=true
#hystrix超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=60000

#开启ribbon超时管理
ribbon.http.client.enabled=false
#请求超时时间
ribbon.ReadTimeout=20000
#连接超时时间
ribbon.ConnectTimeout=20000
ribbon.MaxAutoRetries=0
ribbon.MaxAutoRetriesNextServer=1
ribbon.OkToRetryOnAllOperations=false
  1. 启动类:

启动类上使用注解 @EnableFeignClients 开启 openFeign 功能。

/**
 * 微服务应用服务启动类
 * 1、(@EnableDiscoveryClient)注解为链接微服务注册中心用,如实际环境中使用注册中心,请取消注释部分,
 *     与配置文件中相关注册中心配置信息结合使用。
 * @author xj
 *
 */
@EnableDiscoveryClient
@EnableFeignClients(clients= {ConsumerService.class} )//加入@EnableFeignClients注解 开启Feign远程服务调用,使Feign的bean可以被注入
@ServletComponentScan
@SpringBootApplication(scanBasePackages = {"com.lt.crm"})
@EnableCaching
public class OpenFeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(OpenFeignApplication.class, args);}
}
  1. 测试 熔断降级

启动消费端Consumer,并没有启动服务端Provider ,过了超时时间,此时执行了服务降级,这样做避免了在微服务中服务雪崩的灾难。

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

后台也捕获了error :

连接被拒绝:连接执行GET: Connection refused: connect executing GET http://10.1.8.22:9001/customer/information/customerlist?pageNum=1&pageSize=10

2023-03-17 16:56:52.399 ERROR 20928 --- [ervice-client-1] c.l.c.s.f.TransactionFallBackFactory     : TransactionMsgWaitingConfirm Error : feign.RetryableException: Connection refused: connect executing GET http://10.1.8.22:9001/customer/information/customerlist?pageNum=1&pageSize=10
    at feign.FeignException.errorExecuting(FeignException.java:84)
    at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:113)
    at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:78)
    at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:106)
    at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
    at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
    at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
    at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
    at rx.Observable.unsafeSubscribe(Observable.java:10327)
    at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
    at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
    at rx.Observable.unsafeSubscribe(Observable.java:10327)
    at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
    at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
    at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
    at rx.Observable.unsafeSubscribe(Observable.java:10327)
    at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100)
    at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56)
    at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47)
    at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69)
    at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

现在启动服务端再次调用,已经通过消费者9004端口 拿到了服务端9001提供的数据。

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

三、openFeign传参方式

简介:

在接口当中传参的方式有很多,但是在 openFeig中的 传参有一定规则的,详细如下。
  1. 传递JSON数据

服务端Provider接口中JSON传参方法如下:

@RestController
@RequestMapping("/openfeign/provider")
public class OpenFeignProviderController {
    @PostMapping("/order1")
    public Order createOrder1(@RequestBody Order order){
        return order;
    }
}

注意:在Spring Boot 中通过@RequestBody标识入参。

消费端Consumer在openFeign接口传参如下:

@FeignClient(value = "openFeign-provider")//服务名
public interface OpenFeignService {
    /**
     * 参数默认是@RequestBody标注的,这里的@RequestBody可以不填
     * 方法名称任意
     */
    @RequestMapping("/openfeign/provider/order1", method = RequestMethod.GET)
    Order createOrder1(@RequestBody Order Order);
}

注意: 我用的是@RequestMapping注解 这是一个组合注解,里面包含@RequestBody 可以不填。

  1. POJO表单传参

服务端Provider接口传参方法如下:

@RestController
@RequestMapping("/openfeign/provider")
public class OpenFeignProviderController {
    @PostMapping("/order2")
    public Order createOrder2(Order order){
        return order;
    }
}

注意: 参数使用POJO对象接收。

消费端Consumer在openFeign接口传参如下:

@FeignClient(value = "openFeign-provider")
public interface OpenFeignService {
    /**
     * 参数默认是@RequestBody标注的,如果通过POJO表单传参的,使用@SpringQueryMap标注
     */
    @RequestMapping("/customer/information/order2", method = RequestMethod.GET)
    Order createOrder2(@SpringQueryMap Order order);
}

注意: openFeign提供了一个注解@SpringQueryMap解决POJO表单传参,这也是官方文档明确给出了解决方案。

  1. URL中携带参数

服务端Provider接口传参方法如下:

@RestController
@RequestMapping("/openfeign/provider")
public class OpenFeignProviderController {
 
    @GetMapping("/test/{id}")
    public String test(@PathVariable("id")Integer id){
        return "accept one msg id="+id;
}

注意: 此种方式针对restful方式中的GET请求方式。

消费端Consumer在openFeign接口传参如下:

@FeignClient(value = "openFeign-provider")
public interface OpenFeignService {
 
    @GetMapping("/openfeign/provider/test/{id}")
    String get(@PathVariable("id")Integer id);
}

注意: 使用注解@PathVariable接收url中的占位符。

四、开启日志增强

说明: openFeign 虽提供了日志增强功能,但默认是不显示任何日志的,开发者在调试阶段可以自己配置日志的级别。

级别如下:

  • NONE:默认的,不显示任何日志;

  • BASIC:仅记录请求方法、URL、响应状态码及执行时间;

  • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;

  • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

  1. 配置日志级别

自定义一个配置类,在其中设置日志级别如下:


import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenFeginConfig {
    /**
     * 日志级别定义
     * @return
     */
    @Bean
    Logger.Level OpenFeignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

注意细节:Logger导包是feign包。

  1. YAML配置

logging:
  level:
    com.lt.crm.service.api: debug

注意此处 com.lt.crm.service.api 是 openFeign 接口所在的包名,可以根据自己项目路径配置一个特定的openFeign接口。

  1. 注解@FeignClient中配置 configuration

说明:@FeignClient配置里 configuration = OpenFeginConfig.class

@Repository
@FeignClient(name = "service-client", url = "http://10.1.8.22:9001", contextId = "ConsumerService", fallbackFactory = TransactionFallBackFactory.class, configuration = OpenFeginConfig.class)
public interface ConsumerService {

    @RequestMapping(value = "/customer/information/customerlist", method = RequestMethod.GET)
    JSONObject queryCustomerList(@RequestParam(value = "pageNum") final int pageNum, @RequestParam(value = "pageSize") final int pageSize);


}
  1. 查看打印日志示意图

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

五、Gzip压缩数据(全局压缩~接口与浏览器响应压缩)

  1. 全局压缩相关配置

######################################接口与浏览器响应压缩######################################
#全局压缩(接口与浏览器响应压缩)
server:
  compression:
    #开启Gzip压缩
    enabled: true
    #配置min-response-size 压缩数据大小的最小阈值,默认2048
    min-response-size: 1 #设置1kb以上开始压缩
    #配置mime-types 压缩支持的MIME  TYPE
    mime-types:
    - image/png
    - image/jpeg
    - image/jpg
    - text/html
    - text/xml
    - application/xml
    - application/json

min-response-sizemime-types 含默认值,可以不手动指定,如有其他需要可按需指定

  1. 测试压缩

未开启压缩前 请求数据大小为526KB

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

开启压缩 请求数据大小为82KB

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

点击查看请求头信息

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)
  1. Gzip压缩算法

Gzip是一种数据格式,采用deflate算法压缩数据;当GZIP算法压缩到一个纯文本数据时,效果会非常显著。

数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快,除了节省流量,改善用户的浏览体验外。

Gzip压缩传输原理图如下:

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

1、客户端向服务器请求头中带有:Accept-Encoding:gzip,deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。

2、服务端在收到请求之后,如果发现请求头中含有 Accept-Encoding 字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带 Content-Encoding:gzip 消息头,表示响应报文是根据该格式压缩过的。

3、客户端接收到响应之后,先判断是否有 Content-Encoding 消息头,如果有,按该格式解压报文。否则按正常报文处理。

六、OpenFeign开启Gzip压缩数据(局部压缩~微服务之间利用Feign请求及响应压缩)

  1. 全局压缩相关配置

######################################openFegin Gzip######################################
#局部压缩(微服务之间利用feign请求及响应压缩)
feign:
  compression:
    request:
      enabled: true    # 开启压缩
      min-request-size: 1  # 开启压缩的阈值,单位字节,默认2048,即是2k,此处为演示效果设置成1字节
      mime-types: text/xml,application/xml,application/json
    response:
      enabled: true #响应压缩

注意:openFeign支持的Gzip仅仅是在openFeign接口的请求和响应,即openFeign消费者调用服务提供者的接口。

  1. 开启Gzip压缩后日志信息

查看Gzip压缩开启后的打印日志

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

日志中包含 content-encoding: gzip 表示压缩成功

  1. 开启Gzip压缩后日志信息

查看Gzip压缩关闭后的打印日志

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

日志中未出现 content-encoding: gzip 表示未被压缩 并且可以清楚看到时间为 1231ms

七、如何OpenFegin 替换默认的httpclient ?

前言:

1、OpenFeign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。
2、默认的http客户端是 javax.net.ssl.HttpsURLConnection,详细信息查看feign-core:feign.Client,该http客户端不支持添加拦截器和连接池。所以我们需要添加第三方http客户端。

在生产环境中,通常也不会使用默认的httpclient,两种选择如下:

  • 使用ApacheHttpClient

  • 使用OkHttp

声明: 我使用的是 OkHttp 如果使用 ApacheHttpClient 思路是基本一致 。

  1. pom.xml依赖如下

        <!-- 使用 Apache HttpClient 替换 Feign原生httpclient-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- 使用 Okhttp 替换 Feign原生httpclient-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>10.1.0</version>
        </dependency>
  1. application.properties 启用 OkHttp 配置

# 默认开启
feign.httpclient.enabled=false
# 默认关闭
feign.okhttp.enabled=true

关闭默认的httpclient 开启okhttp

  1. 配置OkHttp响应拦截器

前言:

微服务之间使用OpenFeign,由于业务得需求有时候我们需要在请求或者响应的时候做一些额外的操作。比如请求的时候添加请求头,响应时候判断token是否过期等等。这时候拦截器就派上用场了!
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;

@Slf4j
@Configuration
public class FeignOkHttpClientConfig {


    @Bean
    public OkHttpClient.Builder okHttpClientBuilder() {
        return new OkHttpClient.Builder().addInterceptor(new FeignOkHttpClientResponseInterceptor());
    }


    /**
     * okHttp响应拦截器
     */
    public static class FeignOkHttpClientResponseInterceptor implements Interceptor {


        @Override
        public Response intercept(Chain chain) throws IOException {

            Request originalRequest = chain.request();
            Response response = chain.proceed(originalRequest);

            MediaType mediaType = response.body().contentType();
            String content = response.body().string();           
            //解析content,做你想做的事情!!!
            System.out.print("拦截器"+content+"往哪跑?");

            //生成新的response返回,网络请求的response如果取出之后,直接返回将会抛出异常
            return response.newBuilder()
                    .body(ResponseBody.create(mediaType, content))
                    .build();
        }
    }



}
  1. Feign使用okhttpclient时报错:java.lang.IllegalStateException: original request is required

问题复现:

    2023-03-27 16:43:23.754 ERROR 21120 --- [ervice-client-1] c.l.c.s.f.TransactionFallBackFactory     : TransactionMsgWaitingConfirm Error : java.lang.IllegalStateException: original request is required
    at feign.Util.checkState(Util.java:127)
    at feign.Response.<init>(Response.java:48)
    at feign.Response.<init>(Response.java:38)
    at feign.Response$Builder.build(Response.java:133)
    at feign.okhttp.OkHttpClient.toFeignResponse(OkHttpClient.java:99)
    at feign.okhttp.OkHttpClient.execute(OkHttpClient.java:161)

具体描述:

OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)

可以看到状态200 请求成功 ,并且我设置的超时是5秒,而我查看后台日志请求只用了130ms说明没有超时,为什么一直走熔断? 为什么会一直报: java.lang.IllegalStateException: original request is required

解决方案:

最初我用的okhttp 版本是9.7.0 导致,feign-core 的版本和 feign-okhttp版本不一致问题引起,或者可以理解版本为不兼容导致,将 feign-okhttp版本换成 10.1.0 成方可解决。文章来源地址https://www.toymoban.com/news/detail-400837.html

到了这里,关于OpenFegin+hystrix实现远程HTTP服务调用、服务降级(深度解析~保姆级)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Hystrix入门使用 服务熔断 服务降级 服务雪崩

    hystrix停止更新,理念优秀。 分布式系统面临的问题: 对于复杂的分布式体系,有数十个依赖,依赖不可避免的错误。 服务会出现雪崩, 高可用受到破坏 。 Hystrix就是用于解决分布式系统延迟和容错的开源库。 保证在一个依赖出现问题,不会导致整体的服务失败,避免级联故

    2024年02月07日
    浏览(49)
  • SpringCloud(四)Hystrix服务降级、熔断、监控页面

    官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.5.RELEASE/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients 我们知道,微服务之间是可以进行相互调用的,那么如果出现了下面的情况会导致什么问题? 由于位于最底端的服务提供者E发生故障,那么此时会直接导

    2024年02月17日
    浏览(56)
  • SpringCloud-Hystrix服务熔断与降级工作原理&源码

    在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这

    2024年02月14日
    浏览(53)
  • Spring Cloud 容错机试 Hystrix 服务降级 RestTemplate:

    雪崩效应:   如果短信服务炸了后面的所有服务就会起连锁反应造成全部服务挂掉 , 这就是雪崩效应 , 那么其实短信服务又不是我们主要业务 , 这个时候我们可以采用服务降级 , 服务降级就是暂时的把短信服务停掉能用就返回不能用就返回个错误 , 但是它也不会影响

    2024年02月07日
    浏览(43)
  • 云原生微服务 Spring Cloud Hystrix 降级、熔断实战应用

    第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Spring Cloud Netflix 之 Hystrix 多个微服务之间调用的时候,假如微服

    2024年02月08日
    浏览(42)
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级

    直击微服务作用     遇到了什么问题?         将单体架构拆分成微服务架构后,如果保证多个服务(项目)正常运行?     哪个技术可以解决这个问题?         微服务技术         服务治理: 服务管理,维护服务与服务之间的关系     这个技术如何使用?         netflix/网飞:

    2024年02月03日
    浏览(67)
  • SpringCloud-Hystrix服务熔断与降级工作原理&源码 | 京东物流技术团队

    在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这

    2024年02月14日
    浏览(39)
  • 【从0到1设计一个网关】基于Hystrix实现熔断降级

    上文我们已经成功实现了请求重试与请求限流,接下来我们开始实现熔断与服务降级。 熔断与服务降级,在SpringCloud中设计到的就是我们的hystrix,这里我们也将会考虑配合hystrix来实现熔断与服务降级。 如果不了解hystix的可以先进行一下了解。 由于这里我是用的是基于hystr

    2024年02月05日
    浏览(50)
  • 【OpenFeign】OpenFeign结合Hystrix和Sentinel实现熔断降级

    OpenFeign可以与Hystrix和Sentinel结合使用,实现降级和熔断。 使用OpenFeign需要引入OpenFeign的依赖: spring-cloud-starter-openfeign 引入的依赖如下: 默认已经自动引入了hystrix的依赖,不再需要单独再引入hystrix了。 降级方法的类需要实现FeignClient的接口,同时这个类需要注入到Spring容器

    2024年02月11日
    浏览(40)
  • springCloudNetFlex hystrix 服务降级报错:FactoryBean threw exception on object creation;

    在做服务降级的时候,老是报错 先看一下具体错误: 我是在api模块做的服务降级 springCloudApi IServiceProvider testFallBackService 以上是服务降级的全部代码,然后我搜上面的报错,大部分都是在说我FallbackFactory类,没有加@Component这个注解,但我加了还是报这个错 我仔细看了这个错

    2024年02月14日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包