三. SpringCloud服務註冊與發現

MPolaris發表於2021-01-28

1. Eureka

1.1 Eureka理解

什麼是服務治理

Spring Cloud封裝了Netflix公司開發的Eurkeka模組來實現服務治理

在傳統的rpc遠端呼叫框架中,管理每個服務與服務之間依賴關係比較複雜。管理比較複雜服務之間的依賴關係可以實現服務呼叫,負載均衡,容錯等,實現服務發現與註冊。

什麼是服務註冊與發現

Eureka採用了CS的設計架構,Eureka Server作為服務註冊功能的伺服器,它是服務註冊中心,而系統中的其他微服務,使用Eureka的客戶端連線到Eureka Server並維持 心跳連結 。這樣系統的維護人員就可以通過Eureka Server來監控系統中各個微服務是否正常執行。

在服務註冊與發現中,有一個註冊中心。當伺服器啟動的時候會把當前自己伺服器的資訊(比如:服務地址、通訊地址等)以別名方式註冊到註冊中心中。另一方(消費者/服務提供者)以該別名的方式去註冊中心上獲取到實際的服務通訊地址,然後再實現本地RPC呼叫。RPC遠端呼叫框架核心設計思想:在於註冊中心,因為使用註冊中心管理每個服務與服務之間的依賴關係(服務治理概念)。在任何RPC遠端框架中,都會有一個註冊中心(存放服務地址相關資訊(介面地址))

下圖左邊是Eureka系統架構,右邊是Dubbo系統架構

image-20210125231142531

Eureka包含兩個元件:Eureka Server 和 Eureka Client

  • Eureka Server 提供服務註冊中心

    各個微服務節點通過配置啟動後,會在EurekaServer中進行註冊,這樣EurekaServer中的服務登錄檔中將儲存所有可用服務節點的資訊,服務節點的資訊可以在介面中直觀看到。

  • Eureka Client 通過註冊中心進行訪問

    是一個Java客戶端,用於簡化Eureka Server的互動,客戶端同時也具備一個內建的、使用輪詢(round-robin)負載演算法的負載均衡器。在應用啟動後將會向Eureka Server傳送心跳(預設週期為30秒)。如果Eureka Server在多個心跳週期內沒有接收到某個節點的心跳,Eureka Server將會從服務登錄檔中表把這個服務節點移除(預設90秒)

1.2 單機Eureka構建步驟

IDEA生成EurekaServer端服務註冊中心

類似物業公司

  • 建Module cloud-eureka-server7001
  • pom.xml
<dependencies>
    <!--eureka-server-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <!-- 公共模組 -->
    <dependency>
        <groupId>com.polaris</groupId>
        <artifactId>cloud-api-common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <!-- boot web actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 通用配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

SpringBoot 1x 和 SpringBoot 2x對比

<!-- SpringBoot1.X對應的SpringCloud eureka,不要再用了!!! -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- SpringBoot2.X對應的SpringCloud eureka -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
  • yml配置檔案
server:
  port: 7001

eureka:
  instance:
    hostname: localhost  # eureka服務端的例項名稱
  client:
    # false表示不向註冊中心註冊自己
    register-with-eureka: false
    # false表示自己端就是註冊中心,我的職責就是維護服務例項,並不需要去檢索服務
    fetch-registry: false     
    service-url:
      # 設定與Eureka Server互動的地址查詢服務和註冊服務都需要依賴這個地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  • 主啟動類
@SpringBootApplication
@EnableEurekaServer // 宣告我是服務註冊中心
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain.class);
    }
}
  • 測試

執行該Eureka Server主啟動類,訪問 localhost:7001,就會看到下面的服務註冊中心,可以發現目前還沒有任何服務入駐進服務註冊中心中,在應用中顯示:No instances available

image-20210125234159844

EurekaClient端 服務提供者cloud-provider-payment8001修改

  • pom.xml新增依賴
<!-- 注意:與Eureka Server一樣,這裡SpringBoot2x不再使用spring-cloud-starter-eureka -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
  • yml配置檔案新增配置
spring:
  application:
    name: cloud-payment-service  # 入駐Eureka服務註冊中心的服務名稱

eureka:
  client:
    # 表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    # 是否從EurekaServer抓取已有的註冊資訊,預設為true。
    # 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      # 單機版
      defaultZone: http://localhost:7001/eureka # 入駐的服務註冊中心地址
  • 主啟動類新增註解
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain.class,args);
    }
}
  • 測試

注意先要啟動EurekaServer,因為有了服務註冊中心具體的服務提供者才能後向其中註冊自己的服務

可以發現註冊到服務註冊中心的服務名(圖中藍框)即為我們在yml配置檔案中設定的服務名,下面頁面中出現的 紅字 是Eureka的 自我保護機制

image-20210125235021654

EurekaClient端 服務消費者cloud-sonsumer-order80修改

與服務提供者cloud-provider-payment8001修改差不多,不再贅述

spring:
  application:
    name: cloud-order-service # 入駐Eureka服務註冊中心的服務名稱
eureka:
  client:
    # 表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    # 是否從EurekaServer抓取已有的註冊資訊,預設為true。
    # 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      # 單機
      defaultZone: http://localhost:7001/eureka

總結

此時再回看最開始的Eureka系統架構,在服務註冊中心和服務提供者沒有叢集的情況下,7001埠的微服務就對應了服務註冊中心,而該服務不需要向服務註冊中心註冊自己,8001埠的微服務作為服務提供方入住到服務註冊中心,8002埠的微服務作為服務消費方也同樣註冊到服務註冊中心

image-20210126000301308
1.3 叢集Eureka構建步驟

叢集Eureka原理

服務註冊中心Eureka Server中分為 服務註冊服務發現,服務註冊過程將服務資訊註冊進服務註冊中心,服務發現過程從服務註冊中心上獲取服務資訊,而這個過程的實質就是:將服務名作為key儲存,然後根據value取得服務的呼叫地址。

整個Eureka的過程如下:

  • 先啟動Eureka註冊中心
  • 啟動服務提供者服務
  • 服務提供者服務將自身資訊(比如服務地址)以別名方式註冊到Eureka註冊中心
  • 消費者服務在需要呼叫介面時,使用服務別名到註冊中心獲取實際的RPC遠端呼叫地址
  • 消費者獲得呼叫地址後,底層實際是利用 HttpClient 技術實現遠端呼叫
  • 消費者獲得服務地址後會快取字本地JVM記憶體中,預設每間隔30秒更新一次服務呼叫地址

那麼微服務RPC遠端服務呼叫最核心的是什麼呢?

高可用!如果註冊中心只有一個,而這個註冊中心出現了故障那麼整個微服務就直接GG了,整個微服務環境就不可用了,所以應該搭建Eureka註冊中心叢集, 實現 負載均衡 + 故障容錯

那怎麼實現Eureka註冊中心的叢集呢?用一句話總結就是 - 互相註冊,相互守望。如下圖所示,服務註冊中心實現相互註冊讓彼此都知道對方的存在,也就是註冊中心叢集中的每一個註冊中心都知道整個叢集中的其他註冊中心,比如如果有三個註冊服務中心7001,7002,7003,那麼就將7002和7003註冊給7001, 將7002和7001註冊給7003, 將7003和7001註冊給7002, 以此類推,而這些個註冊服務中心 作為一個整體對外看做一個註冊服務中心。

image-20210126001813038

Eureaka叢集環境構建

參考cloud-eureka-server7001新建一個服務註冊中心cloud-eureka-server7002

  • 修改pom.xml

copy複製cloud-eureka-server7001的POM檔案即可

  • 修改對映配置(域名對映),用不同的埠號來對映同一個地址

找到C:\Windows\System32\drivers\etc路徑下的hosts檔案,將其內容修改成如下內容:

# learn-spring-cloud
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
  • yml配置檔案,7001與7002都修改一下(以前是單機)

以前是單機版配置,而現在已經有兩個註冊中心可以看做兩臺機器,顯然 hostname 不能再叫localhost

更改了服務端的例項名稱後,最重要的是在defaultZone中將自己註冊給其他註冊中心

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com  # eureka服務端的例項名稱
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      # 互相註冊,相互守望
      defaultZone: http://eureka7002.com:7002/eureka/
server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      # 互相註冊,相互守望
      defaultZone: http://eureka7001.com:7001/eureka/
  • 測試

兩個服務中心已經完成了互相註冊。主頁面DS Replicas下面的資訊就表示是這個Eureka Server相鄰節點且這些節點加上自己互為一個叢集。

  • 將服務提供者8001和服務消費者80釋出到2臺Eureka叢集配置中

修改其配置檔案即可,就是將自己的微服務註冊到每一個服務註冊中心裡去,見配置檔案中的defaultZone。

eureka:
  client:
    # 表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    # 是否從EurekaServer抓取已有的註冊資訊,預設為true。
    # 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      # 叢集版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka 
  • 測試
image-20210126005558365

服務提供者叢集環境構建

  • 參考8001服務新建8002服務

  • pom.xml

8002和8001的POM檔案一樣

  • yml配置檔案

將埠改為8002,其他和8001相同,兩個微服務 對外暴露的服務名相同均為cloud-payment-service 從而構成叢集。

server:
  port: 8002

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 當前資料來源操作型別
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驅動包
    url: jdbc:mysql://mpolaris.top:3306/cloud-test?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

eureka:
  client:
    # 表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    # 是否從EurekaServer抓取已有的註冊資訊,預設為true。
    # 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      # 叢集版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.polaris.springcloud.entities    # 所有Entity別名類所在包
  • 主啟動類與業務類與8001基本一致

主啟動類名字分別為PaymentMain8001與PaymentMain8002

  • controller

修改controller,新增埠號以區分這兩個具體的微服務:讀取配置檔案中設定的埠號。8002的修改同8001。

@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String serverPort;

    @PostMapping("/save")
    public CommonResult save(@RequestBody Payment payment) {
        int result = paymentService.save(payment);
        log.info("===> result: " + result);
        if(result > 0) {
            return new CommonResult(200,
                    "儲存到資料庫成功,埠號:" + serverPort,result);
        }
        return new CommonResult(400,"儲存到資料庫失敗",null);
    }

    @GetMapping("/get/{id}")
    public CommonResult<Payment> save(@PathVariable("id") Long id) {
        Payment paymentById = paymentService.getPaymentById(id);
        log.info("===> payment: " + paymentById);
        if(paymentById != null) {
            return new CommonResult(200,
                    "查詢成功,埠號:" + serverPort,paymentById);
        }
        return new CommonResult(400,"查詢失敗",null);
    }
}
  • 測試

如圖可以看到此時服務註冊中心構成叢集,而相同名字的服務提供方的實際提供者已經出現了兩個,分別是8001和8002,也就是說服務提供方微服務也實現了叢集。

image-20210126013058983

負載均衡

  • 發現問題:通過服務消費者80訪問,只能訪問到服務提供者8001
image-20210126013401986
  • 也就是說每次訪問的具體微服務都是8001埠的CLOUD-PAYMENT-SERVICE服務,這明顯是不符合業務邏輯的,原因就是在消費方程式碼中我們將服務訪問地址寫死了,沒有實現負載均衡,這顯然是不對的,所以我們應該讓80訪問服務名而不是具體的服務,即應該將其改為服務提供者 微服務名稱!
image-20210126014041933
  • 同時在配置檔案中通過 @LoadBalanced 註解賦予RestTemplate負載均衡能力,該負載均衡預設為輪詢方式。所以將80服務的配置檔案修改如下:
@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced //使用該註解賦予RestTemplate負載均衡的能力
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}
  • 然後重啟80埠,發現每次訪問 CLOUD-PAYMENT-SERVICE 服務時,具體的微服務在8001和8002之間進行輪詢切換。
Video_2021-01-26_015202
  • 當然此時負載均衡我們還沒有用到Ribbon,在Ribbon和Eureka整合後,消費者可以直接呼叫服務而不用再關心地址和埠號,且該服務還有負載功能。

總結

image-20210126020536213
1.4 actuator微服務資訊完善

主機名稱:服務名稱修改

發現問題:在註冊中心顯示的微服務中,我們發現服務名含有主機名稱,這顯然不是我們希望看到的

image-20210126225633299

怎麼能解決這個問題呢,只需要修改服務提供方(8001和8002)的配置檔案,向其中的eureka部分加入instance例項即可配置該服務顯示的服務名稱

instance:
  instance-id: payment8001

最終的整體配置檔案如下:

server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 當前資料來源操作型別
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驅動包
    url: jdbc:mysql://mpolaris.top:3306/cloud-test?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 1234321

eureka:
  client:
    # 表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    # 是否從EurekaServer抓取已有的註冊資訊,預設為true。
    # 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      # 叢集版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
      # 單機版
      # defaultZone: http://localhost:7001/eureka
  instance:
    instance-id: payment8001

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.polaris.springcloud.entities

8002服務的修改同上,此時再訪問註冊中心,看到的服務具體名稱中就沒有主機名了,而是我們配置好的服務名稱:

image-20210126225527532

訪問資訊有IP資訊提示

發現問題:我們在滑鼠移動到具體服務時,提示的地址資訊中並沒有服務所在具體主機的IP地址,這在開發中不方便定位具體微服務。

image-20210126225852166

解決方式仍然是通過配置檔案,在配置檔案中向其中的eureka部分加入優先ip地址即可配置該服務訪問路徑可以顯示IP地址:

instance:
  prefer-ip-address: true  # 訪問路徑可以顯示IP地址
image-20210126230236440

最終的配置檔案如下:

eureka:
  client:
    # 表示是否將自己註冊進EurekaServer預設為true。
    register-with-eureka: true
    # 是否從EurekaServer抓取已有的註冊資訊,預設為true。
    # 單節點無所謂,叢集必須設定為true才能配合ribbon使用負載均衡
    fetchRegistry: true
    service-url:
      # 叢集版
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    instance-id: payment8001
    prefer-ip-address: true #訪問路徑可以顯示IP地址
1.5 服務發現Discovery

對於註冊進Eureka服務註冊中心的微服務,可以通過服務發現來獲取該服務的資訊

修改微服務的Controller

向其中注入DiscoveryClient,並編寫相應Controller方法

DiscoveryClient物件中的 getServices 方法用於獲取服務列表的資訊,也就是有哪些服務,如cloud-payment-service服務, getInstances 方法用於獲取服務列表對應的具體服務例項,如cloud-payment-service服務對應的8001和8002服務。

import org.springframework.cloud.client.discovery.DiscoveryClient;
//...
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
	//...

    @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping("/discovery")
    public Object discovery() {
        //獲取服務列表的資訊
        List<String> services = discoveryClient.getServices();
        for (String service : services) {
            log.info("===> service:" + service);
        }

        //根據微服務名稱獲取具體服務例項
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance instance : instances) {
            log.info("===> " + instance.getServiceId()
                    + "\t" + instance.getHost()
                    + "\t" + instance.getPort()
                    + "\t" + instance.getUri());
        }
        return this.discoveryClient;
    }
	//...
}

修改主啟動類

只需要在主啟動類上新增註解@EnableDiscoveryClient,修改後的主啟動類:

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}

測試
訪問地址http://localhost:8001/payment/discovery,我們可以看到獲取的服務資訊,即完成了服務發現:

image-20210126234422613

後臺也對服務列表進行了日誌列印:

<img src="https://gitee.com/mp2333/blog-img/raw/master/SpringCloud/20210126234348.png" alt="image-20210126234348515" https://gitee.com/mp2333/blog-img/raw/master/SpringCloud/20210126234854.png />

1.6 Eureka自我保護(屬於CAP裡面的AP分支)

自我保護機制

保護模式主要用於一組客戶端和EurekaServer之間存在網路分割槽場景下的保護。一旦進入保護模式,Eureka Server將會嘗試保護其服務登錄檔中的資訊,不再刪除服務登錄檔中的資料,也就是不會登出任何微服務。換句話說就是,某時刻某一個微服務不可用了,Eureka不會立刻清理,而是依舊會對該微服務的資訊進行儲存。

如果在Eureka Server的首頁看到以下提示,說明Eureka進入了保護模式

image-20210126234854836

產生原因

為什麼會產生Eureka自我保護機制? => 為了防止 EurekaClient可以正常執行,但是與EurekaServer網路不通情況下,EurekaServer不會立刻將EurekaClient服務剔除。

什麼是自我保護模式? => 預設情況下,如果EurekaServer在一定時間內沒有接收到某個微服務例項的心跳,EurekaServer將會登出該例項(預設90秒)。但是當網路分割槽故障發生(延時、卡頓、擁擠)時,微服務與EurekaServer之前無法正常通訊,以上行為可能變得非常危險 - 因為微服務本身是健康的,只是由於網路問題連結不到EurekaServer,此時本不應該登出這個微服務。Eureka通過“自我保護模式”來解決這個問題 :當EurekaServer節點在短時間內丟失過多客戶端時(可能發生了網路分割槽故障,網路延時),那麼這個節點就會進入自我保護模式。在自我保護模式中,EurekaServer會保護服務登錄檔中的資訊,不再登出任何服務例項,寧可保留錯誤的服務註冊資訊,也不盲目登出任何可能健康的服務例項。使用自我保護模式,可以讓Eureka叢集更加的健壯、穩定。

怎麼禁止自我保護

先在EurekaServer端修改配置檔案即可設定關閉自我保護機制

eureka:
  server:
    # 關閉自我保護機制,保證不可用服務被及時剔除。預設為true開啟
    enable-self-preservation: false
    # 時間間隔,單位ms
    eviction-interval-time-in-ms: 2000 

然後在EurekaClient端修改配置檔案

eureka:
  instance:
    instance-id: payment8001
    # Eureka客戶單向服務端傳送心跳的時間間隔,默然是30秒,這裡改成1秒
    lease-renewal-interval-in-seconds: 1
    # Eureka服務端在收到最後一次心跳後等待時間上限,默然為90秒,超時將剔除服務,這裡改成2秒
    lease-expiration-duration-in-seconds: 2

這樣就會使EurekaClient客戶端的微服務很快死亡。

2. Zookeeper

2.1 Eureka停止更新

https://github.com/Netflix/eureka/wiki

我們可以使用SpringCloud整合Zookeeper替代Eureka

2.2 Zookeeper理解

Zookeeper是一個分散式協調工具,可以實現註冊中心功能

安裝Zookeeper

# 關閉Linux伺服器防火牆(關閉預設埠2181也行)
# 2181	對Client端提供服務的埠
# 3888	選舉Leader
# 2888	叢集內的機器通訊使用。(Leader使用此埠)
systemctl stop firewalld
systemctl status firewalld

# 我這裡使用的是zookeeper-3.4.11.tar.gz,解壓即可

# bin目錄下啟動zookeeper伺服器
./zkServer.sh start
 
# 連線zookeeper客戶端
# 如果是連線同一臺主機上的zk程式,那麼直接執行bin/目錄下的kCli.sh,即可連線上zk。
# 直接執行zkCli.sh命令預設以主機號 127.0.0.1,埠號 2181 來連線zk
# 如果要連線不同機器上的zk,可以使用 -server 引數,例如:
./zkCli.sh -server 192.168.0.1:2181
 
# 啟動報錯?
# grep: /usr/local/zookeeper-3.4.11/bin/../conf/zoo.cfg: No such file or directory
# 這裡的原因是因為下載下來的zoo.cfg名字是zoo_sample.cfg,只需要改名字即可
mv zoo_sample.cfg zoo.cfg

Zookeeper伺服器取代Eureka伺服器,zk作為服務註冊中心

2.3 服務提供者

新建cloud-provider-payment8004

pom.xml

<dependencies>
	<!-- 公共模組 -->
	<dependency>
		<groupId>com.polaris</groupId>
		<artifactId>cloud-api-common</artifactId>
		<version>${project.version}</version>
	</dependency>
	<!-- boot web actuator -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-actuator</artifactId>
	</dependency>
    
	<!--SpringBoot整合Zookeeper客戶端-->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
	</dependency>
    
	<!-- 通用配置 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
		<scope>runtime</scope>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<optional>true</optional>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

yml配置檔案

server:  
  # 8004表示註冊到zookeeper伺服器的支付服務提供者埠號
  port: 8004
spring:
  application:
    # 服務別名 - 註冊zookeeper到註冊中心的名稱
    name: cloud-provider-payment
  cloud:
    zookeeper:
      # zookeeper訪問地址
      connect-string: mpolaris.top:2181

主啟動類

@SpringBootApplication
//該註解用於向使用consul或zookeeper作為註冊中心時註冊服務
@EnableDiscoveryClient
public class PaymentMain8004 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8004.class,args);
    }
}

controller

@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/zk")
    public String paymentZk() {
        return "===> SpringCloud with zookeeper:" 
                + serverPort 
                + "\t" 
                + UUID.randomUUID().toString();
    }
}

啟動8004註冊進zookeeper

  • 啟動zk: zkServer.sh start
  • 啟動8004後報錯
image-20210127012232353
  • why?

    • 解決zookeeper版本jar包衝突問題
image-20210127012945364
  • 排除zk衝突後的新pom.xml
<!--SpringBoot整合Zookeeper客戶端-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
	<!-- 先排除自帶的zookeeper3.5.3 -->
	<exclusions>
		<exclusion>
			<groupId>org.apache.zookeeper</groupId>
			<artifactId>zookeeper</artifactId>
		</exclusion>
	</exclusions>
</dependency>

<!-- 新增zookeeper3.4.11版本zookeeper -->
<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.11</version>
</dependency>

驗證測試1

image-20210127013617048

訪問 http://localhost:8004/payment/zk

image-20210127014618342

驗證測試2

image-20210127014115054

獲得json串後用線上工具檢視如下

image-20210127014501415

思考

服務節點是臨時節點還是持久節點? => 臨時節點

image-20210127015507188
2.4 服務消費者

新建cloud-consumerzk-order80

pom.xml

<dependencies>
    <!-- 公共模組 -->
    <dependency>
        <groupId>com.polaris</groupId>
        <artifactId>cloud-api-common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <!-- web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--SpringBoot整合Zookeeper客戶端-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        <exclusions>
            <!--先排除自帶的zookeeper3.5.3-->
            <exclusion>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--新增zookeeper3.4.11版本-->
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.11</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!-- 通用配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

yml配置檔案

server:
  port: 80
spring:
  application:
    # 服務別名
    name: cloud-consumer-order
  cloud:
    zookeeper:
      # 註冊到zookeeper地址
      connect-string: mpolaris.top:2181

主啟動類

@SpringBootApplication
@EnableDiscoveryClient
public class OrderZkMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderZkMain80.class, args);
    }
}

業務類

配置類注入RestTemplate

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

controller

@RestController
@Slf4j
@RequestMapping("/consumer")
public class OrderZkController {

    public static final String INVOKE_URL = "http://cloud-provider-payment";
    @Resource
    private RestTemplate restTemplate;


    /**
     * http://localhost/consumer/payment/zk
     * @return
     */
    @GetMapping("/payment/zk")
    public String paymentInfo() {
        return restTemplate.getForObject(INVOKE_URL 
                                         + "/payment/zk", String.class);
    }
}

測試驗證

訪問地址 http://localhost/consumer/payment/zk

3. Consul

3.1 理解

什麼是Consul

Consul是一套開源的分散式服務發現配置管理系統,由HashiCorp公司用Go語言開發。提供了微服務系統中的 服務治理配置中心控制匯流排 等功能。這些功能中的每一個都可以根據單獨需要使用,也可以一起使用以構建全方位的服務網路,總之Consul提供了一種完整的服務網路解決方案。其官方介紹見Consul官網

它具有很多優點。包括基於raft協議,比較簡潔;支援健康檢查,同時支援HTTPDNS協議,支援跨資料中心的WAN叢集,提供圖形介面,跨平臺,支援Linux/Mac/Windows。

Consul作用

  • 服務發現Service Discovery:提供HTTP和DNS兩種發現方式。
  • 健康監測Health Checking:支援多張方式,HTTP、TCP、Docker、Shell指令碼定製化
  • KV儲存:Key、Value的儲存方式
  • 多資料中心:Consul支援多資料中心
  • 視覺化Web介面
3.2 安裝並執行Consul

官網下載Windows的64位版本Consul後下載的是zip壓縮包,將壓縮包解壓后里面只有一個consul.exe檔案。在該路徑下雙擊其exe檔案就可以執行Consul,進入cmd命令列執行,輸入以下命令檢視Consul的版本號資訊,我用的是1.7.3版本:

consul --version

當然也可以不用有點low的雙擊Consul啟動,可以在命令列中用開發模式啟動,輸入以下命令

consul agent -dev

啟動後我們訪問 localhost:8500 ,就可以看到Consul和Eureka一樣,有一個前端視覺化Web介面。這樣的話Consul服務註冊中心就已經啟動了,且執行在8500埠

image-20210127234107809
3.3 服務提供者

新建cloud-provider-consul-payment8006

pom.xml

在Eureka的服務提供方中我們引入瞭如下的依賴:

<!--eureka-client-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

而我們現在服務註冊中心用的不再是Eureka而是Consul,只需要被Eureka Client的依賴更改為如下依賴,這樣引入了讓Consul服務註冊中心發現自己微服務的相關jar包。

<!--SpringCloud consul-server -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

yml配置檔案

server:
  port: 8006  # consul服務埠號

spring:
  application:
    name: consul-provider-payment
  # consul註冊中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

主啟動類

@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8006.class);
    }
}

業務類Controller

@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @RequestMapping("/consul")
    public String paymentConsul() {
        return "springcloud with consul: " 
            + serverPort 
            + "\t" 
            + UUID.randomUUID().toString();
    }
}

測試

經過以上配置,我們會在Consul服務註冊中心中發現入駐的 consul-provider-payment 服務

image-20210127235144150
3.4 服務消費者

新建cloud-consumer-consul-order80

pom.xml

為了可以將自己註冊到Consul服務註冊中心,和服務提供方一樣,仍然是引入如下依賴:

<!--SpringCloud consul-server -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

yml配置檔案

和服務提供方几乎完全相同,只需要修改下自己的埠號和自己的服務名稱

# consul服務埠號
server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  # consul註冊中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        # hostname: 127.0.0.1
        service-name: ${spring.application.name}

主啟動類

@SpringBootApplication
@EnableDiscoveryClient //用於向使用Consul或Zookeeper作為註冊中心時註冊服務
public class OrderConsulMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderConsulMain80.class);
    }
}

配置類注入RestTemplate

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced  //使用該註解賦予RestTemplate負載均衡的能力
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

業務類Controller

@RestController
@Slf4j
public class OrderConsulController {

    //要訪問的服務提供方的微服務名稱
    public static final String INVOKE_URL = "http://consul-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/consul")
    public String paymentInfo() {
         return restTemplate.getForObject(INVOKE_URL
                + "/payment/consul", String.class);
    }
}

測試

將服務消費方微服務啟動後,在Consul服務註冊中心我們可以發現同時有了提供者和消費者

服務提供方自測是沒有問題的,下面我們通過服務消費方來訪問提供方服務,發現也可以正常訪問

image-20210127235933366

4. 三者異同

4.1 比較
元件名 編寫語言 CAP 服務健康檢查 對外暴露介面 SpringCloud整合
Eureka Java AP 可配支援 HTTP 已整合
Consul Go CP 支援 HTTP/DNS 已整合
Zookeeper Java CP 支援 客戶端 已整合
4.2 什麼是CAP

CAP理論:首先我們要知道CAP對應的都是什麼。

C A P
Consistency Available Partition tolerance
強一致性 可用性 分割槽容錯性

所謂CAP原則又稱CAP定理,指的是在一個分散式系統中,一致性、可用性、分割槽容錯性。CAP 原則指的是這三個要素 最多隻能同時實現兩點,不可能三者兼顧。在分散式架構中,P永遠要求被保證,所以當前的分散式架構只有AP和CP兩種。因此根據CAP原理將NoSQL資料庫分成了滿足CA原則、滿足CP原則和滿足AP原則三大類:

  • CA:單點叢集,滿足一致性、可用性的系統,通長在可擴充套件性上不太強大。
  • CP:滿足一致性、分許容錯性的系統,通常效能不是特別高。
image-20210128000949795
  • AP:滿足可用性、分割槽容錯性的系統,通常可能對一致性要求低一些。
image-20210128001046046

相關文章