Spring Cloud Feign 聲明式服務調用

在微服務的實踐過程當中,Spring Cloud Ribbon 和 Spring Cloud Hystrix 一般一塊兒使用。java

Spring Cloud Feign 是對這兩個基礎工具的更高層次封裝,在 Netflix Feign 的基礎上擴展了對 Spring MVC 的註解支持,提供了一種聲明式的 Web 服務客戶端定義方式。git

快速入門

啓動服務註冊中心 eureka-server 及服務提供方 hello-service,建立 spring boot 工程 feign-consumer。github

1.添加依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
複製代碼

2.註解啓用 Feign

package com.ulyssesss.feignconsumer;

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;

@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class FeignConsumerApplication {

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

3.定義 HelloService 接口

package com.ulyssesss.feignconsumer.service;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient("hello-service")
public interface HelloService {

    @GetMapping("hello")
    String hello(@RequestParam("p1") String p1, @RequestParam("p2") String p2);
}
複製代碼

其中 @FeignClient 指定服務名,Spring MVC 註解綁定具體的 REST 接口及請求參數。web

注意在定義參數綁定時,@RequestParam 、@RequestHeader 等註解的 value 不能省略,Spring MVC 會根據參數名做爲默認值,但 Feign 中必須經過 value 指定。spring

4.編寫 Controller

package com.ulyssesss.feignconsumer.web;

import com.ulyssesss.feignconsumer.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FeignConsumerController {

    @Autowired
    HelloService helloService;

    @GetMapping("hello")
    public String hello(@RequestParam String p1, @RequestParam String p2) {
        System.out.println("feign consumer get hello");
        return helloService.hello(p1, p2);
    }
}
複製代碼

5.配置服務註冊中心地址

spring.application.name=feign-consumer
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
複製代碼

啓動所有應用,訪問 http://localhost:8080/hello?p1=a&p2=b ,feign-consumer 以聲明式的服務調用訪問 hello-service,返回 hello, a, bapi

繼承特性

當使用 Spring MVC 註解來綁定服務接口時,幾乎能夠徹底從服務提供方的 Controller 中複製,因此可以利用 Feign 的繼承特性作進一步抽象,複用 REST 接口定義,減小編碼量。app

1.添加依賴

建立 Maven 工程 hello-service-api,因爲須要用到 Spring MVC 的註解,因此添加 spring-boot-starter-web 依賴。負載均衡

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <scope>provided</scope>
</dependency>
複製代碼

2.定義接口及 DTO

在 hello-service-api 中定義可以複用的 DTO 和接口定義。ide

package com.ulyssesss.helloserviceapi.service;

import com.ulyssesss.helloserviceapi.dto.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

public interface HelloService {

    @GetMapping("hello")
    String hello(@RequestParam("p1") String p1, @RequestParam("p2") String p2);

    @GetMapping("user")
    User user();

    @PostMapping("post")
    String post();
}
複製代碼
package com.ulyssesss.helloserviceapi.dto;

public class User {
    
    private String name;
    private int age;

    public User() {}

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
    // set get
}
複製代碼

3.改寫 hello-service

在 hello-service 中引入 hello-service-api 依賴,重寫 HelloController。spring-boot

package com.ulyssesss.helloservice.web;

import com.ulyssesss.helloserviceapi.dto.User;
import com.ulyssesss.helloserviceapi.service.HelloService;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController implements HelloService {

    @Override
    public String hello(@RequestParam String p1, @RequestParam String p2) {
        System.out.println("hello service get hello");
        return "hello, " + p1 + ", " + p2;
    }

    @Override
    public User user() {
        System.out.println("hello service get user");
        return new User("Jack", 22);
    }

    @Override
    public String post() {
        System.out.println("hello service post");
        return "post";
    }
}
複製代碼

4.改寫 feign-consumer

在 feign-consumer 中引入 hello-service-api 依賴,建立 RefactorHelloService 繼承自 HelloService。

package com.ulyssesss.feignconsumer.service;

import com.ulyssesss.helloserviceapi.service.HelloService;
import org.springframework.cloud.netflix.feign.FeignClient;

@FeignClient(name = "hello-service")
public interface RefactorHelloService extends HelloService {
}
複製代碼

修改 FeignConsumerController,注入 RefactorHelloService。

package com.ulyssesss.feignconsumer.web;

import com.ulyssesss.feignconsumer.service.RefactorHelloService;
import com.ulyssesss.helloserviceapi.dto.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FeignConsumerController {

    @Autowired
    RefactorHelloService refactorHelloService;

    @GetMapping("hello")
    public String hello(@RequestParam String p1, @RequestParam String p2) {
        System.out.println("feign consumer get hello");
        return refactorHelloService.hello(p1, p2);
    }

    @GetMapping("user")
    public User user() {
        System.out.println("feign consumer get user");
        return refactorHelloService.user();
    }

    @PostMapping("post")
    public String post() {
        System.out.println("feign consumer post");
        return refactorHelloService.post();
    }
}
複製代碼

啓動所有應用,訪問 http://localhost:8080/hello?p1=a&p2=b ,feign-consumer 以聲明式的服務調用訪問 hello-service,返回 hello, a, b

使用 Spring Cloud Feign 的繼承特性,能夠將接口從 Controller 中玻璃,配合 Maven 私有倉庫能夠實現接口定義的共享,減小服務消費方的綁定配置。

Ribbon 及 Hystrix 配置

Spring Cloud Feign 的客戶端負載均衡經過 Spring Cloud Ribbon 實現,經過配置 Ribbon 能夠定義客戶端調用參數。Riibon 和 Hystrix 的配置以下:

spring.application.name=feign-consumer
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

## 啓用 hystrix
feign.hystrix.enabled=true
## 全局超時熔斷時間
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

## 全局鏈接超時時間
ribbon.ConnectTimeout=250
## 全局接口調用超時時間
ribbon.ReadTimeout=10000
## 全局重試全部請求(POST 請求等)開關
ribbon.OkToRetryOnAllOperations=false

## 針對 hello-service 服務,重試切換的實例數
hello-service.ribbon.MaxAutoRetriesNextServer=1
## 針對 hello-service 服務,對當前實例重試次數
hello-service.ribbon.MaxAutoRetries=0
複製代碼

服務降級

Hystrix 提供的服務降級是容錯的重要方法,因爲 Feign 在定義服務客戶端時將 HystrixCommand 的定義進行了封裝,致使沒法使用 @HystrixCommand 的 fallback 參數指定降級邏輯。

Spring Cloud Feign 提供了一種簡單的定義服務降級的方式,建立 HelloServiceFallback 實現 HelloService 接口,經過 @Component 聲明爲 Bean,在 HelloServiceFallback 中實現具體的降級邏輯,最後在 @FeignClient 中經過 fallback 屬性聲明處理降級邏輯的 Bean。

package com.ulyssesss.feignconsumer.service;

import com.ulyssesss.helloserviceapi.dto.User;
import org.springframework.stereotype.Component;

@Component
public class HelloServiceFallback implements RefactorHelloService {

    @Override
    public String hello(String p1, String p2) {
        return "error";
    }

    @Override
    public User user() {
        return new User("error", 0);
    }

    @Override
    public String post() {
        return "error";
    }
}
複製代碼
package com.ulyssesss.feignconsumer.service;

import com.ulyssesss.helloserviceapi.service.HelloService;
import org.springframework.cloud.netflix.feign.FeignClient;

@FeignClient(name = "hello-service", fallback = HelloServiceFallback.class)
public interface RefactorHelloService extends HelloService {
}
複製代碼

啓動服務後斷開服務提供方 hello-service,訪問 feign-consumer 接口,feign-consumer 會按照 HelloServiceFallback 中的定義執行降級邏輯。

原文地址

示例代碼 歡迎 Star

相關文章
相關標籤/搜索