微服务8:OpenFeign服务调用和负载均衡(推荐方案)

356 阅读5分钟

OpenFeign 全称 Spring Cloud OpenFeign,它是 Spring 官方推出的一种声明式服务调用与负载均衡组件,它的出现就是为了替代进入停更维护状态的 Feign(被官方抛弃的废物,不做赘述),其是服务调用和负载均衡组件的推荐方案。

OpenFeign 常用注解:

使用 OpenFegin 进行远程服务调用时,常用注解如下表:

注解说明
@FeignClient该注解用于通知 OpenFeign 组件对 @RequestMapping 注解下的接口进行解析,并通过动态代理的方式产生实现类,实现负载均衡和服务调用。
@EnableFeignClients该注解用于开启 OpenFeign 功能,当 Spring Cloud 应用启动时,OpenFeign 会扫描标有 @FeignClient 注解的接口,生成代理并注册到 Spring 容器中。
@RequestMappingSpring MVC 注解,在 Spring MVC 中使用该注解映射请求,通过它来指定控制器(Controller)可以处理哪些 URL 请求,相当于 Servlet 中 web.xml 的配置。
@GetMappingSpring MVC 注解,用来映射 GET 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.GET) 。
@PostMappingSpring MVC 注解,用来映射 POST 请求,它是一个组合注解,相当于 @RequestMapping(method = RequestMethod.POST) 。

Spring Cloud Finchley 及以上版本一般使用 OpenFeign 作为其服务调用组件。由于 OpenFeign 是在 2019 年 Feign 停更进入维护后推出的,因此大多数 2019 年及以后的新项目使用的都是 OpenFeign,而 2018 年以前的项目一般使用 Feign。

Feign VS OpenFeign:

相同点

Feign 和 OpenFegin 具有以下相同点:

  • Feign 和 OpenFeign 都是 Spring Cloud 下的远程调用和负载均衡组件。
  • Feign 和 OpenFeign 作用一样,都可以实现服务的远程调用和负载均衡。
  • Feign 和 OpenFeign 都对 Ribbon 进行了集成,都利用 Ribbon 维护了可用服务清单,并通过 Ribbon 实现了客户端的负载均衡。
  • Feign 和 OpenFeign 都是在服务消费者(客户端)定义服务绑定接口并通过注解的方式进行配置,以实现远程服务的调用。

不同点

Feign 和 OpenFeign 具有以下不同:

  • Feign 和 OpenFeign 的依赖项不同,Feign 的依赖为 spring-cloud-starter-feign,而 OpenFeign 的依赖为 spring-cloud-starter-openfeign。
  • Feign 和 OpenFeign 支持的注解不同,Feign 支持 Feign 注解和 JAX-RS 注解,但不支持 Spring MVC 注解;OpenFeign 除了支持 Feign 注解和 JAX-RS 注解外,还支持 Spring MVC 注解

远程服务调用实现:

1. 服务提供者编码

  1. 设置服务提供接口及实现类,因为OpenFeign是进行注解+接口开发的,所以调用的源头在接口层,且必不可少
package com.zhigong.paymentcluster.service;

import com.zhigong.common.result.Result;
import org.springframework.stereotype.Component;

/**
* @author TL
* @create 2022-04-14 10:05
* @Description OpenFeign服务提供者支付集群接口
*/
@Component
public interface FeignService {
    
    /**
    * OpenFeign测试接口
    *
    * @return
    */
    Result feignTest();
    
}




package com.zhigong.paymentcluster.service.impl;

import com.zhigong.common.result.Result;
import com.zhigong.paymentcluster.service.FeignService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
* @author TL
* @create 2022-04-14 10:07
* @Description OpenFeign服务提供者支付集群接口实现类
*/
@Service
public class FeignServiceImpl implements FeignService {
    
    @Value("${server.port}")
    private String serverPort;
    
    /**
    * OpenFeign测试接口逻辑实现方法
    *
    * @return
    */
    @Override
    public Result feignTest() {
        return Result.success(serverPort);
    }
}

2. 编写相应的调用Controller

package com.zhigong.paymentcluster.controller;


import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.zhigong.common.result.Result;
import com.zhigong.paymentcluster.service.FeignService;
import com.zhigong.paymentcluster.service.wechat.WeChatPayService;
import com.zhigong.paymentcluster.utils.HttpUtils;
import com.zhigong.paymentcluster.utils.WechatPay2ValidatorForRequest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 田治功 <T-zhigong@outlook.com>
 * @description 微信支付控制器
 * @date 2022-01-16 20:09
 */
@CrossOrigin//设置跨域
@Api(tags = "微信支付控制器")
@RestController
@RequestMapping("/api/wx-pay")//将微信地址前缀设置为访问地址前缀
@Slf4j
public class WeChatPayController {

    @Resource
    private FeignService feignService;

    @ApiOperation("feign消费者提供者测试")
    @GetMapping("feignConsumerProviderTest")
    public Result FeignConsumerProviderTest() {
        return feignService.feignTest();
    }
}

2. 服务消费者编码

  1. 添加pom依赖
<!-- spring-cloud-starter-openfeign:服务调用和负载均衡 -->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
  <version>3.1.1</version>
</dependency>

2. 服务提供者编码了接口,那么相应的服务消费也是通过接口处理的,必不可少,@FeignClient注解只能被使用在接口层,所以进行消费者模块的接口(实现由提供者提供,消费者不进行编码)编码

package com.zhigong.orders.service;

import com.zhigong.common.result.Result;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author TL
 * @create 2022-04-13 17:55
 * @Description 消费者调用提供者接口
 */
@Component
@FeignClient("PAYMENTMODULESERVICE")//设置提供者客户端在Eureka中注册的服务名
public interface FeignPaymentService {

    @ApiOperation("消费者提供者测试")
    @GetMapping("/api/wx-pay/feignConsumerProviderTest")//因为提供者其Controller也是调用其自身的接口,故此处可直接调用提供者的Controller层
    Result feignConsumerProviderTest();

}

3. 消费者本地消费使用接口编码

package com.zhigong.orders.controller;


import com.zhigong.common.result.Result;
import com.zhigong.orders.service.FeignPaymentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @author TL
 * @create 2022-03-08 17:48
 * @Description
 */
@Api(tags = "订单信息Api")
@RestController
@RequestMapping("OrderInfo")
public class OrderInfoController {

    @Resource
    private FeignPaymentService feignPaymentService;

    @ApiOperation("feign消费者测试")
    @GetMapping("feignConsumerTest")
    public Result feignConsumerTest() {
        return feignPaymentService.feignConsumerProviderTest();
    }
}

4. 相应的业务代码编码完成后,在启动服务时要进行对OpenFeign的激活启用,即在Appreciation启动类上添加@EnableFeignClients //激活并开启OpenFeign

package com.zhigong.orders;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author TL
 * @create 2022-03-25 16:48
 * @Description 主启动类
 */
@SpringBootApplication
@MapperScan("com.zhigong.orders.mapper")
@EnableEurekaClient //eureka被注册端注解
//@LoadBalancerClients(defaultConfiguration = {RibbonRestConfig.class})//官方提供的使用LoadBalancer替代Ribbon随机询问方法配置
@EnableFeignClients //激活并开启OpenFeign
public class OrdersModuleApplication {

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

注意:因为在微服务9中使用Ribbon进行服务调用时,使用官方提供的LoadBalancer方案更改了其轮询为随机,如果要进行默认轮询,则需要注释掉@LoadBalancerClients和RibbonRestConfig中的randomLoadBalancer方法,建议干掉,因为两者不相干,OpenFeign自带负载均衡配置项

效果预览:

轮询效果

没有干掉LoadBalancer被其影响的随机效果