1. 程式人生 > >Eureka叢集部署以及踩坑記錄(例項始終unavailable)

Eureka叢集部署以及踩坑記錄(例項始終unavailable)

一、Eureka叢集部署

建議先嚴格按照步驟來部署,不然容易出問題,可能出現的問題會在下邊說明

1、新建一個maven工程,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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.iceberg.eurekatest</groupId>
    <artifactId>eureka-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

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

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2、在Application類上加上註解,開啟eureka

@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {

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

3、新增兩個配置檔案 application-peer1.yaml

spring:
  application:
    name: eureka-server

server:
  port: 8001

eureka:
  client:
    #是否將自己註冊到Eureka Server
    register-with-eureka: true
    #是否從Eureka Server獲取註冊資訊
    fetch-registry: true
    serviceUrl:
      defaultZone: "http://peer2:8002/eureka/"
  instance:
    prefer-ip-address: false
    hostname: "peer1"

application-peer2.yaml

spring:
  application:
    name: eureka-server

server:
  port: 8002

eureka:
  client:
    #是否將自己註冊到Eureka Server
    register-with-eureka: true
    #是否從Eureka Server獲取註冊資訊
    fetch-registry: true
    serviceUrl:
      defaultZone: "http://peer1:8001/eureka/"
  instance:
    prefer-ip-address: false
    hostname: "peer2"

4、在host中新增兩條對映
127.0.0.1 peer1
127.0.0.1 peer2

5、在SpringBoot的啟動引數上加上-Dspring.profiles.active=peer1,然後啟動專案。

然後改成peer2,啟動專案(共啟動兩個)

6、注意事項
(1)在啟動第一個eureka的時候,會報下列異常

2019-07-29 15:49:57.335  WARN 15416 --- [nfoReplicator-0] c.n.discovery.InstanceInfoReplicator     : There was a problem with the instance info replicator

com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
	at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:112) ~[eureka-client-1.9.12.jar:1.9.12]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.9.12.jar:1.9.12]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) ~[eureka-client-1.9.12.jar:1.9.12]
	at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77) ~[eureka-client-1.9.12.jar:1.9.12]
	at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.9.12.jar:1.9.12]
	at com.netflix.discovery.DiscoveryClient.register(DiscoveryClient.java:847) ~[eureka-client-1.9.12.jar:1.9.12]
	at com.netflix.discovery.InstanceInfoReplicator.run(InstanceInfoReplicator.java:121) ~[eureka-client-1.9.12.jar:1.9.12]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_212]
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_212]
	at java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_212]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_212]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_212]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_212]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_212]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_212]

這個異常的意思是配置的eureka-server未找到(因為你還沒啟動),當第二個eureka-server啟動好之後就正常了
(2)在eureka單機版配置中,register-with-eureka和fetch-registry 這兩個選項是false,但是叢集版的eureka是利用服務發現來實現的,所以需要改成true來向服務端註冊自己並獲取客戶端資訊
7、啟動完成後,瀏覽器訪問localhost:8001和localhost:8002
看到下圖表示成功

二、叢集部署時可能遇到的坑

1、不想加host,直接使用localhost
有些朋友可能不知道怎麼改host或者乾脆就是懶,直接用localhost取代之前的peer1和peer2,配置檔案如下
application-peer1.yaml

spring:
  application:
    name: eureka-server

server:
  port: 8001

eureka:
  client:
    #是否將自己註冊到Eureka Server
    register-with-eureka: true
    #是否從Eureka Server獲取註冊資訊
    fetch-registry: true
    serviceUrl:
      defaultZone: "http://localhost:8002/eureka/"
  instance:
    prefer-ip-address: false
    hostname: "localhost"

application-peer2.yaml

spring:
  application:
    name: eureka-server

server:
  port: 8002

eureka:
  client:
    #是否將自己註冊到Eureka Server
    register-with-eureka: true
    #是否從Eureka Server獲取註冊資訊
    fetch-registry: true
    serviceUrl:
      defaultZone: "http://peer1:8001/eureka/"
  instance:
    prefer-ip-address: false
    hostname: "localhost"

這裡分成兩種情況:
(1)單機部署兩個eureka,然後訪問http://localhost:8001/
可以看到registered-replicas那一欄為空了,為什麼會這樣子?
我們來看下eureka的原始碼中PeerEurekaNodes的resolvePeerUrls()方法,這個方法的作用是從配置的serviceUrl中獲取可用的地址

protected List<String> resolvePeerUrls() {
    InstanceInfo myInfo = applicationInfoManager.getInfo();
    String zone = InstanceInfo.getZone(clientConfig.getAvailabilityZones(clientConfig.getRegion()), myInfo);
    List<String> replicaUrls = EndpointUtils
            .getDiscoveryServiceUrls(clientConfig, zone, new EndpointUtils.InstanceInfoBasedUrlRandomizer(myInfo));

    int idx = 0;
    while (idx < replicaUrls.size()) {
	//這個地方就是罪魁禍首
        if (isThisMyUrl(replicaUrls.get(idx))) {
            replicaUrls.remove(idx);
        } else {
            idx++;
        }
    }
    return replicaUrls;
}

public boolean isThisMyUrl(String url) {
    final String myUrlConfigured = serverConfig.getMyUrl();
    if (myUrlConfigured != null) {
        return myUrlConfigured.equals(url);
    }
    return isInstanceURL(url, applicationInfoManager.getInfo());
}

//判斷url的hostnam和當前的hostname是否一致,如果一致則直接忽略
public boolean isInstanceURL(String url, InstanceInfo instance) {
    String hostName = hostFromUrl(url);
    String myInfoComparator = instance.getHostName();
    if (clientConfig.getTransportConfig().applicationsResolverUseIp()) {
        myInfoComparator = instance.getIPAddr();
    }
    return hostName != null && hostName.equals(myInfoComparator);
}

通過isInstanceURL()方法,eureka會把hostname相同的url移除掉,而恰好我們配置的都是localhost,所以雖然你啟動了兩個eureka,但是它們不會把自己當成叢集
(2)多機(或者多個虛擬機器)部署
如果你把兩個eureka啟動在了不同的主機上,並配置了localhost,那你會一直看到com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server這個錯誤,因為它無法通過localhost找到另一個eureka

2、不使用域名,而是使用IP註冊
eureka提供了一個選項,可以使eureka註冊的地址不使用域名而是IP,配置項是eureka.instance.prefer-ip-address=true
(1)單機多網絡卡部署
這裡解釋一下什麼是多網絡卡,我們安裝vmware之後,它會給我們建立一些虛擬網絡卡,比如我這邊它就給我建立了兩個虛擬地址

多網絡卡的意思是你使用ipconfig命令,能看到多個IP地址(只是我自己的定義,不代表學術定義)
這種情況下eureka註冊的IP跟你實際的IP可能是不同的,比如我剛才啟動的eureka它的instance info顯示的IP就是 192.168.157.1,這種情況下他註冊的IP也是192.168.157.1,為了讓他註冊實際的IP,我們需要通過 eureka.instance.ip-address=10.60.44.136指定一下
然後問題就來了,假如你的peer1填的ip-address是10.60.44.136,peer2中的serviceUrl.defaultZone中的url也得是10.60.44.136,那peer2的ip-address用啥呢?也用10.60.44.136的話就會出現之前的問題,被eureka自己排除掉,所以你需要換一個IP且仍然表示本地的,比如127.0.0.1,配置檔案如下所示: application-peer1.yaml

spring:
  application:
    name: eureka-server

server:
  port: 8001

eureka:
  client:
    #是否將自己註冊到Eureka Server
    register-with-eureka: true
    #是否從Eureka Server獲取註冊資訊
    fetch-registry: true
    serviceUrl:
      defaultZone: "http://127.0.0.1:8002/eureka/"
  instance:
    prefer-ip-address: true
    hostname: "localhost"
    ip-address: "10.60.44.136"

application-peer2.yaml

spring:
  application:
    name: eureka-server

server:
  port: 8002

eureka:
  client:
    #是否將自己註冊到Eureka Server
    register-with-eureka: true
    #是否從Eureka Server獲取註冊資訊
    fetch-registry: true
    serviceUrl:
      defaultZone: "http://10.60.44.136:8001/eureka/"
  instance:
    prefer-ip-address: true
    hostname: "localhost"
    ip-address: "127.0.0.1"

執行後的結果如下圖

(2)單機單網絡卡部署
剛才是多網絡卡表示ipconfig有多個IP,單網絡卡就是隻有一個IP啦,根據之前的描述,只有一個IP的情況下,你單機是無法部署叢集成功的(會被忽略掉),所以就略過啦~

(3)多機部署
這個也沒啥好說的了,ip指定為能互相訪問的ip,只要正確配置就OK啦

三、總結

1、單機偽叢集部署
方法一:每個eureka例項使用不同的域名對映到同一個IP
方法二:每個eureka例項使用不同的IP,但是這些IP要都表示本地

2、多機部署 同樣可以使用域名或者IP,但是不要使用localhost,要保證例項之間可以通過域名或IP找到對方

以上就是我在部署eureka叢集的一些總結,