SpringCloud學習筆記——聲明式服務調用Feign

Feign簡介

在前面我們已經搭建了Spring Cloud中的Ribbon和Hytrix,從而實現了微服務架構中客戶端的負載均衡以及斷路器機制保護服務,這兩者的使用非常廣泛並且經常一起出現,SpringCloud Feign其實就是一個整合工具,對兩者進行了整合,除了原有的功能外,還提供了聲明式的Web服務客戶端定義方式。

快速搭建

  • 新創建一個springboot項目feign-consumer,添加feign和eureka依賴:
dependencies {
	implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server'
	implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
}
  • 在啓動類上添加註解開啓Feign支持功能
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class FeignConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(FeignConsumerApplication.class, args);
	}

}
  • 定義HelloService接口,添加@FeignClient註解指定已註冊到註冊中心的服務名,並且創建對應的REST接口
@FeignClient("hello-service")
public interface HelloService {

    @RequestMapping("/hello")
    String hello();
}
  • 定義ConsumerController來實現service層的調用
@RestController
public class ConsumerController {

    @Autowired
    HelloService helloService;

    @RequestMapping(value = "/feign-consumer")
    public String helloConsumer() {
        return helloService.hello();
    }
}

當然最後不要忘記在配置文件中添加註冊中心的地址並且指定端口號,由於這是我們springcloud學習過程中搭建的第四個項目,所以我這裏port指定爲4444

spring.application.name=feign-consumer
server.port=4444

eureka.client.service-url.defaultZone = http://localhost:1111/eureka/,http://localhost:1112/eureka/

我們已經簡單的搭建了feign客戶端,現在啓動項目並且通過接口調用http://localhost:4444/feign-consumer ,你會發現和之前直接使用ribbon-consumer的調用結果是一樣的,但是於ribbon不同的是,通過feign我們只需要定義服務接口,以聲明式的方法優雅處理了服務調用

參數綁定

實際開發過程中這樣的無參數接口幾乎是很少見的,大部分情況下我們需要入參,接下來我們在hello-service中增加兩個帶入參的接口:

public String hello2(String name) {
        return "Hello " + name;
    }

    public String hello3(User user) {
        return "Hello " + user.getName() + ", " + user.getAge();
    }

在HelloController中增加對應的方法:

@RequestMapping(value = "/hello2", method = RequestMethod.GET)
    public String hello2(@RequestParam String name) {
        return helloService.hello2(name);
    }

@RequestMapping(value = "/hello3", method = RequestMethod.POST)
public String hello3(@RequestBody User user) {
    return helloService.hello3(user);
}

最後在feign-consumer中增加相應的實現,有一點需要注意的是,可能平時開發過程中習慣了在@RequestParam後面不會再指定入參的名字,因爲在Spring MVC中將參數名作爲默認值,但是在Feign中這個參數的value值是必須的,否則會報錯:

Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0

這裏比較容易犯錯誤的點就在於如果使用feign發送GET請求的話,那麼在所有參數前都必須加上@RequestParam(“xxx”),而如果是發送POST請求的話,那麼可以加@RequestBody也可以不加,因爲如果沒有註解的話默認是@RequestBody

Ribbon配置

全局配置

Feign客戶端的負載均衡是通過Ribbon實現的,所以在項目中我們可以對Ribbon進行配置,比如設置全局變量的超時時間:

ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000

指定服務配置

全局服務固然簡單,但是像微服務這種項目顯然各個服務的超時時間不可能統一,而是應該根據各自服務的特點進行調整,所以可以採用<client>.ribbon.key=value進行配置:

hello-service.ribbon.ReadTimeout=2000
hello-service.ribbon.ConnectTimeout=500
hello-service.ribbon.OkToRetryOnAllOperations=true
hello-service.ribbon.MaxAutoRetriesNextServer=2
hello-service.ribbon.MaxAutoRetries=1

重試機制

上面的配置中已經包含了關於重試的配置,我們可以修改hello-service進行一下驗證:

  • 在hello-service接口實現中加入隨機延遲,只修改其中一個hello-service實例,另一個保持接口正常有利於後續觀察結果,這裏我們修改了2221端口的實例
//模擬超時
int time = new Random().nextInt(3000);
Thread.sleep(time);
  • 在配置中我們配置了幾個參數,hello-service.ribbon.MaxAutoRetries表示請求失敗後會重試同一個實例n次,如果n次後仍然失敗纔會切換實例訪問,而hello-service.ribbon.MaxAutoRetriesNextServer控制更換實例的次數

  • 配置完成後全部重新啓動,我們仍然通過http://localhost:4444/feign-consumer 的接口調用,會發現當負載均衡到port=2222的實例時請求總是正常的,當請求到port=2221時,由於我們模擬了接口超時,我遇到了這樣的結果:

INFO 19584 --- [nio-2221-exec-1] o.zyc.springcloud.service.HelloService   : /hello, host :localhost, service_id :HELLO-SERVICE
INFO 19584 --- [nio-2221-exec-1] o.zyc.springcloud.service.HelloService   : sleep for 1781ms
INFO 19584 --- [nio-2221-exec-3] o.zyc.springcloud.service.HelloService   : /hello, host :localhost, service_id :HELLO-SERVICE
INFO 19584 --- [nio-2221-exec-3] o.zyc.springcloud.service.HelloService   : sleep for 397ms
INFO 19584 --- [nio-2221-exec-4] o.zyc.springcloud.service.HelloService   : /hello, host :localhost, service_id :HELLO-SERVICE
INFO 19584 --- [nio-2221-exec-4] o.zyc.springcloud.service.HelloService   : sleep for 508ms
INFO 19584 --- [nio-2221-exec-5] o.zyc.springcloud.service.HelloService   : /hello, host :localhost, service_id :HELLO-SERVICE
INFO 19584 --- [nio-2221-exec-6] o.zyc.springcloud.service.HelloService   : /hello, host :localhost, service_id :HELLO-SERVICE
INFO 19584 --- [nio-2221-exec-5] o.zyc.springcloud.service.HelloService   : sleep for 2443ms
INFO 19584 --- [nio-2221-exec-6] o.zyc.springcloud.service.HelloService   : sleep for 725ms

我們可以清楚的看到,前三次請求正常,因爲超時時間設置了2000ms,如何第四次請求2443ms超過了設置,所以根據當前實例重試一次的配置,會再次請求port=2221的實例,這次隨機了725ms,接口仍然正常返回。

這裏有一點需要注意的是,ribbon的超時和hytrix超時完全是兩個概念,一個屬於接口允許的超時時間和重試次數,另一個則是爲了保護系統而設置的最差結果,所以hytrix超時時間必須大於ribbon超時時間,否則接口直接熔斷,配置的重試機制也就毫無意義了

Hystrix配置

那麼接下來我們就看看如何在feign中使用hystrix,在默認情況下,Spring Cloud Feign會將所有的Feign客戶端方法都封裝到Hystrix命令中進行服務保護

禁用Hystrix

我們可以通過feign.hystrix.enabled=false來關閉hystrix功能,但是如果不想全局關閉Hystrix支持的話,可以通過配置Feign.builder實例完成功能

  • 創建一個配置類
@Configuration
public class DisableHystrixConfiguration {

    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
}
  • 在HelloService的@FeignClient註解中加入上面的配置類
@FeignClient(name = "hello-service", configuration = DisableHystrixConfiguration.class)

服務降級配置

在Feign中我們無法像前面是Hystrix那樣在@HystrixCommand註解中加入fallback參數,但是Feign也提供了一種更爲簡單的方式:

  • 創建一個服務接口的服務降級實現類,例如HelloService的實現類HelloServiceFallback,重寫所有方法並針對每個方法提供降級策略即可
@Component
public class HelloServiceFallback implements HelloService {
    @Override
    public String hello() {
        return "error";
    }

    @Override
    public String hello(@RequestParam String name) {
        return "error";
    }

    @Override
    public String hello(@RequestBody User user) {
        return "error";
    }
}
  • 在HelloService的@FeignClient中添加fallback屬性
@FeignClient(name = "hello-service", fallback = HelloServiceFallback.class)

1.要在配置文件中加入feign.hystrix.enabled=true纔可以啓動hystrix
2.記得把之前DisableHystrixConfiguration配置去掉

此時啓動feign-consumer之後不啓動hello-service類,這是調用接口會發現返回了error,也就是我們預先設置的服務降級處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章