一、關於feigin
feigin是一種模板化,聲明式的http客戶端,feign可以通過注解綁定到接口上來簡化Http請求訪問。當然我們也可以在創建Feign對象時定制自定義解碼器(xml或者json等格式解析)和錯誤處理。
二、添加SpringCloud對feign的支持
gradle配置:

compile('org.springframework.cloud:spring-cloud-starter-feign')
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 }
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 }
注意示例中需要添加對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));
三、在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
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 }
注意在接口上加上注解:@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()); } }
注意在啟動類上加上@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注解
四、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(); }
其次定義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 { }
由於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; } }
最后@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; } }; } }
注意: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; } }
對於Hystrix可以參考:SpringCloud學習之Hystrix