SpringCloud学习之feign

Posted 专注Java后端技术

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud学习之feign相关的知识,希望对你有一定的参考价值。

一、关于feigin

  feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。当然我们也可以在创建Feign对象时定制自定义解码器(xml或者json等格式解析)和错误处理。

 

二、添加SpringCloud对feign的支持

gradle配置:

compile(\'org.springframework.cloud:spring-cloud-starter-feign\')
View Code

feigin最基本使用方法:

 1 interface GitHub {
 2   @RequestLine("GET /repos/{owner}/{repo}/contributors")
 3   List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
 4 }
 5 
 6 static class Contributor {
 7   String login;
 8   int contributions;
 9 }
10 
11 public static void main(String... args) {
12   GitHub github = Feign.builder()
13                        .decoder(new GsonDecoder())
14                        .target(GitHub.class, "https://api.github.com");
15 
16   // Fetch and print a list of the contributors to this library.
17   List<Contributor> contributors = github.contributors("OpenFeign", "feign");
18   for (Contributor contributor : contributors) {
19     System.out.println(contributor.login + " (" + contributor.contributions + ")");
20   }
21 }
View Code

feign发送json与xml的格式的http请求:

 1 interface LoginClient {
 2 
 3   @RequestLine("POST /")
 4   @Headers("Content-Type: application/xml")
 5   @Body("<login \\"user_name\\"=\\"{user_name}\\" \\"password\\"=\\"{password}\\"/>")
 6   void xml(@Param("user_name") String user, @Param("password") String password);
 7 
 8   @RequestLine("POST /")
 9   @Headers("Content-Type: application/json")
10   // json curly braces must be escaped!
11   @Body("%7B\\"user_name\\": \\"{user_name}\\", \\"password\\": \\"{password}\\"%7D")
12   void json(@Param("user_name") String user, @Param("password") String password);
13 }
View Code

注意示例中需要添加对gson的支持

feign发送https信任所有证书的代码:

 1 final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
 2             @Override
 3             public void checkClientTrusted(
 4                     java.security.cert.X509Certificate[] chain,
 5                     String authType) {
 6             }
 7 
 8             @Override
 9             public void checkServerTrusted(
10                     java.security.cert.X509Certificate[] chain,
11                     String authType) {
12             }
13 
14             @Override
15             public java.security.cert.X509Certificate[] getAcceptedIssuers() {
16                 return null;
17             }
18         }};
19         final SSLContext sslContext = SSLContext.getInstance("TLSv1");
20         sslContext.init(null, trustAllCerts,
21                 new java.security.SecureRandom());
22         // Create an ssl socket factory with our all-trusting manager
23         final SSLSocketFactory sslSocketFactory = sslContext
24                 .getSocketFactory();
25         Feign.builder().client(new Client.Default(sslSocketFactory, (s, sslSession) -> true));
View Code

 

三、在SpringCloud中使用Feign

比如说注册中心有如下服务:

1)application.yml的配置:

spring:
  application:
    name: demo-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8080/eureka,http://localhost:8081/eureka
server:
  port: 8090
View Code

2)创建接口

 1 package com.bdqn.lyrk.consumer.demo.api;
 2 
 3 import org.springframework.cloud.netflix.feign.FeignClient;
 4 import org.springframework.web.bind.annotation.RequestMapping;
 5 
 6 @FeignClient("demo")
 7 public interface DemoConfigService {
 8 
 9     @RequestMapping("/demo.do")
10     String demoService();
11 }
View Code

注意在接口上加上注解:@FeignClient("demo") 注解里的参数是在eureka注册的服务名

3)编写启动类:

package com.bdqn.lyrk.consumer.demo;

import com.bdqn.lyrk.consumer.demo.api.DemoConfigService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;

@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class DemoConsumerProvider {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoConsumerProvider.class, args);
        DemoConfigService demoConfigService = applicationContext.getBean(DemoConfigService.class);
        System.out.println(demoConfigService.demoService());
    }
}
View Code

注意在启动类上加上@EnableFeignClients来开启Feign功能

运行后输出:

 

此时我们可以发现使用feign客户端我们访问服务的代码简洁了好多

4) feign多参数设置方法

service-api模块的接口定义:

public interface IBillService {

    @PostMapping("/queryBill")
    List<BillDTO> queryOrders(@RequestBody  BillsVO billsVO);
}

相关服务实现类:


@RestController
public class BillServiceImpl implements IBillService {

    @Autowired
    private BillMapper billMapper;

    @PostMapping("/queryBill")
    @Override
    public List<BillDTO> queryOrders(@RequestBody BillsVO billsVO) {
        return billMapper.query(BeanMap.create(billsVO));
    }
}

注意 需要在接口定义与实现类的参数上加@RequestBody注解
View Code

 

四、feign中的使用Hystrix

  1) 在@FeignClient中有两个属性我们值得关注一下,它们分别是fallBack和fallBackFactory,当然我们系统里要添加Hystrix支持并且在属性文件里设置:

feign.hystrix.enabled=true

  同样我们要在启动类里加上@EnableCircuitBreaker注解打开Hystrix保护

  

  2) fallBack属性很简单,用来设置降级方法,当feign请求服务失败时所调用的方法, 这里我给出接口的例子:

  首先定义一个接口:IOrderService  

package com.bdqn.lyrk.service.api;

import com.bdqn.lyrk.service.dto.OrderDTO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


public interface IOrderService {

    @GetMapping("/orderId/{orderId}")
    OrderDTO getOrderById(@PathVariable("orderId") Integer orderId);

    @GetMapping("/errorOrder")
    OrderDTO getErrorOrder();
}
View Code

  其次定义Feign的接口OrderServiceClient继承IOrderService 

package com.bdqn.lyrk.order.service.consumer.feign;

import com.bdqn.lyrk.service.api.IOrderService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Primary;

@Primary
@FeignClient(value = "ORDER-SERVER", fallBack="FailedOrderServiceClientImpl.class")
public interface OrderServiceClient extends IOrderService {

}
View Code

 

  由于IOrderService不在同一个项目里,而且SpringCloud不推荐服务端和客户端使用同一个接口,所以我采用继承的方式,注意加上@Primary注解以免使用@Autowired时注入失败

  在定义实现类:

package com.bdqn.lyrk.order.service.consumer.feign;

import com.bdqn.lyrk.service.dto.OrderDTO;
import org.springframework.stereotype.Component;

@Component
public class FailedOrderServiceClientImpl implements OrderServiceClient {
    @Override
    public OrderDTO getOrderById(Integer orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(orderId);
        orderDTO.setOrderName("服务中失败的订单,id为:" + orderId);
        return null;
    }

    @Override
    public OrderDTO getErrorOrder() {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setOrderName("服务中失败的订单");
        orderDTO.setId(-1);
        return orderDTO;
    }
}
View Code

 

  最后@FeignClient中设置属性fallBack="FailedOrderServiceClientImpl.class" 就可以了

 

  3) 当我们需要封装服务端的异常信息时,可以指定fallbackFactory属性,请看下面的例子:

  

package com.bdqn.lyrk.order.service.consumer.feign;

import com.bdqn.lyrk.service.dto.OrderDTO;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class OrderServiceFallbackFactoryImpl implements FallbackFactory<OrderServiceClient> {
    @Override
    public OrderServiceClient create(Throwable cause) {
       return new OrderServiceClient() {
           @Override
           public OrderDTO getOrderById(Integer orderId) {
               OrderDTO orderDTO = new OrderDTO();
               orderDTO.setOrderName(cause.getMessage());
               return orderDTO;
           }

           @Override
           public OrderDTO getErrorOrder() {
               OrderDTO orderDTO = new OrderDTO();
               orderDTO.setOrderName(cause.getMessage());
               return orderDTO;
           }
       };
    }
}
View Code

  注意:FallbackFactory的泛型参数一定要指定为@FeignClient修饰的接口,同时不建议fallback与fallbackFactory同时使用

  最后 我贴一下服务端的实现代码:

  

package com.bdqn.lyrk.springcloud.order.service;

import com.bdqn.lyrk.service.api.IOrderService;
import com.bdqn.lyrk.service.dto.OrderDTO;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;


@RestController
public class OrderServiceImpl implements IOrderService {

    @HystrixCommand(fallbackMethod = "errorDTO",
            commandProperties = {@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")}

    )
    @GetMapping("/orderId/{orderId}")
    @Override
    public OrderDTO getOrderById(@PathVariable("orderId") Integer orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(orderId);
        orderDTO.setOrderName("订单ID为" + orderId + "的订单");
        try {
            TimeUnit.SECONDS.sleep(orderId);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        return orderDTO;
    }

    @Override
    public OrderDTO getErrorOrder() {
        System.out.println(1 / 0);
        return null;
    }

    public OrderDTO errorDTO(Integer orderId) {
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setId(-1);
        orderDTO.setOrderName("错误的订单,请重试");
        return orderDTO;
    }
}
View Code

 

  对于Hystrix可以参考:SpringCloud学习之Hystrix

以上是关于SpringCloud学习之feign的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Alibaba 的学习之feign篇

SpringCloud学习笔记-p3(Feign远程调用)

SpringCloud+Feign环境下文件上传与form-data同时存在的解决办法

Springcloud学习之Nacos

SpringCloud gateway学习之helloWorld

SpringCloud学习之整合eureka