一篇文章概括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]