一篇文章概括Spring Cloud微服務教程

banq發表於2019-01-11

現在流行的是Spring Cloud基於NetFlix解決方案提供的解決方案。那麼讓我們來演示如何使用它。原文來自tomask79,點選標題見原文!

1. 註冊中心
基於Spring Cloud的MicroServices的Hearth是Eureka Server。也稱為Discovery Server。因為該伺服器儲存有關您的系統可以在其執行位置,健康狀況和其他方面使用的所有微服務的資訊。很明顯,在生產中,這個伺服器需要具有高可用性。使用Spring Cloud,您可以透過將EnableEurekaServer註釋新增到Spring Boot應用程式的啟動類來建立此伺服器。

@SpringBootApplication
@EnableEurekaServer
public class SpringMicroserviceRegistryApplication {

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

只需要這一行程式碼就可以啟動eureka伺服器了,預設在http://localhost:8761可訪問註冊中心,可以在application.properties/yaml中配置埠等特定配置:

server.port=9761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF

在除錯環境可以配置關閉一些引數:
  • eureka.client.register-with-eureka和eureka.client.fetch-registr引數設定為false表示註冊伺服器不向自己註冊服務。
  • 對logging.level.com.netflix.eureka和logging.level.com.netflix.discovery的設定OFF,關閉了“沒有用於註冊伺服器的副本節點”的警告資訊。


微服務註冊到Discovery Server
現在讓我們實現我們的微服務。首先,Spring Boot應用程式需要知道在哪裡註冊您的微服務:

spring.application.name=personsService
eureka.client.serviceUrl.defaultZone=http://localhost:9761/eureka


還要注意微服務名為“ personsService ”。現在編碼微服務,是一個簡單的REST控制器返回一些JSON:

@RestController
public class PersonsController {

    @RequestMapping("/persons")
    public Persons getPersonById() {
        final Persons persons = new Persons();
        final Person person = new Person();
        person.setName("Tomas");
        person.setSurname("Kloucek");
        person.setDepartment("Programmer");
        persons.getPersons().add(person);

        return persons;
    }
}


Spring Cloud MicroService的客戶端
現在你可以訪問http://localhost:8080/persons ,也可以使用使用RestTemplate 直接訪問這個微服務, 但這樣做是愚蠢的。更聰明的是讓您的客戶端透過MicroService id(在我的情況下是personService)首先訪問註冊伺服器,詢問Ribbon loadbalancer負載平衡器來獲取微服務的URL,然後呼叫該服務。檢視客戶端程式碼:

首先,我們需要讓我們的客戶端成為發現伺服器客戶端。因此EnableDiscoveryClient註釋。(Spring Boot 2以後可不需要此註釋)

@SpringBootApplication
@EnableDiscoveryClient
public class SpringMicroserviceClientApplication {

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


然後我們需要讓Ribbon loadbalancer透過提供服務ID來選擇微服務的一個例項。

@Component
public class MicroServiceClient implements CommandLineRunner {

    @Autowired
    private LoadBalancerClient loadBalancer;

    @Override
    public void run(String... arg0) throws Exception {
        final RestTemplate restTemplate = new RestTemplate();

        final ServiceInstance serviceInstance = loadBalancer.choose("personsService");
        if (serviceInstance != null) {
            System.out.println("Invoking instance at URL: "+serviceInstance.getUri());
            System.out.println(
                restTemplate.getForObject(serviceInstance.getUri()+"/persons", String.class));
        } else {
            System.out.println("Didn't find any running instance of personsService at DiscoveryServer!");
        }
    }
}

LoadBalancerClient將在Discovery伺服器上選擇註冊的一個正在執行的微服務例項!(banq注:通常需要透過Feign實現JSON物件轉換的方式訪問遠端微服務)

執行演示
下載演示
執行發現伺服器

  • mvn clean install(在帶有pom.xml的spring-microservice-registry目錄下)
  • java -jar target / demo-0.0.1-SNAPSHOT.war
  • 訪問http:// localhost:9761

執行MicroService
  • mvn clean install(在帶有pom.xml的spring-microservice-service目錄下)
  • java -jar target/demo-0.0.1-SNAPSHOT.war
  • 現在重新重新整理http:// localhost:9761頁面,您應該看到MicroService已在發現伺服器上註冊。

執行客戶端
  • mvn clean install(在帶有pom.xml的spring-microservice-client目錄下)
  • java -jar target/demo-0.0.1-SNAPSHOT.war



2. 在Spring Cloud 微服務中使用斷路器Circuit-Breaker
在編寫微服務時,如果無法訪問特定微服務,需要告訴微服務要執行什麼操作。也就是說當被訪問的微服務不可用時,有幾個選項:

  • 呼叫另一個備份微服務。
  • 返回一些快取的結果。
  • 返回不可用的頁面...

用於實現此目的的廣泛使用的模式是斷路器模式。在你繼續閱讀之前,一定要閱讀Martin Fowler定義的這個描述。
無論如何,簡而言之。斷路器的作用是將MicroService呼叫方法包裝在代理監控MicroService呼叫失敗中。如果失敗將達到某個閾值,則所有其他呼叫將以異常結束,或者如果您使用備份計劃呼叫來定義... Spring Cloud具有出色的實現,稱為Hystrix

使用Hystrix斷路器
使用之前的personsService這個微服務,使用以下規則向呼叫者新增容錯邏輯:

每20秒(metrics.rollingStats.timeInMilliseconds)從6個請求(收集統計資料circuitBreaker.requestVolumeThreshold),如果所有的人都用崩潰(截至circuitBreaker.errorThresholdPercentage)然後重定向呼叫到後備邏輯。每隔5秒嘗試一次這個微服務是否可用(circuitBreaker.sleepWindowInMilliseconds)。
提到的MicroService呼叫元件將如下所示:

@Component
public class MicroServiceInvoker {

    @Autowired
    private LoadBalancerClient loadBalancer;

    @HystrixCommand(fallbackMethod = "invokeMicroServiceFallback",
                commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "100"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "20000"),
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "6"),
                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000")
                }
            )
    public void invokeMicroService() {
        final RestTemplate restTemplate = new RestTemplate();

        final ServiceInstance serviceInstance = loadBalancer.choose("personsService");
        if (serviceInstance != null) {
            System.out.println("Invoking instance at URL: "+serviceInstance.getUri());
            System.out.println("Result :"+
                    restTemplate.getForObject(serviceInstance.getUri()+"/persons",
                            String.class));
        } else {
            System.out.println("Service is down...");
            throw new IllegalStateException("PersonsService is not running!");
        }
    }

    public void invokeMicroServiceFallback() {
        System.out.println("Waiting for circuit-breaker to close again...");
    }
}


測試斷路器
下載原始碼
執行發現伺服器:

mvn clean install (in the spring-microservice-registry directory with pom.xml)
java -jar target/demo-0.0.1-SNAPSHOT.war


執行微服務

mvn clean install (in the spring-microservice-service directory with pom.xml)
java -jar target/demo-0.0.1-SNAPSHOT.war


在http://localhost:9761確認微服務已經註冊。

執行另外一個微服務,它是上面微服務客戶端,呼叫者:

mvn clean install (in the spring-microservice-client directory with pom.xml)
java -jar target/demo-0.0.1-SNAPSHOT.war


啟動客戶端你會看到:

Invocation number :16
Invoking instance at URL: http://192.168.1.112:8080
Result :{"persons":[{"name":"Tomas","surname":"Kloucek","department":"Programmer"}]}
Invocation number :17
Invoking instance at URL: http://192.168.1.112:8080
Result :{"persons":[{"name":"Tomas","surname":"Kloucek","department":"Programmer"}]}
Invocation number :18
Invoking instance at URL: http://192.168.1.112:8080
Result :{"persons":[{"name":"Tomas","surname":"Kloucek","department":"Programmer"}]}
Invocation number :19


關閉personService微服務 20秒, 你會看到輸出:

Invocation number :18
Invoking instance at URL: http://192.168.1.112:8080
Waiting for circuit-breaker to close again...
Invocation number :19
Invoking instance at URL: http://192.168.1.112:8080
Waiting for circuit-breaker to close again...


一會兒看到輸出:

Invocation number :78
Waiting for circuit-breaker to close again...
Invocation number :79
Waiting for circuit-breaker to close again...


每隔5秒改變到:

Invoking instance at URL: http://192.168.1.112:8080
Waiting for circuit-breaker to close again...


當Hystrix測試微服務例項是否再次正常執行時,在你執行微服務之後,斷路器應該是關閉的,微服務客戶端在啟動執行時就能發現這個情況...總而言之,斷路器有以下狀態:
  • OPEN:微服務呼叫時發生異常,呼叫回退邏輯
  • CLOSED:沒有錯誤。正確呼叫MicroService。
  • HALF-OPENED:當circuitBreakerSleepWindowInMilliseconds時間發生時,Hystrix將允許請求呼叫微服務來測試它是否處於活動狀態=半開狀態。


3. 使用Netlix Feign作為呼叫微服務
之前展示了一個微服務客戶端如何在Ribbon的幫助下使用RestTemplate呼叫另外一個微服務的:

@Component
public class MicroServiceClient implements CommandLineRunner {

    @Autowired
    private LoadBalancerClient loadBalancer;

    @Override
    public void run(String... arg0) throws Exception {
        final RestTemplate restTemplate = new RestTemplate();

        final ServiceInstance serviceInstance = loadBalancer.choose("personsService");
        if (serviceInstance != null) {
            System.out.println("Invoking instance at URL: "+serviceInstance.getUri());
            System.out.println(
                restTemplate.getForObject(serviceInstance.getUri()+"/persons", String.class));
        } else {
            System.out.println("Didn't find any running instance of personsService at DiscoveryServer!");
        }
    }
}

老實說,這裡有太多的樣板程式碼,在大型系統中會經常重複。這是引入Feign的理由:
Feign為您帶來以下好處:
  • 呼叫程式碼是在執行時根據註釋建立的。
  • 無需使用任何負載平衡器來呼叫其他微服務。
  • 微服務呼叫系統更易於維護。

使用Feign的先前程式碼將如下所示:
遠端微服務呼叫返回的是原始JSON,那麼大多數時候你都想要的是Java POJO。因此,讓我們建立另一個呼叫相同MicroService的Feign客戶端宣告:

@Component
@FeignClient("personsService")
public interface JacksonMicroServiceFeignClient {
    @RequestMapping(method = RequestMethod.GET, value = "/persons")
    ClientPersonsTO invokePersonsMicroService();
}


只要呼叫上面這個介面就可以實現遠端微服務的呼叫,比如:

@Autowired
private JacksonMicroServiceFeignClient jacksonMicroServiceFeignClient;
.
.
final ClientPersonsTO clientPersonsTO = jacksonMicroServiceFeignClient.
                        invokePersonsMicroService();


而遠端微服務的程式碼是這個樣子:

@RestController
public class PersonsController {

    @RequestMapping("/persons")
    public Persons getPersonById() {
        final Persons persons = new Persons();
        final Person person = new Person();
        person.setName("Tomas");
        person.setSurname("Kloucek");
        person.setDepartment("Programmer");
        persons.getPersons().add(person);

        return persons;
    }
}

遠端微服務返回的是Persons物件,這個Persons是透過轉換成JSON到達你的客戶端的,你的Feign客戶端將這個JSON字串又轉換為ClientPersonsTO,兩者名稱可以不同,但是內部資料結構應該相同額。

將Feign與Hystrix結合起來
在具有maven依賴關係的類路徑中包含Hystrix:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

當然你也應該包含進Feign等元件,這些配置可以透過Idea等Spring 導航選擇相應元件後自動生成pom.xml配置。

從現在開始,Feign將用Hystrix封裝每個MS微服務呼叫。您可以透過以下設定禁用它:feign.hystrix.enabled = false應用程式屬性。您的Feign客戶端介面方法也可以返回HystrixCommand以允許呼叫者使用帶有Observable Java的反應模式。那麼讓我們建立Feign Hystrix客戶端:

@Component
@FeignClient(value = "personsService", fallback = MicroServiceHystrixFallback.class)
public interface HystrixMicroServiceFeignClient {
    @RequestMapping(method = RequestMethod.GET, value = "/persons")
    ClientPersonsTO getPersonsWithHystrix();
}


如果斷路器開啟,我在這裡定義了後備也就是快速失敗或回退返回。順便說一句,當返回HystrixCommand時,還不支援fallback 。現在,使用archaius配置Feign Hystrix,它使用方法名作為command鍵,因此application.properties中的配置將是:

server.port=8888
eureka.client.serviceUrl.defaultZone=http://localhost:9761/eureka
hystrix.command.getPersonsWithHystrix.fallback.enabled=true
hystrix.command.getPersonsWithHystrix.metrics.rollingStats.timeInMilliseconds=35000
hystrix.command.getPersonsWithHystrix.circuitBreaker.sleepWindowInMilliseconds=5000
hystrix.command.getPersonsWithHystrix.circuitBreaker.requestVolumeThreshold=6
hystrix.command.getPersonsWithHystrix.circuitBreaker.errorThresholdPercentage=100
hystrix.command.getPersonsWithHystrix.execution.isolation.strategy=THREAD


測試
下載原始碼
執行Eureka:

mvn clean install (in the spring-microservice-registry directory with pom.xml)
java -jar target/demo-0.0.1-SNAPSHOT.war


執行被呼叫微服務:

mvn clean install (in the spring-microservice-service directory with pom.xml)
java -jar target/demo-0.0.1-SNAPSHOT.war
verify with http://localhost:9761 that MicroService is registered.


執行呼叫微服務:

mvn clean install (in the spring-microservice-client directory with pom.xml)
java -jar target/demo-0.0.1-SNAPSHOT.war [0-2]


點選標題見原文。

下一篇:Spring Cloud配置和閘道器代理

更多:Spring Cloud微服務雲應用教程
 

相關文章