SpringCloud第七章,升級篇,服務註冊與發現Eureka、Zookeeper和Consule

SpringCloud第七章,升級篇,服務註冊與發現Eureka、Zookeeper和Consule

一、基礎概念

1、服務治理

傳統的遠程RPC遠程調用框架中,管理每個服務與服務之間的依賴關係比較複雜。所以需要使用服務治理,用於管理服務與服
務之間的依賴關係,可以實現服務調用、負載均衡、容錯等。實現服務的註冊與發現。

Eureka模塊就是用來實現服務治理的

2、服務註冊與發現

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

在服務註冊與發現中,有一個註冊中心。當服務器啓動的時候,會把當前自己服務器的信息比如服務通訊地址以別名的方式
註冊到註冊中心上。另一方,服務消費者以該別名的方式去註冊中心上獲取實際的服務通訊地址,然後再實現本地RPC調用。

RPC遠程調用框架的設計思想在於:註冊中心。因爲使用註冊中心管理每個服務與服務之間的依賴關係。
在任何RPC遠程調用框架中,都會有一個註冊中心(存放服務地址相關信息)。

在這裏插入圖片描述

二、Eureka

Eureka包含兩個組件 Eureka Server和Eureka Client,Eureka Server 提供服務註冊服務。Eureka Client通過註冊中心進行訪問。

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

<!--Eureka Client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
Eureka Server 提供服務註冊服務。
	各微服務節點通過配置啓動後,會在EurekaServer中進行註冊。這樣EurekaServer中的服務註冊表中就會存儲所有
可用服務節點的信息,各服務節點的信息就可以在界面中直觀看到。

Eureka Client通過註冊中心進行訪問。
	是一個java客戶端,用於簡化EurekaServer的交互,客戶端同時也具備一個內置的、使用輪詢round-robin負載
算法的負載均衡器。在應用啓動後,將會向EurekaServer發送心跳(默認週期爲30s)如果EurekaServer在多個心跳週期內沒有收到某個節點的心跳,EurekaServer將會從服務註冊表中將這個服務節點移除(默認90s)。

1、單機Eureka服務構建

1.1、Eureka構建

new maven module

moduleName   cloud-Eureka-server-7001
parentProject  cloud_2020
groupId      com.lee.springcloud
artifactId    cloud_2020
packaging     jar

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-Eureka-server-7001</artifactId>

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

        <dependency>
            <groupId>com.lee.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        
		<!--springboot-->
        <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>

</project>

application.yml

server:
  port: 7001

eureka:
  instance:
    hostname: localhost 
  client:
    register-with-eureka: false #表示不向註冊中心註冊自己
    fetch-registry: false #不需要去註冊中心獲取其他服務
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機 指向自己

主啓動類

package com.lee.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

//這是Eureka server
@EnableEurekaServer
@SpringBootApplication
public class EurekaMain7001 {

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

測試:

http://localhost:7001/

在這裏插入圖片描述

下面我們要將服務的提供者provider和服務的消費者consumer都注入eureka中

1.2、cloud-provider-payment-8001構建

POM添加

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

application.yml添加

eureka:
  client:
    register-with-eureka: true #向eureka server註冊自己
    fetch-registry: true #需要去註冊中心獲取其他服務的地址
    service-url:
      defaultZone: http://localhost:7001/eureka ##指向Eureka服務註冊中心
      
#原來的配置 表示自己向Eureka server註冊時,自己的服務名稱
spring:
  application:
    name: cloud-payment-service #自己的服務名稱

主啓動類添加

//表示自己是Eureka的客戶端
@EnableEurekaClient

測試:

##啓動eureka provider,刷新Eureka   查看是否註冊
http://localhost:7001/

在這裏插入圖片描述

1.3、cloud-consumer-order-80構建

幾乎同cloud-provider-payment-8001一樣

POM添加

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

application.yml添加

spring:
  application:
    name: cloud-consumer-order #表示自己向Eureka server註冊時,自己的服務名稱
eureka:
  client:
    register-with-eureka: true #向eureka server註冊自己
    fetch-registry: true #需要去註冊中心獲取其他服務的地址
    service-url:
      defaultZone: http://localhost:7001/eureka #指向Eureka服務註冊中心

主啓動類添加

//表示自己是Eureka的客戶端
@EnableEurekaClient

測試:

##啓動eureka provider和consumer,刷新Eureka   查看是否註冊
http://localhost:7001/

在這裏插入圖片描述

1.4、修改消費者訪問機制

原來consumer-order訪問provider-payment的方法是通過指定provider的真實地址使用RestTemplate直接進行訪問的(Eureka並未參與其中):

private static final String PAYMENT_URL = "http://localhost:8001/";

restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);

restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class, id);

現在,我們要將consumer-order通過provider-payment向Eureka註冊的服務名稱,在Eureka server中進行查找provider-payment的真實地址,然後再進行訪問(Eureka參與其中):

現修改consumer-order的controller如下:

package com.lee.springcloud.controller;

import com.lee.springcloud.entities.CommonResult;
import com.lee.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController
@RequestMapping("/consumer")
public class OrderController {

    //這是provider-payment-8001的真實路徑
    //private static final String PAYMENT_URL = "http://localhost:8001/";

    //這是provider-payment-8001在Eureka Server中註冊的‘服務名稱’
    private static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE";

    @Resource
    private RestTemplate restTemplate;

    @PostMapping("/payment/create")
    public CommonResult<Payment> create(@RequestBody Payment payment) {
        return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
    }

    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        return restTemplate.getForObject(PAYMENT_URL + "/payment/get/" + id, CommonResult.class, id);
    }



}

修改配置類如下:

package com.lee.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * 配置類
 */
@Configuration
public class ApplicationContextConfig {


    //標註此註解後,RestTemplate就具有了客戶端負載均衡能力
    //必須添加此註解,否則java.net.UnknownHostException: CLOUD-PAYMENT-SERVICE
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

測試:

##啓動Eureka  provider consumer

http://localhost/consumer/payment/get/2

結果:

<CommonResult>
    <code>200</code>
    <message>查詢數據成功 :Payment(id=2, serial=002)</message>
    <data/>
</CommonResult>

2、Eureka集羣構建

爲了防止Eureka服務的單節點故障,實現高可用,我們需要搭建Eureka集羣。

在這裏插入圖片描述

2.1、cloud-Eureka-server-7002

參考cloud-Eureka-server-7001創建cloud-Eureka-server-7002

host文件修改

#路徑 C:\Windows\System32\drivers\etc\hosts

127.0.0.1  eureka7001.com
127.0.0.1  eureka7002.com

application.yml文件修改

##cloud-Eureka-server-7001配置如下:
server:
  port: 7001

spring:
  application:
    name: cloud-eureka-server #eureka服務端實例名稱
eureka:
  instance:
    hostname: eureka7001.com   #其實還是localhost
  client:
    register-with-eureka: false #表示不向註冊中心註冊自己
    fetch-registry: false #不用去註冊中心獲取其他服務的地址
    service-url:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機 指向自己
      defaultZone: http://eureka7002.com:7002/eureka #集羣 指向另一個Eureka Server服務地址
      
      
      
      
##cloud-Eureka-server-7002配置如下:
server:
  port: 7002

spring:
  application:
    name: cloud-eureka-server #eureka服務端實例名稱
eureka:
  instance:
    hostname: eureka7002.com #其實還是localhost
  client:
    register-with-eureka: false #表示不向註冊中心註冊自己
    fetch-registry: false #不用去註冊中心獲取其他服務的地址
    service-url:
      #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #單機 指向自己
      defaultZone: http://eureka7001.com:7001/eureka #集羣 指向另一個Eureka Server服務地址

測試:

訪問:http://localhost:7001/  和   http://localhost:7002/

結果:

頁面分別出現
DS Replicas
	eureka7002.com
和
DS Replicas
	eureka7001.com

2.2、cloud-provider-payment-8001修改集羣

修改application.yml如下:

eureka:
  client:
    register-with-eureka: true #向eureka server註冊自己
    fetch-registry: true #需要去註冊中心獲取其他服務的地址
    service-url:
      #defaultZone: http://localhost:7001/eureka #單機 指向Eureka服務註冊中心
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002:7002.com/eureka #集羣 執行Eureka服務註冊中心

2.3、cloud-consumer-order-80修改集羣

修改application.yml如下:

同上

eureka:
  client:
    register-with-eureka: true #向eureka server註冊自己
    fetch-registry: true #需要去註冊中心獲取其他服務的地址
    service-url:
      #defaultZone: http://localhost:7001/eureka #單機 指向Eureka服務註冊中心
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002:7002.com/eureka #集羣 指向eureka服務註冊中心

2.4、創建cloud-provider-payment-8002

參考cloud-provider-payment-8001創建cloud-provider-payment-8002

爲了區分8001和8002,現修改他們的controller如下

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

    @Resource
    private PaymentService paymentService;

    //爲了區分調用的哪個8001和8002兩個provider
    @Value("${server.port}")
    private String serverPort;


    @PostMapping("/create")
    public CommonResult create(@RequestBody Payment payment) {
        int result = paymentService.create(payment);
        log.info("插入數據的ID:\t" + payment.getId());
        log.info("插入結果:" + result);
        if (result > 0) {
            return new CommonResult(200, "插入數據成功 serverPort:"+serverPort +result);
        } else {
            return new CommonResult(444, "插入數據失敗 serverPort"+serverPort, null);
        }
    }

    @GetMapping("/get/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id) {
        Payment payment = paymentService.getPaymentById(id);
        log.info("***查詢結果O(∩_∩)O哈哈~:" + payment);
        if (payment != null) {
            return new CommonResult(200, "查詢數據成功 serverPort:"+serverPort + payment);
        } else {
            return new CommonResult(444, "沒有對應記錄 serverPort"+serverPort, null);
        }
    }
}

測試:

##分別啓動Eureka7001  Eureka7002 provider8001 provider8002 consumer  訪問如下:

http://localhost:7001/
http://localhost:7002/
http://localhost/consumer/payment/get/2

結果:

說明:
訪問 http://localhost/consumer/payment/get/2時,輪詢調用8001和8002的controller
從打印信息中可以看到:
{"code":200,"message":"查詢數據成功 serverPort:8002 Payment(id=2, serial=002)","data":null}
{"code":200,"message":"查詢數據成功 serverPort:8001 Payment(id=2, serial=002)","data":null}

在這裏插入圖片描述

3、actuator微服務信息完善

POM

<!--這個配置原來已經添加了-->
<!--springboot-->
<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>

Application.yml新增

eureka:
	instance:
		instance-id: cloud-provider-payment-8001
		prefer-ip-address: true #是否顯示服務IP地址
		
#其他服務類似

4、Eureka服務發現

eureka服務發現是將註冊進eureka的服務 服務名稱 地址 端口等暴露出來的服務

以cloud-provider-payment-8001爲例:

添加一個DiscoveryController類

package com.lee.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/eureka")
public class DiscoveryController {

    @Resource
    private DiscoveryClient discoveryClient;

    @GetMapping("/discovery")
    public Object discovery(){
        List<String> services = discoveryClient.getServices();
        for(String s: services){
            log.info("==================================");
            log.info("----->service :"+s);
            List<ServiceInstance> instances = discoveryClient.getInstances(s);
            for (ServiceInstance si : instances){
                log.info("   ---->"+si.getServiceId()+"  "+si.getInstanceId()+"  "+si.getHost()+"  "+si.getPort()+"  "+si.getUri());
            }
            log.info("==================================");
        }
        return this.discoveryClient;
    }
}

啓動類添加註解

package com.lee.springcloud;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

//服務發現
@EnableDiscoveryClient
//表示自己是Eureka的客戶端
@EnableEurekaClient
@SpringBootApplication
@MapperScan("com.lee.springcloud.dao")
public class PaymentMain8001 {

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

測試:

http://localhost:8001/eureka/discovery

結果:

瀏覽器打印:
{"services":["cloud-consumer-order","cloud-payment-service"],"order":0}


console打印:
: ==================================
: ----->service :cloud-consumer-order
:    ---->CLOUD-CONSUMER-ORDER  cloud-consumer-order-80  192.168.0.117  80  http://192.168.0.117:80
: ==================================
: ==================================
: ----->service :cloud-payment-service
:    ---->CLOUD-PAYMENT-SERVICE  cloud-provider-payment-service-8002  192.168.0.117  8002  http://192.168.0.117:8002
:    ---->CLOUD-PAYMENT-SERVICE  cloud-provider-payment-service-8001  192.168.0.117  8001  http://192.168.0.117:8001
: ==================================

5、Eureka自我保護

5.1、概述:

保護模式主要用於一組eureka client和eureka server之間存在網絡分區場景下的保護。

   一旦進入保護模式:eureka server將會嘗試保護其服務註冊表中的信息,不再刪除服務註冊表中的數據,也就是
不會註銷任何微服務。
	
	換一句話說,就是,某時刻一個微服務不可用了,eureka server不會立刻清理,依舊會對該服務信息進行保存。

	屬於CAP原理中的AP:A高可用  P分區容錯性

1、爲什麼會產生eureka自我保護機制?
	爲防止eureka client本可以正常運行,但是與eureka server網絡不通的情況下,eureka server出現立刻將
eureka client服務剔除的情況。

2、什麼事自我保護模式?
	默認情況下,如果eureka server在一定時間內沒有接收到某個微服務實例的心跳(60s),eureka server將會
註銷該實例。
	但是當網絡分區故障發生時(延遲、卡頓、擁擠),微服務與eureka server之間無法正常通信,以上行爲就可能變得非常危險了。-----因爲微服務本身是非常健康的,此時本不應該註銷這個服務。
	eureka通過"自我保護模式"解決這個問題:當eureka server在短時間內丟失過多客戶端時(可能發生了網絡分區故
障),那麼eureka server節點就會進入自我保護模式。

	寧可保留錯誤的服務註冊信息,也不盲目註銷任何可能健康的服務實例。使用自我保護模式使得eureka 集羣更加健壯
、穩定。

5.2、相關配置

###################Eureka Server#############################
#默認爲true,開啓eureka自我保護機制
eureka.server.enable-self-preservation=true
# 掃描失效服務的間隔時間(單位毫秒,默認是60*1000)即60秒
eureka.server.eviction-interval-timer-in-ms=5000
#設置 eureka server同步失敗的等待時間 默認 5分
#在這期間,它不向客戶端提供服務註冊信息
eureka.server.wait-time-in-ms-when-sync-empty=5
#設置 eureka server同步失敗的重試次數 默認爲 5 次
eureka.server.number-of-replication-retries=5
#自我保護係數(默認0.85)
eureka.server.renewal-percent-threshold=0.49

##################Eureka client###############################
#Eureka客戶端向服務端發送心跳的時間間隔,單位爲秒(默認是30秒)
eureka.instance.lease-renewal-interval-in-seconds=30
#Eureka服務端在收到最後一次心跳後等待時間上限 ,單位爲秒(默認是90秒),超時剔除服務
eureka.instance.lease-expiration-duration-in-seconds=90

三、Zookeeper

Zookeeper是一個分佈式協調工具,可以替代Eureka實現註冊中心功能。

因爲有些公司的項目是從dubbo轉變springcloud的,所以zookeeper也成爲了一種註冊中心的選擇。

這裏只說zookeeper的單機版配置。

1、Zookeeper簡單安裝

##下載
1>、wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.9/zookeeper-3.4.14.tar.gz

##解壓
2>、tar -zxvf zookeeper-3.4.9.tar.gz

##重命名
3>、mv zookeeper-3.4.9 zookeeper

##移動
4>、mv zookeeper /opt/

5>、修改配置文件

cd /opt/zookeeper/conf
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/opt/zookeeper/data/data
# the port at which the clients will connect
clientPort=2181
dataLogDir=/opt/zookeeper/data/log
server.1=localhost:2888:3888

6>、啓動
./zkServer.sh start

7、關閉
./zkServer.sh stop

8、其他
如放開2181阿里ESC的防火牆配置等不再詳說

2、創建cloud-provider-payment-8004

new maven module

moduleName   cloud-provider-payment-8004
parentProject  cloud_2020
groupId      com.lee.springcloud
artifactId    cloud_2020
packaging     jar

創建完成後 父工程POM文件會多了個標籤

錯誤的POM演示

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>cloud-provider-payment-8004</artifactId>

    <dependencies>

        <dependency>
            <groupId>com.lee.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>


        <!--SpringBoot整合Zookeeper客戶端 其實這裏zk會產生jar包衝突-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>

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


</project>

application.yml

server:
  port: 8004

spring:
  application:
    name: cloud-provider-payment # 服務別名---註冊zookeeper到註冊中心的名稱
  cloud:
    zookeeper:
      connect-string: zk服務器的IP:2181

主啓動類

package com.lee.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 該註解用於向使用consul或者zookeeper作爲註冊中心時註冊服務
 * 同時也可以用於對外服務暴露-服務發現
 */
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentZkMain8004 {

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

Controller

package com.lee.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

/**
 * 這裏不再像cloud-provider-payment-8001和8002一樣寫service等方法了
 * 直接寫一個controller方法
 */
@Slf4j
@RestController
public class PaymentController {

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

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

}

測試

啓動ZK
啓動cloud-provider-payment-8004

報jar包衝突

在這裏插入圖片描述

**解決方案:**修改POM

<!--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>

<!--添加zookeeper 3.4.9版本,同zk服務器版本一致-->
<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.4.9</version>
    <type>pom</type>
</dependency>

結果:

在這裏插入圖片描述

{
  "name": "cloud-provider-payment",
  "id": "64c897d0-85ef-42ab-854d-424adae6ebc9",
  "address": "DESKTOP-3H86HI9",
  "port": 8004,
  "sslPort": null,
  "payload": {
    "@class": "org.springframework.cloud.zookeeper.discovery.ZookeeperInstance",
    "id": "application-1",
    "name": "cloud-provider-payment",
    "metadata": {}
  },
  "registrationTimeUTC": 1585305270235,
  "serviceType": "DYNAMIC",
  "uriSpec": {
    "parts": [
      {
        "value": "scheme",
        "variable": true
      },
      {
        "value": "://",
        "variable": false
      },
      {
        "value": "address",
        "variable": true
      },
      {
        "value": ":",
        "variable": false
      },
      {
        "value": "port",
        "variable": true
      }
    ]
  }
}

3、創建cloud-consumer-order-81

new maven module

moduleName   cloud-consumer-order-81
parentProject  cloud_2020
groupId      com.lee.springcloud
artifactId    cloud_2020
packaging     jar

創建完成後 父工程POM文件會多了個標籤

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order-81</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.lee.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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.9版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</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>

</project>

application.yml

server:
  port: 81
spring:
  application:
    # 服務別名
    name: cloud-consumer-order
  cloud:
    zookeeper:
      # 註冊到zookeeper地址
      connect-string: zk服務器IP:2181

主啓動類

package com.lee.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 該註解用於向使用consul或者zookeeper作爲註冊中心時註冊服務
 * 同時也可以用於對外服務暴露-服務發現
 */
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerZkMain81 {

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

config

package com.lee.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

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

}

controller

package com.lee.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@RestController
public class OrderController {

    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Resource
    private RestTemplate restTemplate;

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


}

測試

啓動zk、cloud-provider-payment-8004、cloud-consumer-order-81
瀏覽:
http://localhost:81/consumer/payment/zk
結果:
SpringCloud with zookeeper:8004 05acbb42-6e89-4803-bc99-6f8117bc1ecc

知識點:

各微服務註冊進zookeeper中的節點是:臨時節點。

四、Consul

1、Consul概述

consul是一套開源的分佈式服務發現和配置管理系統。
consul提供了微服務系統中的服務治理、配置中心、控制總線等功能。這些功能中的每一個都可以根據需要單獨使用,也可以一起使用構建全方位的服務網絡。

官網:https://www.consul.io/intro/index.html

下載地址:https://www.consul.io/downloads.html

如何跟springcloud一起使用:https://www.springcloud.cc/spring-cloud-consul.html

2、安裝並運行consul

由於國內主流都使用springcloud alibaba的Nacos作爲註冊中心,所以這裏就簡單的拿windows版做下運用。

下載完成後,只有一個consul.exe

執行consul agent -dev,以開發者模式運行。

訪問http://localhost:8500

在這裏插入圖片描述

3、創建cloud-provider-payment-8006

new maven module

moduleName   cloud-provider-payment-8006
parentProject  cloud_2020
groupId      com.lee.springcloud
artifactId    cloud_2020
packaging     jar

創建完成後 父工程POM文件會多了個標籤

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-payment-8006</artifactId>

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

        
        <dependency>
            <groupId>com.lee.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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>

</project>

application.yml

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

主啓動類

package com.lee.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 該註解用於向使用consul或者zookeeper作爲註冊中心時註冊服務
 * 同時也可以用於對外服務暴露-服務發現
 */
@EnableDiscoveryClient
@SpringBootApplication
public class PaymentConsulMain8006 {

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

Controller

package com.lee.springcloud.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

/**
 * 這裏不再像cloud-provider-payment-8001和8002一樣寫service等方法了
 * 直接寫一個controller方法
 */
@RestController
public class PaymentController {

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

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

}

測試:

啓動consul: 

訪問:http://localhost:8500/  查看節點情況

4、創建cloud-consumer-order-82

new maven module

moduleName   cloud-consumer-order-82
parentProject  cloud_2020
groupId      com.lee.springcloud
artifactId    cloud_2020
packaging     jar

創建完成後 父工程POM文件會多了個標籤

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud_2020</artifactId>
        <groupId>com.lee.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-order-82</artifactId>

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

        
        <dependency>
            <groupId>com.lee.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <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>

</project>

Application.yml

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

主啓動類

package com.lee.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * 該註解用於向使用consul或者zookeeper作爲註冊中心時註冊服務
 * 同時也可以用於對外服務暴露-服務發現
 */
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerConsulMain82 {

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

config

package com.lee.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ApplicationContextConfig {

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

}

Controller

package com.lee.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@Slf4j
@RestController
public class OrderController {

    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Resource
    private RestTemplate restTemplate;

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


}

測試:

啓動consul、cloud-provider-payment-8006、cloud-consumer-order-82 

訪問:http://localhost:82/consumer/payment/consul

五、三個註冊中心對比

1、先複習下CAP原則

CAP原則又稱CAP定理,指的是在一個分佈式系統中中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分區容錯性),三者不可得兼。 CAP原則的精髓就是要麼AP,要麼CP,要麼AC,但是不存在CAP。

C Consistency 一致性
A Availability 可用性
P Partition tolerance 分區容錯性

2、三者區別

組件名 語言 CAP SpringCloud集成
Eureka Java AP 已集成
Zookeeper Java CP 已集成
Consul Go CP 已集成

AP即是:如果兩個服務器沒有完成數據同步,仍然能夠對外提供服務。

CP是:如果兩個服務器沒有完成數據同步,則不再進行對外提供服務,知道數據同步完成。

========================================================
SpringCloud Alibaba Nacos作爲服務註冊中心的部分,將在後面的文章中寫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章