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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM