Eureka集群部署以及踩坑记录

关于本机Eureka集群部署

若想在单机搭建虚拟集群的话,必须设置域名IP映射,如果不设置的话,直接使用如127.0.0.1这样的本机IP地址的话。Eureka会排出相同的IP地址,即使每个Eureka服务端口号不同也不行。

以下文章只是解决方案之一,我自己学习过程中并没有使用下面的解决方案,集群部署过程不再赘述,只需新建Eureka服务项目,将需要集群的Eureka服务的地址添加即可,用“,”分割。Provider则同时向集群的所有Eureka服务注册,用“,”分割

一、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://localhost: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找到对方

原文链接