1.Feign
1.1Feign简介
Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。与当我们访问别的服务端口的时候 大部分使用httpclient等请求进行调用不同,在eureka注册的服务,我们可以使用Feign 声明接口的形式来进行相关服务的调用,并提供了失败回退(其实是Hystrix组件的使用)。Feign只是一个便利的rest框架,简化调用,最后还是通过ribbon在注册服务器中找到服务实例,然后对请求进行分配。
Feign是简化Java HTTP客户端开发的工具(java-to-httpclient-binder),它的灵感来自于Retrofit、JAXRS-2.0和WebSocket。Feign的初衷是降低统一绑定Denominator到HTTP API的复杂度,不区分是否为restful。
Fegin的关键机制是使用了动态代理
1)、首先,对某个接口定义了@FeginClient注解,Fegin就会针对这个接口创建一个动态代理
2)、接着调用接口的时候,本质就是调用Fegin创建的动态代理
3)、Fegin的动态代理会根据在接口上的@RequestMapping等注解,来动态构造要请求的服务的地址
4)、针对这个地址,发起请求、解析响应Fegin是和Ribbon以及Eureka紧密协作的
1)、首先Ribbon会从Eureka Client里获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端口
2)、然后Ribbon就可以使用默认的Round Robin算法,从中选择一台机器
3)、Fegin就会针对这台机器,构造并发起请求
1.2Ribbon、Feign和OpenFeign的区别
1.2.1Ribbon
Ribbon 是 Netflix开源的基于HTTP和TCP等协议负载均衡组件
Ribbon 可以用来做客户端负载均衡,调用注册中心的服务
Ribbon的使用需要代码里手动调用目标服务,请参考官方示例:https://github.com/Netflix/ribbon
1.2.2Feign
Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端
Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。
从jar包来看,openFeign中不仅内置了ribbon还内置了Hystrix服务熔断,所以Feign、openFeign也是负载均衡的

Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务
Feign支持的注解和用法请参考官方文档:https://github.com/OpenFeign/feign
Feign本身不支持Spring MVC的注解,它有一套自己的注解
1.2.3OpenFeign
OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。
OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
1.2.4feign和openFeign比较
1.2.5ribbon和feign比较
Ribbon和Feign都是用于调用其他服务的,不过方式不同。
1.启动类使用的注解不同
Ribbon用的是@RibbonClient,Feign用的是@EnableFeignClients。
2.服务的指定位置不同
Ribbon是在@RibbonClient注解上声明,Feign则是在定义抽象方法的接口中使用@FeignClient声明。
3.调用方式不同
Ribbon需要自己构建http请求,模拟http请求(也就是封装request对象,包括请求头信息,请求参数等等)然后使用RestTemplate发送给其他服务,步骤相当繁琐。也就是上面提到的需要代码里手动调用目标服务
Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致。
1.3feign快速入门案例
(1)在tensquare_qa模块添加依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring‐cloud‐starter‐openfeign</artifactId></dependency>
(2)修改tensquare_qa模块的启动类,添加注解
@EnableDiscoveryClient
@EnableFeignClients
package com.tensquare.qa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import util.IdWorker;
import util.JwtUtil;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class QaApplication {
public static void main(String[] args) {
SpringApplication.run(QaApplication.class, args);
}
@Bean
public IdWorker idWorker() {
return new IdWorker(1, 1);
}
@Bean
public JwtUtil jwtUtil() {
return new JwtUtil();
}
}
(3)在tensquare_qa模块创建 com.tensquare.qa.client包,包下创建接口
package com.tensquare.qa.client;
import com.tensquare.qa.client.impl.LabelClientImpl;
import entity.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author: Luck-zb
* description:微服务之间调用 -- 使用feign技术,调用base微服务
* <p>
* 测试 -- Hystrix熔断器测试,使用规则:feign中提供了熔断器技术,所以引入了opn feign的jar包后即可使用熔断器
* 使用方法,在@FeignClient注解中,通过fallback属性,指定一个实现类,当服务异常的时候,就会执行fallback中
* 指定的实现,可以自定义一些处理,比如返回提示信息等
* <p>
* Date:2021/2/1 - 9:18
*/
@Component
@FeignClient(value = "tensquare-base", fallback = LabelClientImpl.class)
public interface LabelClient {
/**
* 根据id查询
*
* @param labelId
* @return
*/
@RequestMapping(value = "/label/{labelId}", method = RequestMethod.GET)
Result findById(@PathVariable("labelId") String labelId);
}
@FeignClient注解用于指定从哪个服务中调用功能 ,注意 里面的名称与被调用的服务名保持一致,并且不能包含下划线。
@RequestMapping注解用于对被调用的微服务进行地址映射。注意 @PathVariable注解一定要指定参数名称,否则出错
(5)修改tensquare_qa模块的 ProblemController
@Autowired
LabelClient labelClient;
/**
* 使用feign技术实现微服务之间的调用 -- 测试
* <p>
* 下面两个RequestMapping的路径在于测试,在这里的controller中RequestMapping中的value属性值,
* 是没有特殊要求的,就是这里访问的还是ProblemController中的接口,所以这里路径是可以自己随便写的,
* 但是要注意不能与其它方法冲突,比如路径参数,请求方式也一样,
* <p>
* 真正通过feign调用其它服务,是通过xxxClient中的方法来调用的,对xxxClient方法中的路径才有要求,
* 必须写接口的全路径,参数等也必须保持一致,而且@PathVariable注解中必须指定属性映射参数
* <p>
* 当使用zuul网关后,请求头信息会丢失,为了解决这个问题,使用了一个Zuul网关过滤器来实现,
* 也用该方法来进行测试
*
* @param labelId
* @return
*/
// @RequestMapping(value = "/label/{labelId}",method = RequestMethod.GET)
@RequestMapping(value = "/test/{labelId}", method = RequestMethod.GET)
public Result testFeign(@PathVariable String labelId) {
String authorization = request.getHeader("Authorization");
if (logger.isInfoEnabled()) {
logger.info("receive header :{}", authorization);
}
Result result = labelClient.findById(labelId);
return result;
}
(6)测试:http://localhost:9003/problem/test/1 能看到标签的信息
1.4负载均衡测试
测试:同时开启多个基础微服务,看是否是轮流调用。
修改tensquare_base工程LabelController的findById方法
@RequestMapping(value="/{id}", method = RequestMethod.GET)
public Result findById(@PathVariable String id){
System.out.println("No.1");
return new Result(true,StatusCode.OK,"查询成
功",labelService.findById(id) );
}
启动基础微服务后,修改端口和输出信息,再次启动基础微服务启动问答微服务,浏览器执行http://localhost:9003/problem/test/1 看是否轮流调用。结果,使用feign的服务调用是负载均衡的
