1. 程式人生 > >一篇文章概括Spring Cloud微服務教程

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

現在流行的是Spring Cloud基於NetFlix解決方案提供的解決方案。那麼讓我們來演示如何使用它。

 

一、 註冊中心

基於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

 

二、在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將允許請求呼叫微服務來測試它是否處於活動狀態=半開狀態。

 

三、使用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]