SpringCloud(Hoxton.SR3)基礎篇:第六章、Feign聲明式服務調用


一、Feign簡介

  在前面的文章中可以發現當我們通過RestTemplate調用其它服務的API時,所需要的參數須在請求的URL中進行拼接,如果參數少的話或許我們還可以忍受,一旦有多個參數的話,這時拼接請求字符串就會效率低下

  那么有沒有更好的解決方案呢?答案是確定的有,Netflix已經為我們提供了一個框架:Feign。而Feign則會完全代理HTTP請求,我們只需要像調用方法一樣調用它就可以完成服務請求及相關處理。Feign整合了Ribbon和Hystrix,可以讓我們不再需要顯式地使用這兩個組件。

總起來說,Feign具有如下特性:

  • 可插拔的注解支持,包括Feign注解和JAX-RS注解;
  • 支持可插拔的HTTP編碼器和解碼器;
  • 支持Hystrix和它的Fallback;
  • 支持Ribbon的負載均衡;
  • 支持HTTP請求和響應的壓縮。

這看起來有點像我們springmvc模式的Controller層的RequestMapping映射。這種模式是我們非常喜歡的。Feign是用@FeignClient來映射服務的。

 

二、Feign使用搭建

 

  (1) pom.xml引入相關依賴,引入Feign依賴

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- eureka客戶端依賴jar包 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- springBoot運維監控,打開eureka健康檢查需要的jar依賴 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!-- feign客戶端依賴 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

  

  (2) application.yml配置如下:

server:
  port: 8011
spring:
  application:
      name: consumer-movie-feign
#eureka客戶端連接配置
eureka:
   client:
      #打開eureka健康檢查
      healthcheck:
         enabled: true
      service-url:
      #注冊中心地址
         defaultZone: http://user:password123@localhost:8761/eureka
   instance:
      #將ip注冊到eureka上
      prefer-ip-address: true
      #微服務向eureka注冊實例名${spring.cloud.client.ip-address} 表示ip地址 spring2.0以上為ip-address
      instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}}

  

  (3) 傳輸對象User類

import java.math.BigDecimal;

public class User {
  private Long id;

  private String username;

  private String name;

  private Short age;

  private BigDecimal balance;

  //序列化傳輸的時候必須要有空構造方法,不然會出錯
  public User() {
  }
  
  getter() & setter()    
}

 

  (4)Feign的@FeignClient(“服務名稱”)映射服務調用接口類

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.qxj.cloud.entity.User;

@FeignClient(name="provider-user")
public interface UserFeignClient {
    @RequestMapping(value="/simple/{id}",method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id); //// 兩個坑:1. @GetMapping不支持   2. @PathVariable得設置value
}

 

  (5)controller類注入UserFeignClient這個接口,進行遠程服務調用

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.qxj.cloud.entity.User;
import com.qxj.cloud.feign.UserFeignClient;

@RestController
public class MovieController {

    @Autowired
    private UserFeignClient userFeignClient;

    @RequestMapping(value="/movie/{id}",method = RequestMethod.GET,produces="application/json;charset=UTF-8")
    public User findById(@PathVariable Long id) {
        User user = this.userFeignClient.findById(id);
        return user;
    }
}

  

  (6)接着在Feign模塊的啟動類哪里打上Eureka客戶端的注解@EnableEurekaClient和Feign客戶端的注解@EnableFeignClients

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
//該注解表明應用既作為eureka實例又為eureka client 可以發現注冊的服務
@EnableEurekaClient
@EnableFeignClients
public class ConsumerMovieFeignApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ConsumerMovieFeignApplication.class, args);
    }
}

 

  啟動Eureka微服務,啟動provider-user微服務,最后啟動consumer-movie-feign微服務,瀏覽器上輸入localhost:8011/movie/3運行結果如下:

 

 

三、Feign使用Hystrix進行服務降級

  (1)在feign中已經集成了Hystrix組件相關的起步依賴,所以我們不需要額外的添加。

  (2)spring cloud 2.x 以上版本feign默認是關閉了Hystrix功能,application.yml配置啟動Hystrix功能

#默認的feign功能中,熔斷開關是關閉的,所以,熔斷器hystrix的開關需要手動打開
feign:
   hystrix:
      enabled: true

  (3)接下來需要在@FeignClient上添加fallback屬性配置快速失敗處理類。該處理類是feign hystrix的邏輯處理類,還需要繼承UserFeignClient

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.qxj.cloud.entity.User;

@FeignClient(name="provider-user",fallback = HystrixUserFeignFallback.class)
public interface UserFeignClient {
    @RequestMapping(value="/simple/{id}",method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id); //// 兩個坑:1. @GetMapping不支持   2. @PathVariable得設置value
}

  (4)HystrixUserFeignFallback服務降級實現類

import org.springframework.stereotype.Component;

import com.qxj.cloud.entity.User;

//@Component注解將Hystrix類交給spring管理
@Component
public class HystrixUserFeignFallback implements UserFeignClient{

    @Override
    public User findById(Long id) {
        User user = new User();
        user.setId(0L);
        return user;
    }

}

   接着我們再把那服務提供模塊provider-user模塊進行停止,運行結果如下所示:

 

 

 

 

四、使用FallbackFactory檢查回退原因

  (1)修改UserFeignClient接口注解@FeignClient使用fallbackFactory屬性

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.qxj.cloud.entity.User;

//fallback屬性存在時,fallbackFactory會失效
@FeignClient(name="provider-user",/*fallback = HystrixUserFeignFallback.class,*/fallbackFactory = HystrixUserFeignFactory.class)
public interface UserFeignClient {
    @RequestMapping(value="/simple/{id}",method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id); //// 兩個坑:1. @GetMapping不支持   2. @PathVariable得設置value
}

 

 

  (2)HystrixUserFeignFactory實現類

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.qxj.cloud.entity.User;

import feign.hystrix.FallbackFactory;

@Component
public class HystrixUserFeignFactory implements FallbackFactory<UserFeignClient>{

    private static final Logger LOGGER= LoggerFactory.getLogger(HystrixUserFeignFactory.class);
    
    @Override
    public UserFeignClient create(Throwable cause) {
        //捕捉異常
        LOGGER.error("fallback; reason was: {}", cause.getMessage());
        
        //必須返回UserFeignClient(有@FeignClient的接口)的實現類
        return (id) -> {
            User user = new User();
            user.setId(-1L);
            return user;
        };
        
        //必須返回UserFeignClient(有@FeignClient的接口)的實現類
        /*return new UserFeignClient() {
            @Override
            public User findById(Long id) {
                User user = new User();
                user.setId(-1L);
                return user;
            }
            
        };*/
    }

}

  執行結果

 

 

 

 

   控制台打印的異常信息是null

 

 

 

  但實際上錯誤原因是TimeoutException,如下

 

 五、為Feign單個Client禁用Hystrix

  (1)全局禁用Hystrix,只須在 application.yml 中配置 feign.hystrix.enabled=false 即可

  (2)借助Feign的自定義配置,可輕松為指定名稱的Feign客戶端禁用Hystrix。

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.qxj.cloud.entity.User;
import com.qxj.cloud.feign.config.FeignDisableHystrixConfiguration;

@FeignClient(name="provider-user",fallback = HystrixUserFeignFallback.class,configuration = FeignDisableHystrixConfiguration.class)
public interface UserFeignClient {
    @RequestMapping(value="/simple/{id}",method=RequestMethod.GET)
    public User findById(@PathVariable("id") Long id); //// 兩個坑:1. @GetMapping不支持   2. @PathVariable得設置value
}

  (3)FeignDisableHystrixConfiguration配置類實現

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import feign.Feign;

public class FeignDisableHystrixConfiguration {
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}

  運行結果,Hystrix已經被關閉

 


免責聲明!

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



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