SpringCloud微服務實戰筆記

Spring Boot

Spring Boot有什麼做用

Spring Boot經過自動化的配置簡化Spring原有的樣板化的配置。 java

Spring Boot提供了不少現成的starter,能夠快速的實現各類服務。git

actuator

actuator用於監控和管理服務信息。spring

服務治理:Eureka

有了,找到了!設計模式

Eureka的功能

Eureka的功能是提供服務的註冊和服務的發現。api

實現一個Eureka服務很是簡單:緩存

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

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

配置文件:安全

server.port=1111

eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

服務提供者一樣簡潔,只須要EnableDiscoveryClient服務器

@EnableDiscoveryClient
@SpringBootApplication
public class HelloApplication {

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

提供controller:網絡

@RestController
public class HelloController {
    @RequestMapping("/hello")
    public String index() {
        return "Hello Spring Boot";
    }
}

配置文件application.properties:app

server.port=8888
spring.application.name=hello-service
eureka.client.serviceUrl.defaultZone=http://111.222.83.251:1111/eureka/

服務消費者以下,經過url指明服務名稱和接口,使用RestTemplate進行http請求。

@Service
public class HelloService {
    @Autowired
    RestTemplate restTemplate;

    public String helloService() {
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
    }
}

配置文件:

eureka.client.serviceUrl.defaultZone=http://111.222.83.251:1111/eureka/

Eureka的運行

有三種方法能夠運行:

  1. 能夠經過maven的編譯、打包,最後java -jar運行。
  2. spring-boot-maven-pluginmvn spring-boot:run運行。
  3. 在Idea中直接啓動main方法。

我本機java環境爲9,在項目中設置的java爲1.8,致使只有第3種方法有效,由於其它兩種依賴於本機安裝的java運行環境,儘可能保持編譯環境和運行環境一致。

服務提供者的運行機制

Eureka用了雙層map結構來維護註冊的服務信息,第一層Key爲服務的名稱,第二層key爲服務的實例名稱。

當某一個服務向一個Eureka節點註冊服務時,Eureka會將該請求轉發給其它的Eureka節點,從而能夠同步服務註冊信息。

服務經過週期性的心跳來通知Eureka本身的狀況,又稱爲服務續約(renew)。

服務消費者的運行機制

經過REST請求想Eureka獲取服務列表,週期性的更新服務列表緩存。

服務下線後,Eureka會通知給服務消費者。

服務註冊中心的運行機制

爲了防止服務的異常下線,會週期性的清理列表中未續約的服務。

Region和Zone

一個服務能夠屬於一個Region和多個Zone。

服務用Region和Zone來刻畫本身所處的物理位置,方便負載均衡器就近的選擇同一個Zone服務。同時,服務也能夠根據Region和Zone來選擇應該向哪一個Eureka註冊。

客戶端負載均衡:Ribbon

用RestTemplate發送rest請求

Spring提供了方便的RestTemplate像目標服務發送請求,有GET、POST、PUT和DELETE等基本操做。

如何讓客戶端具備負載均衡能力

LoadBalanced註解經過向RestTemplate添加攔截器,使其具有負載均衡的能力。

在服務消費者中實現以下,在RestTemplate上加入LoadBalanced註解。

@SpringCloudApplication
public class RibbonConsumerApplication {

   @Bean
   @LoadBalanced
   RestTemplate restTemplate() {
       return new RestTemplate();
   }

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

負載均衡器的基本功能

  • BaseLoaderBalancer

    • 維護該服務下的全部節點列表。
    • 定義IPing對象,會定時輪詢列表中的服務,檢查服務是否存活。
    • 定義負載均衡的規則IRule,這裏是線性輪詢的策略。
    • 定義添加、獲取服務的方法。
  • DynamicServerListLoadBalancer

    • 在BaseLoaderBalancer基礎上實現了動態獲取服務的能力,實現了從Eureka查詢服務的方法。
    • 動態的更新服務列表。策略有定時更新和Eureka提示更新兩種。
    • 定義Filter過濾出須要的服務節點,好比區域感知需求的服務會選擇同一個區域的其它服務,好比根據服務的質量評估節點健康情況,選擇更健康的服務節點進入列表。
  • ZoneAwareLoadBalancer

    • 對DynamicServerListLoadBalancer進一步擴展,根據zone對服務實例劃分,而後採用策略先篩選zone,最後再選一個服務實例。

負載均衡的策略

  • RandomRule

    • 隨機從列表中選擇一個
  • RoundRobbinRule

    • 線性輪詢服務
  • RetryRule

    • 在RoundRobbinRule基礎上進行必定的重試
  • WeightedResponseTimeRule

    • 根據服務節點的響應時間,計算節點的權重,響應時間短的更容易被選中。
  • 其它。

    • 採用自定義的過濾規則,先過濾出符合條件的節點結合,而後採用輪詢的方式依次使用。

服務容錯保護:Hystrix

Hystrix的功能

服務降級、服務熔斷、線程和信號隔離、請求緩存、請求合併以及服務監控等。

爲依賴的服務提供艙壁

對於每個依賴的服務,Hystrix提供了專用的線程池,防止某個依賴服務影響其它的依賴服務,這種模式叫作「艙壁模式」(Bulkhead Pattern)。

斷路器實現邏輯

Hystrix經過註解EnableCircuitBreakerhystrixCommand提供了斷路保護功能,在下游服務調用產生異常時進行功能降級。

在以下條件下打開斷路器:

  1. 在每秒請求數量QPS大於閾值時。
  2. 在錯誤百分比大於閾值時。

當斷路器打開時,若是打開時間已經到達設定睡眠時間,則去嘗試發送請求,測試下游服務是否已經恢復正常,若是請求成功,則關閉斷路器,恢復正常。

保留10秒的bucket歷史數據,記錄請求成功、失敗、延遲和拒絕次數做爲開閉斷路器的依據。

消費者端實現以下:

@Service
public class HelloService {
    @Autowired
    RestTemplate restTemplate;

    @HystrixCommand(fallbackMethod = "helloFallback")
    public String helloService() {
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
    }

    public String helloFallback() {
        return "error";
    }
}

怎麼減小網絡請求?Hystrix提供了緩存功能

根據請求參數,想結果緩存入線程安全的map結構。

Hystrix提供了CacheResult來實現緩存。

還能怎麼減小網絡請求?Hystrix提供了合併請求的功能

Hystrix提供了HystrixCollapser進行請求的合併,將一小段時間內的請求合併爲一個,不只減小了網絡請求的次數,同時減小了線程池資源的佔用。

聲明式服務調用:Feign

Feign整合了Ribbon和Hystrix,除了這二者的功能,還提供了聲明式的Web服務客戶端的定義方式。

在調用其它服務時,能夠經過以下簡單封裝實現。FeignClient指明瞭服務的名稱,RequestMapping指明瞭服務的具體接口。和傳統的方式相比,更爲簡潔方便。

@FeignClient("hello-service")
public interface HelloService {
    @RequestMapping("/hello")
    String hello();
}

客戶端和服務端共享接口定義

Feign在客戶端聲明的服務接口和服務端定義的接口是對應的,能夠說形式徹底相同。因此,能夠將這些接口發佈出來,供客戶端和服務端共同使用,從而減小客戶端綁定配置。

API網關服務:Zuul

Zuul針對外部客戶端的訪問,提供了請求路由、負載均衡和校驗過濾等基本功能,還有與服務治理結合、請求轉發的熔斷機制、服務的聚合等。

建立一個簡單的zuul,首先開啓zuul:

@EnableZuulProxy
@SpringBootApplication
public class ApiGatewayApplication {

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

配置路由規則:

spring.application.name=api-gateway
server.port=5555

zuul.routes.api-a.path=/api-a/**
zuul.routes.api-a.serviceId=hello-service

zuul.routes.api-b.path=/api-b/**
zuul.routes.api-b.serviceId=feign-consumer

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

每個路由規則包含兩個部分,path指明瞭外部訪問的url,對應的serviceId指明瞭所要映射到的服務。

Zuul的過濾器

Zuul不只作路由轉發,還會對請求進行過濾。過濾會在請求的各個階段執行:

  • pre:在請求到達網關API時的階段,先作一些前置加工。
  • routing:在請求已經開始路由到目標服務器時,進行處理。
  • error:該過濾器在上述兩階段拋出異常時,會進入該階段進行處理,該階段通常會在上下文中設置error標誌,告訴port階段返回適當的error信息。固然該階段有時候會在post階段以後出現,由於post階段也可能拋出異常,這就致使客戶端可能沒法收到返回的信息,固然,有多種解決該問題的方法,再也不贅述。
  • post:在目標服務將結果返回後,能夠對返回值進行處理,而後將正確的返回值或者error信息返回給客戶端。

其實Zuul的過濾器更像是設計模式中的責任鏈或者handler,一個請求在收到、轉發、返回的各個階段裏,由多個過濾器依次處理。

Zuul動態加載路由配置和過濾器

Zuul結合Spring Cloud Config,能夠動態的獲取配置信息,從而能夠實現動態的更新路由。

Zuul結合Groovy等動態語言,能夠在運行時動態的加載自定義的過濾器。

分佈式配置中心:Config

建立一個Config server十分容易,首先建立一個遠程git倉庫,我在碼雲中建立了https://gitee.com/tiantianchi...

建立spring boot項目,加入以下配置:

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

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

在application.properties中加入:

spring.application.name=config-server
server.port=7001

spring.cloud.config.server.git.uri=https://gitee.com/tiantianchi/spring-config
spring.cloud.config.server.git.searchPaths=config-repo
spring.cloud.config.server.git.username=username
spring.cloud.config.server.git.password=password

在倉庫中建立配置文件tc-dev.properties,而後經過http://localhost:7001/tc/dev能夠訪問其中的配置內容。

Config客戶端的實現以下,首先建立配置文件boostrap.properties文件,填入以下配置:

spring.application.name=tc
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:7001/

server.port=7002

客戶端就能夠從Config server中獲取tc-dev.properties配置文件。

消息總線:Bus

Spring Cloud Bus依賴於現有的消息隊列框架,如RabbitMQ和Kafka,實現微服務之間異步消息的發佈和訂閱。

能夠利用Bus實現配置的自動更新。在git倉庫中的配置發生變化時,經過hook將更新請求發送到Config服務,Config經過Bus發送給相關服務,告知其進行配置的更新。

在Bus中,將RabbitMQ等工具做爲消息發佈和獲取的代理。每一個服務中,由Listener負責監聽和處理事件,EventPublisher負責與消息代理進行通訊,它會收到本地發送的事件,而且發送到代理中,同時從代理中獲取事件,發回給本地註冊的Listener。Endpoint則負責暴露API,用戶能夠經過Endpoint提供的接口發起事件,Endpoint會利用EventPublisher將事件發到消息代理中。

相關文章
相關標籤/搜索