Spring声明式调用Feign原理以及扩展应用

Spring声明式调用Feign原理以及扩展应用

微服务的远程调用

在如今微服务大行其道的大环境下,服务间的远程调用就成为微服务不得不解决的问题。在保证性能的情况下,同时需要敏捷开发,易扩展的能力成为重要指标。
在整个SpringCloud生态环境下,Feign的声明式远程调用成为当下微服务的主要技术栈,那么接下来就让我们详细了解Feign的使用与原理

Feign的原理

服务间的调用可以将服务分为服务生产者消费者,但是两者之间并没有严格区分,因为在服务设计拆分时,往往会存在服务间相互调用的情况(根据微服务原则,拆分需尽量严格分为上下游应用,表面此种情况)。所以在区分生产者与消费者是,只是针对一次远程调用。
比如做应用公共平台业务时,我们有一个订单服务order-service,同时有一个第三方服务third-service,当我们在下单时,记录订单数据的同时需要根据所选合作方同步第三方相关下单数据,并根据第三方服务接口返回的信息来更新相关订单状态。所以在下单的这个操作下order-service就是消费者,而第三方服务就是生产者third-service。

  1. 首先快速搭建一个简单生产者,即third-service,利用springBoot,三分钟搞定。
    在这里插入图片描述
    直接访问得到结果报文:
    {“code”:0,“message”:“处理成功”,“data”:{“orderNo”:2131232112,“orderStatus”:“S”,“orderDate”:“20220202”}}
  2. 现在开始搭建一个消费端服务,基本与服务端搭建差不多,只是消费端这边就需要使用到Feign
@FeignClient(name="third-service",url="127.0.0.1:8080")
public interface ThirdServiceFeign {
    @PostMapping("/order")
    ResponseResult<OrderRspDto> order(@RequestBody OrderReqDto reqDTO);
}

然后在直接调用该接口方法,即可完成远程调用。

@RestController
public class NewController {
    @Autowired
    private ThirdServiceFeign thirdServiceFeign;

    @GetMapping("/ordered")
    public String order(){
        OrderReqDto reqDto = new OrderReqDto();
        reqDto.setOrdeNo("123");
        reqDto.setOrderAmout("222");
        ResponseResult<OrderRspDto> order = thirdServiceFeign.order(reqDto);
        System.out.println(order);
        return "success";
    }
}

这就是feign接口的基本使用,本质也是Http请求调用,只是通过Feign来针对我们定义的接口进行了动态代理生产了代理类,实际我们调用到的方法是封装好了的http调用,okhttp等。

Feign原理

其实让我们自己实现一个类似Feign的简单远程方法调用能力其实也不难。
首先我们是直接接口调用,接口不能实例化,那么需要调用其方法的话就需要我们自己定义方法拦截器,使得其实际调用时,先是调用的生成的代理类,在代理类中,我们就可以实现http接口的调用能力,然后对请求参数与返回参数做相应的处理序列化和反序列化。
了解了上面这一点,那么再来理解Feign其实就很简单了。

Feign的扩展

想这种调用第三方服务的情况,有时候在测试环境下往往需要我们自己模拟对方接口的返回参数这个时候,我们就需要自己来重写远程服务返回的接口报文来满足我们正常的测试情况。

  1. 替换Decoder
    Decoder是用来装饰或者解析接口影响数据的,在这个接口里我们可以自己顶一个Decoder然后再对应的FeignClient上注入,这样就能将Feign接口的返回参数进行替换了。
public class MyConfiguration {
    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;
    
    @Bean
    public Decoder feignDecoder() {
        return new MyConfiguration.MyDecoder(new OptionalDecoder(
                new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))));
    }
    class  MyDecoder implements Decoder{
        final Decoder delegate;

        public MyDecoder(Decoder delegate) {
            Objects.requireNonNull(delegate, "Decoder must not be null. ");
            this.delegate = delegate;
        }

        @Override
        public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
            System.out.println(response);
            String str="{\"msgCd\":\"CHN00000\",\"msgInfo\":\"交易成功\",\"output\":{\"noticeResultCode\":\"00000\",\"noticeDescription\":\"交易成功\"}}";
            return   JSON.parseObject(str);
            
        }
}
  1. 替换client
    既然是进行远程http调用,那么使用的client可能就需要根据情况来定了,我们可以自定义一个Client实现Feign的Client接口,然后将该client注入Spring容器中,或者配置在@FeignClient的Configuration指定的配置上也可以完成替换,注意一点,一个是全局,一个是单个FeignClient。
  2. SpringAop的能力
    Spring的Aop本身就是面向切面编程了,针对这个需求,我们只需要在对应的FeignClient进行切面织入,利用Aop的Around通知,在Aop的方法中判断接口以及参数,来决定是否需要调用远程方法或者直接从本地数据库获取数据返回。
@Aspect
@Component
@Order(0)
public class MyAspect {

    @Pointcut("within(com.rech.service.ServiceFeign+)")
    public void myPonit(){};

    @Around("myPonit()")
    public Object mock(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName="";
        Class<?> returnType =null;
        Signature signature = joinPoint.getSignature();
        if(signature instanceof MethodSignature){
            MethodSignature ms= (MethodSignature)signature;
             returnType = ms.getMethod().getReturnType();
            methodName= ms.getMethod().getName();
        }
        //自定义逻辑什么情况下模拟数据,
        //不满足就走原来的方法调用
        if("order".equals(methodName)){
            
            String str="{\"msgCd\":\"CHN00000\",\"msgInfo\":\"交易成功\",\"output\":{\"noticeResultCode\":\"00000\",\"noticeDescription\":\"交易成功\"}}";
            return JSON.parseObject(str, returnType);
        }
        return  joinPoint.proceed();
    }
}

推荐使用第三种方法,简单快速,本就是执行在测试环境的需求,对切面对性能的影响也可以忽略不计。

总结

在没有与ribbon客户端负载均衡结合的情况下,简单描述了下Feign接口响应的基本扩展,当然对对应请求肯定也是有扩展的,如实现RequestInterceptor接口也可以对请求参数做处理。简单来说只要我们熟悉spring框架的原理,以及SpringMVC,对Feign进行扩展实际是很简单的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值