在前面的文章中可以发现当我们通过 RestTemplate 调用其它服务的 API 时, 所需要的参数须在请求的 URL 中进行拼接, 如果参数少的话或许我们还可以忍受, 一旦有多个参数的话, 这时拼接请求字符串就会效率低下, 并且显得好傻.
那么有没有更好的解决方案呢? 答案是确定的有, Netflix 已经为我们提供了一个框架: Feign.
Feign 是一个声明式的 web Service 客户端, 它的目的就是让 Web Service 调用更加简单. Feign 提供了 HTTP 请求的模板, 通过编写简单的接口和插入注解, 就可以定义好 HTTP 请求的参数, 格式, 地址等信息.
而 Feign 则会完全代理 HTTP 请求, 我们只需要像调用方法一样调用它就可以完成服务请求及相关处理. Feign 整合了 Ribbon 和 Hystrix(关于 Hystrix 我们后面再讲), 可以让我们不再需要显式地使用这两个组件.
总起来说, Feign 具有如下特性:
可插拔的注解支持, 包括 Feign 注解和 JAX-RS 注解;
支持可插拔的 HTTP 编码器和解码器;
支持 Hystrix 和它的 Fallback;
支持 Ribbon 的负载均衡;
支持 HTTP 请求和响应的压缩.
这看起来有点像我们 springmvc 模式的 Controller 层的 RequestMapping 映射. 这种模式是我们非常喜欢的. Feign 是用 @FeignClient 来映射服务的.
首先第一步, 在原来的基础上新建一个 Feign 模块, 接着引入相关依赖, 引入 Feign 依赖, 会自动引入 Hystrix 依赖的, 如下:
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-eureka</artifactId>
- <version>1.3.5.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-feign</artifactId>
- <version>1.4.0.RELEASE</version>
- </dependency>
application.yml 配置如下:
- server:
- port: 8083
- spring:
- application:
- name: feign-consumer
- eureka:
- client:
- service-url:
- defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
接着在前面文章中的的的两个 provider1 和 provider2 两个模块的服务新增几个方法, 如下代码所示:
- /**
- * Created by cong on 2018/5/8.
- */
- @RestController
- public class HelloController {
- @RequestMapping("/hello")
- public String hello(){
- System.out.println("访问来 1 了......");
- return "hello1";
- }
- @RequestMapping("/hjcs")
- public List<String> laowangs(String ids){
- List<String> list = new ArrayList<>();
- list.add("laowang1");
- list.add("laowang2");
- list.add("laowang3");
- return list;
- }
- // 新增的方法
- @RequestMapping(value = "/hellol", method= RequestMethod.GET)
- public String hello(@RequestParam String name) {
- return "Hello" + name;
- }
- @RequestMapping(value = "/hello2", method= RequestMethod.GET)
- public User hello(@RequestHeader String name, @RequestHeader Integer age) {
- return new User(name, age);
- }
- @RequestMapping(value = "/hello3", method = RequestMethod.POST)
- public String hello (@RequestBody User user) {
- return "Hello"+ user. getName () + "," + user. getAge ();
- }
- }
接着是上面代码所需用到的 User 类, 代码如下:
- /**
- * Created by cong 2017/12/2.
- */
- public class User {
- private String name;
- private Integer age;
- // 序列化传输的时候必须要有空构造方法, 不然会出错
- public User() {
- }
- public User(String name, Integer age) {
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Integer getAge() {
- return age;
- }
- public void setAge(Integer age) {
- this.age = age;
- }
- }
接下来用 Feign 的 @FeignClient("服务名称") 映射服务调用. 代码如下:
- package hjc;
- import org.springframework.cloud.netflix.feign.FeignClient;
- import org.springframework.web.bind.annotation.*;
- /**
- * Created by cong on 2018/5/17.
- */
- //configuration = xxx.class 这个类配置 Hystrix 的一些精确属性
- //value="你用到的服务名称"
- @FeignClient(value = "hello-service",fallback = FeignFallBack.class)
- public interface FeignService {
- // 服务中方法的映射路径
- @RequestMapping("/hello")
- String hello();
- @RequestMapping(value = "/hellol", method= RequestMethod.GET)
- String hello(@RequestParam("name") String name) ;
- @RequestMapping(value = "/hello2", method= RequestMethod.GET)
- User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
- @RequestMapping(value = "/hello3", method= RequestMethod.POST)
- String hello(@RequestBody User user);
- }
接着在 Controller 层注入 FeiService 这个接口, 进行远程服务调用, 代码如下:
- /**
- * Created by cong on 2018/5/17.
- */
- @RestController
- public class ConsumerController {
- @Autowired
- FeignService feignService;
- @RequestMapping("/consumer")
- public String helloConsumer(){
- return feignService.hello();
- }
- @RequestMapping("/consumer2")
- public String helloConsumer2(){
- String r1 = feignService.hello("hjc");
- String r2 = feignService.hello("hjc", 23).toString();
- String r3 = feignService.hello(new User("hjc", 23));
- return r1 + "-----" + r2 + "----" + r3;
- }
- }
接着在 Feign 模块的启动类哪里打上 Eureka 客户端的注解 @EnableDiscoveryClient Feign 客户端的注解 @EnableFeignClients, 代码如下:
- @SpringBootApplication
- @EnableDiscoveryClient
- @EnableFeignClients
- public class FeignApplication {
- public static void main(String[] args) {
- SpringApplication.run(FeignApplication.class, args);
- }
- }
接着启动启动类, 浏览器上输入 localhost:8083/consumer 运行结果如下:
可以看到负载均衡轮询出现 hello1,hello2.
接着继续在浏览器上输入 localhost:8083/consumer2, 运行结果如下:
接下来我们进行 Feign 声明式调用服务下的, 服务降级的使用, 那么我们就必须新建一个 FeignFallBack 类来继承 FeiService, 代码如下:
- package hjc;
- import org.springframework.stereotype.Component;
- /**
- * Created by cong on 2018/5/17.
- */
- @Component
- public class FeignFallBack implements FeignService{
- // 实现的方法是服务调用的降级方法
- @Override
- public String hello() {
- return "error";
- }
- @Override
- public String hello(String name) {
- return "error";
- }
- @Override
- public User hello(String name, Integer age) {
- return new User();
- }
- @Override
- public String hello(User user) {
- return "error";
- }
- }
接着我们再把那两个服务提供模块 provider1,provider2 模块进行停止, 运行结果如下所示:
可以看到我们这几个调用, 都进行了服务降级了.
那么如果我们想精确的控制一下 Hystrix 的参数也是可以的, 比方说跟 Hystrix 结合的参数, 那么可以在 FeignClient 注解里面配置一个 Configuration=XXX 类. class 属性, 在哪个类里面精确的指定一下属性.
或者在 application.yml 里面配置, 如下:
- hystrix:
- command:
- default:
- execution:
- isolation:
- thread:
- timeoutinMilliseconds: 5000
- ribbon:
- connectTimeout: 500
- # 如果想对单独的某个服务进行详细配置, 如下
- hello-service:
- ribbon:
- connectTimeout: 500
这里满足了我们大部分场景的调用, 但是有写精细场景, 还是要用原生的 Hystrix, 跟我们之前的 Hystrix 用法一下, 不要走 Feign 客户端调用就行了, 如下:
- /**
- * Created by cong on 2018/5/17.
- */
- public class HjcCommand extends HystrixCommand {
- protected HjcCommand(HystrixCommandGroupKey group) {
- super(group);
- }
- @Override
- protected Object run() throws Exception {
- return null;
- }
- }
来源: https://www.cnblogs.com/huangjuncong/p/9053576.html