一、SpringCloud注册中心

CY 2019年03月16日 8次浏览

SpringCloud很多的组件都已经停止更新或者进入维护阶段了,所以是时候应该学习一下这些组件的替代方案了。

今天从第一个开始吧,学习一下服务注册中心Eureka的替代方案。

Spring Cloud版本

Spring CloudSpring Boot的版本对应情况:https://spring.io/projects/spring-cloud#overview

更具体的对应情况说明:https://start.spring.io/actuator/info (JSON数据)

Eureka的准备工作

Eureka是一个Java编写的注册中心,设计原则是AP,这就意味着舍弃了数据一致性,所以Eureka适用于需要高度响应的分布式环境,并不适用于数据严格一致的分布式环境。

关于CAP的一篇文章,阮老师写的:点击查看

在开始Eureka之前,先做一个RestTemplate的准备工作,首先建立一个Provider项目,就是一个普普通通的SpringMVC项目,暴露一个接口就可以了,例如下面的样子:

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

@GetMapping("payInfo")
public String payInfo() {
    return "返回了一个信息!!!" + serverPort;
}

紧接着用另外一个项目(Consumer)去访问这个接口,例如下面的样子:

@Resource
private RestTemplate restTemplate;

/**
 * 这里要指向提供者接口
 */
private static final String BASE_URL = "http://localhost:8001";

@GetMapping("pay")
public String payInfo() {
    return restTemplate.getForObject(BASE_URL + "/payInfo", String.class);
}

RestTemplate的配置如下:

@Bean
public RestTemplate restTemplate() {
    return new RestTemplate();
}

然后访问Consumer,就可以通过Consumer来访问到Provider了,这样的缺点就是Provider的地址在代码中写死了,使用注册中心可以解决这个问题。

安装Eureka服务端

Eureka不需要专门去安装一个软件,直接在项目中创建一个项目,导入Eureka的包就可以使用Eureka

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

这个包就是Eureka的服务端,导入包之后,需要对Eureka进行一些配置:

server:
  port: 7001
eureka:
  instance:
    hostname: localhost
  client:
    # 不把自己注册到Eureka中
    register-with-eureka: false
    # 不去注册中心检索服务
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

上面两个false的作用就是用来说明,当前是服务端,不需要将自己作为服务注册到注册中心,也不需要去检索注册中心里面拥有的服务。

紧接着写一个启动类,就可以完美的启动Eureka的服务端了。

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {

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

@EnableEurekaServer的作用就是启用Eureka的服务端,启动这个main方法就可以启动Eureka服务了。

启动之后可以直接访问http://localhost:7001就可以看到Eureka的服务界面了,他是一个Web-UI

Eureka启动成功

可以看到当前是没有实例可用的,也就是没有服务注册到Eureka上面,接下来就让之前编写的Provider注册上来。

关于这个页面的具体的一些细节,以及Eureka的一些扩充内容,后面再记录。

注册Provider到Eureka

和创建服务端基本类似,只是换了换pom和配置,启动类也发生了一点变化。

导入Eureka的客户端

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

接下来编写application.yaml文件

spring:
  application:
    name: cloud-provider-paymanet
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka

上面的spring.application.name很重要,因为这个就是注册到注册中心的名称。

编写启动类:

@SpringBootApplication
@EnableEurekaClient
public class PaymentApplication {

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

上面使用了@EnableEurekaClient注解标明当前是Eureka的客户端。

然后启动后,就可以在Eureka的服务端上面看到这个实例了。

如何让Consumer去注册中心寻找Provider

注册Consumer到Eureka

Provider同样的步骤,唯一不一样的是RestTemplateBASE_URL发生了改变,不再用手动规定了,可以直接修改为:

private static final String BASE_URL = "http://CLOUD-PROVIDER-PAYMANET";

这个URL中的CLOUD-PROVIDER-PAYMANET就是Provider配置文件中规定的(注意是大写):

spring:
  application:
    name: cloud-provider-paymanet

只改了一下BASE_URL是不足以让Consumer找到Provider的,还需要让RestTemplate去自动寻找这个服务。所以之前的RestTemplate配置需要发生一些改变了。

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

和之前不一样的只是多写了一个注解@LoadBalanced,这个注解就可以做到让RestTemplate去注册中心寻找地址。

到目前为止,就基本在项目中用到了Eureka

但是Eureka只有一个,当然是不够的。

Eureka集群搭建

搭建集群很简单,但是因为是开发环境,所以我们需要再次新建一个Eureka服务端,用这两个Eureka服务端来一起配合做集群。

只需要在两个配置文件中互相注册一下,就可以了,三个Eureka或者更多的Eureka,也是一样的道理。

首先需要配置一下hosts文件,让两个Eureka都拥有自己的hostname,毕竟是一台机器上模拟集群,所以只好这样做了。

127.0.0.1 eureka1
127.0.0.1 eureka2

修改配置文件:

假设这两个Eureka的端口一个是7001,一个是7002,那么这两个的配置文件是下面的样子:

7001

server:
  port: 7001
eureka:
  instance:
    hostname: eureka1
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://eureka2:7002/eureka/

7002

server:
  port: 7002
eureka:
  instance:
    hostname: eureka2
  client:
    # 不把自己注册到Eureka中
    register-with-eureka: false
    # 不去注册中心检索服务
    fetch-registry: false
    service-url:
      defaultZone: http://eureka1:7001/eureka/

可以看到70017002互相注册了一下。怎么查看集群成功了呢?

同时启动70017002,查看Eureka的界面,看到下面的内容,就是成功了。

Eureka集群配置成功

因为有了集群,所以之前的ProviderConsumer也就不能简简单单的注册到一个Eureka了,它们可以同时注册进两个Eureka服务。

eureka:
  client:
    service-url:
      defaultZone: http://eureka1:7001/eureka,http://eureka2:7002/eureka

可以看到两个地址中间用了,,这样就可以同时将服务注册到两个Eureka了。

Eureka集群搭建完了,注册中心有了集群,但是Provider并没有集群,照样是不安全的。

所以给Provider也要配置集群。

配置Provider的集群

这就很简单了,只需要拥有多个Provider就可以了,将多个Provider同时注册到Eureka,不就达到了集群的效果吗?所以需要拷贝一下Provider,让Provider拥有两个就可以了。

最后启动两个Provider,可以看到Eureka的界面上显示是下面这样,就意味着配置集群成功了。

Provider集群配置成功

Eureka的其他说明

上面简单的介绍了Eureka的使用,Eureka的其他信息都在这里进行补充。

Eureka中Web-UI显示信息完善

EurekaWeb-UI界面中的服务默认是主机名:服务名:端口,而且鼠标放在服务上面显示的是localhost,并不是服务对应的IP地址,所以可以通过配置来改变。

instance:
  instance-id: provider01
  prefer-ip-address: true

instance-idEureka服务显示的名字,prefer-ip-address是显示IP地址。

然后重新启动后就可以看到效果:

Actuator微服务信息完善

DiscoveryClient对象的使用

如果要在应用内部得知注册中心有哪些服务,以及服务的详情信息,就可以直接在类中注入一个对象即可:

@Resource
private DiscoveryClient discoveryClient;

这个对象可以获取到服务的名字和实例的名字:

@GetMapping("discovery")
public DiscoveryClient discovery() {
    List<String> services = discoveryClient.getServices();
    for (String service : services) {
        System.out.println("|- 服务的名字:" + service);
        List<ServiceInstance> instances = discoveryClient.getInstances(service);
        for (ServiceInstance instance : instances) {
            System.out.println("\t|- 实例的信息:" + instance.getHost() + ":" + instance.getPort());
        }
    }
    return discoveryClient;
}

控制台打印:

|- 服务的名字:cloud-consumer-order
	|- 实例的信息:localhost:80
|- 服务的名字:cloud-provider-paymanet
	|- 实例的信息:192.168.207.1:8001
	|- 实例的信息:192.168.207.1:8002

@EnableDiscoveryClient注解的使用

之前使用的@EnableEurekaClient可以使用@EnableDiscoveryClient注解来代替,因为后面将要介绍到的几种注册中心都使用的是@EnableDiscoveryClient

Eureka的自我保护

Eureka默认是开启了自我保护机制的,30秒钟发送一次心跳,如果90秒没有发送心跳才会将服务剔除,可以关闭这个自我保护,让Eureka一旦发现服务停止发送心跳,就立刻将服务剔除:

eureka:
  server: 
    # 关闭自我保护
    enable-self-preservation: false
    # 修改检查服务失效的时间
    eviction-interval-timer-in-ms: 3000

微服务可以修改默认的心跳时间:

eureka:
  instance:
    # 默认90秒
    lease-expiration-duration-in-seconds: 10
    # 默认30秒
    lease-renewal-interval-in-seconds: 3

Eureka2.0停更

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

Wiki上面说:

The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.

Eureka 1.x is a core part of Netflix's service discovery system and is still an active project.

既然这样的话,就应该寻找一下Eureka的替代方案了。

替代方案1:Zookeeper

Zookeeper之前也有过一篇文章进行记录,Zookeeper的基本概念和安装就不介绍了,直接使用SpringCloud和它进行整合。

Zookeeper就不用自己去写服务端了,安装到Linux之后启动,然后关闭防火墙,只需要在ProviderConsumer里面将注册中心换成Zookeeper就可以了。

改写Provider

需要导入一个pom坐标:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>

配置文件:

spring:
  application:
    name: cloud-provider-paymanet
  cloud:
    zookeeper:
      # 这里不要添加http://,因为它并不是http协议
      connect-string: 192.168.207.128:2181

紧接着在启动类上面添加上@EnableDiscoveryClient注解,然后启动一下微服务,就可以发现服务被注册到了Zookeeper上。

微服务成功注册到Zookeeper

或者说使用get来获取注册的微服务信息:

get获取信息

将微服务关闭以后过一段时间,发现zookeeper中注册的服务也消失掉了,这就足以证明,SpringCloud使用ZookeeperCP原则,并不是AP原则,微服务在Zookeeper中保存的Znode是一个临时节点。

改写Consumer

和改写Provider一个道理。只是RestTemplateBASE_URL发生了变化:

private static final String BASE_URL = "http://cloud-provider-paymanet";

Zookeeper替代方案就到这里了,对于Zookeeper的集群可以参照其他文章,对于Provider的集群,和上面介绍的一个道理,这里就不再重复了。

替代方案2:Consul

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

下载完成后解压就是一个exe文件,使用命令就可以操作了

# 查看Consul的版本
./consul.exe --version
# 启动Consul
./consul.exe agent -dev

启动后可以查看界面:http://localhost:8500/ui/dc1/services

然后和替换成Zookeeper一样,替换成Consul也需要导入pom坐标

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

加入配置:

spring:
  application:
    name: cloud-provider-paymanet
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        service-name: ${spring.application.name}

其他都和配置Zookeeper一样。

如果说遇到下面的错误,只需要加入一个Maven坐标就可以了。

Consul遇到错误

加入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Eureka是AP原则,Zookeeper和Consul是CP原则

替代方案3:Nacos

可以查看该系列的第八篇文章:SpringCloud Alibaba Nacos