Eureka快速入门
本节主要介绍什么是服务治理,如何做到服务治理。在具体的服务治理中,包括服务注册中心的搭建,服务提供者模块的搭建,以及如何搭建高可用的服务端。
服务治理
在微服务中,服务治理是最基础的模块,用于完成自动化注册与发现。那么为什么要服务注册与发现呢?什么是服务的注册与发现?
为什么要服务注册与发现
在最开始时,我们的微服务系统中服务不是太多,手动静态地配置,手动维护即可,我们也不会涉及服务注册与发现,如图10.1所示。

在图10.1中,我们不需要进行服务注册与发现,也不会涉及负载均衡,直接调用即可。如果系统再大一些,我们可以多花一些时间维护;如果系统想让服务高可用,只要继续维护这个服务中的几个节点就行。但是如果系统再大一些,发展到图10.2所示的情况时,我们的集群规模、服务命名、服务位置的情况也会变得复杂,可能随时在进行变化。手动维护时,花费更多时间的同时,也会出现更多的错误,使得微服务的开发变得困难。

在上面的图中可发现,我们的后台服务与前台服务在部署的时候,服务的维护就很复杂。这个时候,我们就希望有一个模块可以自动注册存在的微服务,而不是再手动配置。在调用的时候,我们不再直接调用,而是到这个模块中查找被注册的服务的位置,然后直接调用,这个就是服务治理。
在图10.2中有一个服务网关它会在后面被介绍,在这里重要的是理解上图的服务治理的概念与思路。
服务注册与发现
一般的服务注册,首先需要存在一个服务注册中心,然后,每个服务向这个服务注册中心进行注册。在服务注册时,一般需要保存主机IP、端口号等。下面举一个实例,具体 Eureka 的注册信息会在后面说明,这里只是说明普通的服务注册概念,实例如表10.1所示。

在注册完成之后,服务注册中心需要以心跳的方式检测服务的状态,如果服务变得不可用,则需及时进行清除,保持服务列表的可用性。
服务注册之后,我们就可开始在服务治理框架中进行操作了,不会再直接调用指定的服务实例。这时请求是通过服务名实现,没有服务的地址,所以首先向服务注册中心发送一个请求,服务注册中心会将这个服务对应的位置以清单的方式返回,这样我们就可以知道这个服务的具体位置。当然后续还有别的操作。一般来说,返回的清单中的位置是多个的,此时会采用一定的负载均衡策略获取其中的一个。
Eureka的服务治理
Eureka 是一个服务治理的模块,对于它的原理我们先从大的框架上看,主要有两个组件,服务端和客户端。服务端也称为服务注册中心,客户端则包含服务提供者与服务消费者,这样就分为三个部分,服务注册中心、服务提供者、服务消费者。图10.3是 Eureka 框架图。

对上图整个流程进行一些说明:服务提供者启动之后,根据配置文件指定的服务注册中心位置,进行服务注册。服务消费者启动之后,根据配置文件指定的服务注册中心,去服务注册中心订阅所需要的服务。服务消费者获取从服务注册中心返回的地址清单,并调用服务提供者提供的服务。
当这三个部分完全运行起来时,Eureka 的客户端则根据配置文件或者默认值不断地发送心跳给服务注册中心来续约服务,如果超过了一个时间值没有接收到心跳,服务注册中心会将服务剔除。当然,所有的客户端都会从服务注册中心查询服务缓存到本地,并周期地刷新状态。
Eureka 服务端,主要充当服务注册中心。它不仅可以单机部署,还可以高可用部署。在高可用部署的时候,面对故障场景,如在集群中有分片出现问题,它会进入自我保护模式,继续提供服务治理功能,当分片恢复后,其他的分片会同步服务状态。在后面,我们会搭建单机服务端,也会搭建集群服务端,并和大家熟悉的 zookeeper 注册中心做一个简单的对比。
Eureka 客户端,实现服务注册与服务发现。关于客户端,主要是通过注解与参数配置,将客户端服务嵌入应用程序。程序运行起来,就开始分为服务提供者与服务消费者,而且客户端会发送心跳,与服务端进行维护服务。
Eureka的服务注册中心搭建
在这里,先搭建一个服务注册中心。因为使用 IDEA 进行搭建更加方便,所以在下面的演示中,继续使用 IDEA 进行搭建。在新建项目时,选择 Spring Initializr,如图10.4所示。新建一个 eureka 项目,如图10.5所示。


在这里选择依赖,服务注册中心使用的是 Eureka Server,如图10.6所示。

现在已经完成了新建一个服务注册中心的项目,不过还有一些细节需要处理。下面是新建项目的 pom 文件,我们做一些分析。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<groupId>com.cloudTest</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.RC2</spring-cloud.version>
</properties>
<dependencies>
<!--在这里省略了AOP与Web的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
首先,因为篇幅过长这里省略了 AOP 与 Web 的依赖,同时我们可以看到,其实 parent 引用的是 Spring Boot,也就是说明在学习 Spring Cloud 之前要熟悉 Spring Boot。在搭建服务注册中心时,需要引用 spring-cloud-starter-eureka-server 的依赖。最后是对 Spring Cloud 的依赖管理,使用的版本是 Greenwich.RC2。需要说明,在搭建时 Spring Boot 使用的版本是 2.1.1。
然后,修改启动类程序,添加 @EnableEurekaServer 注解,代码如下所示。
package com.cloudtest.eureka;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
//启动类
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
在上面的代码中,使用注解 @EnableEurekaServer,用于启动服务注册中心,以提供给其他应用一个对话。只要在 Spring Boot 应用上添加,声明这是一个 Eureka Server,这时,就可以进行启动了。在启动之后,访问 http://localhost:8080/ ,可以看到一个界面,如图10.7所示(地址栏默认省略 “http://”)。
但是,我们发现后台的控制台不断地出现报错信息,这是因为 Eureka 模块中服务端包含了客户端,具体原因可以看看下面这段代码,即注解 @EnableEureka 的代码清单。
package org.springframework.cloud.netflix.eureka.server;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}

默认服务注册中心也可以作为客户端,使得服务注册中心也可以把自己注册。因此,我们需要禁止自己注册自己的这种行为,这里可以用配置文件 application.properties 增加配置。在图10.7中,我们还标注了一下默认的服务注册中心地址,在配置文件中,可以对其进行修改。最后,设置应用的端口与注册地址端口相同。
server.port=8764
eureka.client.service-url.defaultZone=http://localhost:8764/eureka/
eureka.client.register-with-eureka=false
Eureka的服务提供者
在10.1.3节的服务注册中心搭建中,在将服务注册中心自身不作为服务之后,对服务注册中心来说它还是一个空的中心。根据10.1.2节的理论说明,我们需要将服务注册到服务注册中心。因此下一步我们开始搭建一个服务提供者。
搭建服务的提供者,可以使用 Spring Boot 的项目进行改造,主要是添加依赖文件与注解。考虑到本书后面会继续使用,也方便单独章节读者可以阅读,这里将重新进行搭建,而且搭建的是 Eureka 的客户端。首先,新建项目,如图10.8所示。

在添加依赖时,选择 Eureka Discovery,如图10.9所示。

现在,在 pom 文件中可以发现在客户端新增了如下依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
然后,在启动类上添加注解 @EnableEurekaClient。
package com.cloudtest.eurekaprovider;
@SpringBootApplication
@EnableEurekaClient
public class EurekaproviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaproviderApplication.class, args);
}
}
在上面的代码中,使用注解 @EnableEurekaClient,激活 Eureka 的客户端。然后,我们写一个应用,并把这个应用注册上去。按照惯例,这里写一个 “/hello” 的访问请求,代码如下所示。
package com.cloudtest.eurekaprovider.controller;
@RestController
public class HelloController {
@Autowired
private Discovery Client client;
@Autowired
private Registration registration; // 服务注册
private final Logger logger =LoggerFactory.getLogger(HelloController.class);
@GetMapping("/hello")
public String hello(){
ServiceInstance instance = serviceInstance();
String result = "host:port=" + instance.getUri() + ", "
+ "service_id:" + instance.getServiceId();
logger.info(result);
return "hello eureka!";
}
public ServiceInstance serviceInstance() {
List<ServiceInstance> list =client.getInstances(registration.getServiceId());
if (list != null && list.size() > 0) {
for(ServiceInstance itm : list){
if(itm.getPort()==8090){
return itm;
}
}
}
return null;
}
}
在上面的代码中,使用客户端特有的 DiscoveryClient 类访问 “/hello” 时,能够将信息输出。最后,修改 application.properties,让应用可以注册到服务注册中心。下面是配置项。
server.port=8090
spring.application.name=helloService
eureka.client.service-url.defaultZone=http://localhost:8764/eureka/
在配置项中,defaultZone 配置服务注册中心的地址。为了方便管理,我们还需要配置一个信息,就是应用的 name,否则在服务注册中心中显示的是 UNKNOWN。
进入服务注册中心页面,我们可以发现新建的应用已经被注册成功,如图10.10所示。

在应用写完后,验证服务的结果,输入网址 localhost:8090/hello,在控制台上可以看到下面的信息。
2019-01-06 10:37:28.948 INFO 22256 --- [nio-8090-exec-1]
c.c.e.controller.HelloController :
host:port=http://DESKTOP-TQ4OD79:8080, service_id:HELLOSERVICE
在日志上,我们发现这里输出的信息与服务注册中心面板上的信息相同。
Eureka Server的高可用
在上面的实例中,我们使用的 Eureka Server 是单节点的,但在实际使用中肯定会配置成高可用。在配置高可用前,我们通过框架图说明它的原理。首先,我们看下面这种框架,它就是配置为高可用,只对某一个 Server 进行注册。高可用框架图如图10.11所示。
通过实验,将 Client 注册到左边一台 Server 上,在右边的服务注册中心上依旧可以查询到 Client。所以如果左边的 Server 宕机,依旧可以执行查询服务。但是这种情况是有缺点的,就是左边的 Server 宕机之后,不能够再注册 Client。
我们再看另一种框架,它可以避免这种缺陷,框架如图10.12所示。


图10.12是我们即将要搭建的高可用 Eureka 框架图。在图上可以看到,有两个 Eureka Server,它们互相注册,保持了数据的同步。然后 Client 分别在 Server 上进行注册。
我们使用前面搭建过的 Eureka 项目,修改应用名称为 EurekaApplication1,配置文件 application.properties 的配置项如下。
server.port=8764
eureka.client.service-url.defaultZone=http://localhost:8765/eureka/
eureka.client.register-with-eureka=false
在上面的配置文件中,我们知道把应用往 8765 服务注册中心进行注册。启动应用,然后我们使用 IDEA 将第一个应用复制一份,应用名称改为 EurekaApplication2,如图10.13所示。

修改它的配置文件,配置文件如下。
server.port=8765
eureka.client.service-url.defaultZone=http://localhost:8764/eureka/
eureka.client.register-with-eureka=false
在上面的配置文件中可以看到,将应用 EurekaApplication2 注册到 8764 服务注册中心上。
然后启动。最后我们对 Client 进行注册,修改客户端应用 eurekaprovider 的配置文件,配置文件如下。
server.port=8090
spring.application.name=helloService
eureka.client.service-url.defaultZone=http://localhost:8764/eureka/,http://localhost:8765/eureka/
在上面的这段配置文件中,Client 的注册地址分别是 8765 与 8764。我们访问 http://localhost:8764/ 与 http://localhost:8765/ 将会发现,Client 被注册成功。