Spring Cloud 之 Eureka.

1、微服務概述

1. 什麼是微服務

 簡單地說, 微服務是系統架構上的一種設計風格, 它的主旨是將一個本來獨立的系統拆分紅多個小型服務,這些小型服務都在各自獨立的進程中運行,服務之間基於 RPC 進行通訊協做。 被拆分紅的每個小型服務都圍繞着系統中的某一項或一些耦合度較高的業務功能進行構建, 而且每一個服務都維護着自身的數據存儲(劃重點,每一個微服務都有本身的數據庫實例)、 業務開發、自動化測試案例以及獨立部署機制。java

2. 微服務的特性

  • 服務組件化:一個獨立的系統拆成多個小型服務。
  • 以業務劃分服務:微服務應該以業務來劃分,而不是按能力或其餘因素來劃分(好比以前作的一個項目直接將緩存能力建成了一個微服務組件)。
  • 智能端點和啞管道:服務之間經過 RPC 的方式調用,一般會使用如下兩種服務調用方式:
    第一種:使用 HTTP 的 RESTfl API 或輕量級的消息發送協議, 實現信息傳遞與服務調用的觸發。
    第二種:經過在輕量級消息總線上傳遞消息, 相似 RabbitMQ 等 一些提供可靠異步交換的中間件。
  • 去中心化處理:不一樣的微服務組件能夠選擇不一樣的技術方案,甚至能夠選擇不一樣的語言。只有實現了對技術平臺的透明, 才能更好地發揮不一樣語言對不一樣業務處理能力的優點, 從而打造更爲強大的大型系統。
  • 去中心化管理數據:採用分佈式數據庫,不一樣的微服務組件有着本身單獨的數據庫實例。雖然數據管理的去中心化可讓數據管理更加細緻化,經過採用更合適的技術可以讓數據存儲和性能達到最優。可是,因爲數據存儲於不一樣的數據庫實例中後,數據一致性也成爲微服務架構中亟待解決的問題之一。
  • 基礎設施自動化:自動化測試(每次部署前的強心劑, 儘量地得到對正在運行的軟件的信心)、自動化部署(解放煩瑣枯燥的重複操做以及對多環境的配置管理)等。
  • 容錯設計:在微服務架構中,快速檢測出故障源並儘量自動恢復服務是必須被設計和考慮的。
  • 演進式設計:沒有必要一開始就把服務拆分的又細又多,能夠隨着業務系統的發展,把壓力大的服務或者穩定不變化的模塊作拆分合並動做。

2. 微服務的缺陷

  1. 運維的成本提升。這個是不可避免的,原來只須要運維一個單一獨立的系統,如今要管理幾個或者幾十個微服務。
  2. 接口的一致性問題。A 微服務修改了接口,須要調用方B、C 服務同時作出修改。除非開發過程當中嚴格遵照開閉原則(在這個敏捷流式開發的背景下,幾乎很難作到)。
  3. 分佈式的複雜性。網絡延遲、分佈式事務、異步消息等。

tips:分佈式事務自己的實現難度就很是大,因此在微服務架構中,咱們更強調在各服務之間進行 「 無事務 」 的調用,而對於數據一致性,只要求數據在最後的處理狀態是一致的便可;若在過程當中發現錯誤,經過補償機制來進行處理,使得錯誤數據可以達到最終的一致性。git

2、Spring Cloud 簡介

 Spring Cloud 是一個基於Spring Boot實現的微服務架構開發工具。它爲微服務架構中涉及的配置管理、服務治理、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分佈式會話和集羣狀態管理等操做提供了一種簡單的開發方式。github

 Spring Cloud 的出現,能夠說是對微服務架構的巨大支持和強有力的技術後盾。它是一個解決微服務架構實施的綜合性解決框架,它整合了諸多被普遍實踐和證實過的框架做爲實施的基礎部件,又在該體系基礎上建立了一些很是優秀的邊緣組件。舉個 Dubbo 和 Spring Cloud 差別性的例子:在使用 Dubbo 開發過程當中,分佈式配置中心(百度的 Disconf、Netflix的Archaius、360的QConf、淘寶的 Diamond 等)、連接跟蹤(京東的 Hydra、Twitter的 Zipkin 等)...一系列須要的組件,我都要去找第三方進行集成,還要考慮版本兼容的問題。而 Spring Cloud 就是一個微服務解決方案的「全家桶」,幾乎我須要的所有微服務組件,我都能在其中找到「原裝組件」:分佈式配置中心(Config)、連接跟蹤(Sleuth)、批量任務(Task),並且能夠完美兼容。spring

3、Eureka 簡介

 服務治理體系能夠說是微服務架構中最爲核心和基礎的模塊, 它主要用來實現各個微服務實例的自動化註冊與發現。服務治理體系中的三個核心角色: 服務註冊中心、 服務提供者以及服務消費者。而 Eureka Server 就承擔了 Spring Cloud 的服務註冊中心。接下來捋一捋 Eureka Server 進行服務治理的過程:數據庫

  1. 服務註冊:」服務提供者」在啓動的時候會經過發送 REST 請求的方式將本身註冊到 Eureka Server 上,同時帶上了自身服務的一些元數據信息(hostName 之類的)。Eureka Server 接收到這個 REST 請求以後,將元數據信息存儲在一個雙層結構Map中,其中第一層的key是服務名,第二層的key是具體服務的實例名。
  2. 服務同步:因爲服務註冊中心之間互相註冊爲服務(Eureka Server 高可用場景),當服務提供者發送註冊請求到一個服務註冊中心時,它會將該請求轉發給集羣中相連的其餘註冊中心, 從而實現註冊中心之間的服務同步。經過服務同步,兩個服務提供者的服務信息就能夠經過這兩臺服務註冊中心中的任意一臺獲取到。
  3. 服務續約:在註冊完服務以後,「服務提供者」會維護一個心跳用來持續告訴 Eureka Sever "我還活着 」, 以防止Eureka Server 的 「 剔除任務 」 將該服務實例從服務列表中排除出去。
  4. 服務消費:當咱們啓動「服務消費者」的時候,它會發送一個 REST 請求給服務註冊中心,來獲取上面註冊的服務清單 。爲了性能考慮, Eureka Serer會維護一份只讀的服務清單來返回給客戶端,同時該緩存清單會每隔30秒更新一次。
  5. 服務調用:「服務消費者」在 獲取服務清單後,經過服務名能夠得到具體提供服務的實例名和該實例的元數據信息。由於有這些服務實例的詳細信息,因此客戶端能夠根據本身的須要決定具體調用哪一個實例,在 Ribbon 中會默認採用輪詢的方式進行調用,從而實現客戶端的負載均衡。
  6. 服務下線:服務實例進行正常的關閉操做時,它會觸發一個服務下線的 REST 請求給 Eureka Server,告訴服務註冊中心:「我要下線了 」。服務端在接收到請求以後,將該服務狀態置爲下線(DOWN), 並把該下線事件傳播出去。
  7. 失效剔除:有些時候,咱們的服務實例並不必定會正常下線,可能因爲內存溢出、網絡故障等緣由使得服務不能正常工做,而服務註冊中心並未收到 「服務下線 」 的請求。爲了從服務列表中將這些沒法提供服務的實例剔除,Eureka Server 在啓動的時候會建立一個定時任務,默認每隔 一段時間(默認爲60秒) 將當前清單中超時(默認爲90秒)沒有續約的服務剔除出去。
  8. 自我保護:Eureka Server在運行期間,會統計心跳失敗的比例在15分鐘以內是否低於85%, 若是出現低於的狀況(在單機調試的時候很容易知足, 實際在生產環境上一般是因爲網絡不穩定致使),Eureka Server 會將當前的實例註冊信息保護起來, 讓這些實例不會過時, 儘量保護這些註冊信息。

4、Eureka 實戰

SpringBoot 版本號:2.1.6.RELEASE
SpringCloud 版本號:Greenwich.RELEASE緩存

1. 服務註冊中心

  • pom.xml
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
  • application.yml
server:
  port: 1111

eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
  client:
    # 表示不向註冊中心註冊本身
    register-with-eureka: false
    # 註冊中心的職責是維護實例,不須要去檢索服務
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
   server:
     # 是否要打開自我保護機制
     enable-self-preservation: true

eureka 的配置項主要有三項:instance、client、server。「instance」維護該服務的實例信息,包括 hostname、port 這類描述實例特徵的元數據信息;「client」主要是服務註冊數據的配置,好比超時時間、服務緩存時間等;「server」是服務註冊中心特有的配置,配置 Eureka Server 的相關配置項,好比上面的是否打開自我保護。網絡

  • Application.java
//啓動一個服務註冊中心
@EnableEurekaServer
@SpringBootApplication
public class Application {

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

至此,咱們一個Eureka Server — 服務註冊中心就搭建好了。架構

2. 服務提供者

  • pom.xml
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
  • application.yml
server:
  port: 2222

spring:
  application:
    name: cloud-eureka-client

eureka:
  # 服務註冊相關的配置信息
  client:
    service-url:
      defaultZone: http://localhost:1111/eureka/
  instance:
    # 是否優先使用IP地址做爲主機名的標識
    prefer-ip-address: true

就這樣,咱們的一個 Eureka Client 算是註冊到 Eureka Server 上了。接下來,讓咱們試試用 DiscoveryClient 發現咱們的服務信息:app

// 自動化配置, 建立 DiscoveryClient 接口針對 Eureka 客戶端的 EurekaDiscoveryClient 實例
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application .class, args);
    }
}
@RestController
public class HelloController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private DiscoveryClient discoveryClient;
    @Value("${spring.application.name}")
    private String serviceId;

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String index() {
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
        ServiceInstance instance = instances.get(0);
        logger.info("/hello, host:" + instance.getHost() + ", serviceId:" + instance.getServiceId());
        return "Hello World";
    }
}

3. 服務消費者

有了服務註冊中心和服務提供者,咱們試試用 RestTemplate 調用一次服務吧!負載均衡

@RestController
public class ConsumerController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/ribbon-consumer")
    public String helloConsumer() {
        // 這裏訪問的是服務名,而不是一個具體的地址(爲了實現負載均衡策略),在服務治理框架中,這是一個很是重要的特性。
        ResponseEntity<String> result = restTemplate.getForEntity("http://cloud-eureka-client/hello", String.class);
        return result.getBody();
    }
}

5、附加

  • 默認狀況下,Eureka 使用 Jersey 和 XStream 配合 JSON 做爲 Server 與 Client 之間的通訊協議。
  • YAML 的意思實際上是: Yet Another Markup Language — 還是一種標記語言(這個看着有點想笑)。
  • 在 SpringBoot 的屬性配置文件中,能夠經過使用 ${random} 配置來產生隨機的 int 值、long 值或者 string 字符串。
  • 在 Spring Boot 中,多環境配置的文件名須要知足 application-{profile}.properties的格式, 其中{profile}對應你的環境標識。經過啓動參數 --spring.profiles.active=test 來指定激活的環境變量。

演示源代碼 :https://github.com/JMCuixy/spring-cloud-demo

內容參考《Spring Cloud 微服務實戰》

相關文章
相關標籤/搜索