一、Eureka 介绍
注册中心的作用一句话概括就是存放和调度服务,实现服务和注册中心,服务和服务之间的相互通信。注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。
Eureka 是 Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如 Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。
Eureka 由两个组件组成:Eureka 服务器和 Eureka 客户端。
- Eureka 服务器用作服务注册服务器。
- Eureka 客户端是一个 java 客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix 在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。
Eureka 的基本架构:
从上图可以看出 Eureka 由三个角色组成:
1、Eureka Server
Eureka Server 作为一个独立的部署单元,以 REST API 的形式为服务实例提供了注册、管理和查询等操作。同时,Eureka Server 也为我们提供了可视化的监控页面,可以直观地看到各个 Eureka Server 当前的运行状态和所有已注册服务的情况。
2、Service Provider
服务提供方
将自身服务注册到 Eureka,从而使服务消费方能够找到
3、Service Consumer
服务消费方
从 Eureka 获取注册服务列表,从而能够消费服务
二、创建一个EurekaServer 项目
选择依赖
继续下一步即可。
2.1 程序实现
1、在启动类上添加注解
@SpringBootApplication
@EnableEurekaServer //声明这是一个EurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
2、编写 application.yml 配置文件
server:
port: 10114
spring:
application:
# 名字随便取 应用名称,会在 Eureka 中显示
name: user-eureka
eureka:
client:
register-with-eureka: false # 是否注册自己的信息到 EurekaServer,默认是 true
fetch-registry: false # 是否拉取其它服务的信息,默认是 true
service-url:
# EurekaServer 的地址,现在是自己的地址,如果是集群,需要加上其它 Server 的地址。
defaultZone: http://127.0.0.1:${server.port}/eureka
3、测试
启动服务,并访问:http://127.0.0.1:10114
三、注册 user-service 到 Eureka
要将服务注册到 Eureka,那么需要在服务上添加 Eureka 客户端的依赖。这样 Eureka 的
客户端会自动将服务注册到注册中心中。
1、添加 SpringCloud 的依赖
<!-- Eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- SpringCloud 的依赖 -->
<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>
2、在启动类中开启 Eureka 客户端(添加注解)
@SpringBootApplication
@EnableDiscoveryClient //开启 Eureka 客户端
@MapperScan("com.zsn.cloud.dao")
public class CloudServiceApplication {
public static void main(String[] args) {
SpringApplication.run(CloudServiceApplication.class, args);
}
}
3、修改 application.yml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 10
application:
name: user-service # 应用名称
mybatis:
type-aliases-package: com.zsn.cloud.pojo
eureka:
client:
service-url: #EurekaServer 地址
defaultZone: http://127.0.0.1:10114/eureka
instance:
prefer-ip-address: true # 当调用 getHostname 获取实例的hostname 时,返回ip 而不是 host 名称
ip-address: 127.0.0.1 # 指定自己的 ip 信息,不指定的话会自己寻找
4、测试
启动 EurekaServer,然后启动 user-service。重新打开 http://127.0.0.1:10114
服务提供者
服务提供者要向 EurekaServer 注册服务,并且完成服务续约等工作。
1、服务注册
服务提供者在启动时,会检测配置属性中的:eureka.client.register-with-erueka=true 参数是否正确,事实上默认就是 true。如果值确实为 true,则会向 EurekaServer 发起一个 Rest请求,并携带自己的元数据信息,Eureka Server 会把这些信息保存到一个双层 Map 结构中。第一层 Map 的 Key 就是服务名称,第二层 Map 的 key 是服务的实例 id。
2、服务续约
在注册服务完成以后,服务提供者会维持一个心跳(定时向 EurekaServer 发起 Rest 请求),告诉 EurekaServer:“我还活着”。这个我们称为服务的续约(renew)
有两个重要参数可以修改服务续约的行为:
eureka:
instance:
#服务失效时间,默认值 90 秒
lease-expiration-duration-in-seconds: 90
#服务续约(renew)的间隔,默认为 30 秒
lease-renewal-interval-in-seconds: 30
也就是说,默认情况下每个 30 秒服务会向注册中心发送一次心跳,证明自己还活着。
如果超过 90 秒没有发送心跳,EurekaServer 就会认为该服务宕机,会从服务列表中移除
四、消费者从 Eureka 中获取服务
前面将服务发布到了 Eureka 中,下面修改 consumer 工程,让其从 Eureka 中获取服务。
要从 Eureka 中获取服务就需要引用 Eureka 的客户端。
1、添加 SpringCloud 的依赖
<!-- Eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- SpringCloud 的依赖 -->
<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>
2、修改 application.yml
server:
port: 8081
spring:
application:
name: customer # 应用名称
eureka:
client:
service-url: # EurekaServer 地址
defaultZone: http://127.0.0.1:10114/eureka #,http://127.0.0.1:11114/eureka
instance:
prefer-ip-address: true # 当其它服务获取地址时提供 ip 而不是 hostname
ip-address: 127.0.0.1
3、修改代码,用 DiscoveryClient 对象的方法,根据服务的名称来获取服务的实例
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate template;
@Autowired
private DiscoveryClient discoveryClient;
//在 SpringBoot 集成了 Eureka 后,转换成 xml 的优先级要高于 json,
//所以如果需要 json 格式的数据,就需要加入下面的代码
@RequestMapping(value="/detail/{id}" ,produces= MediaType.APPLICATION_JSON_VALUE)
public User consumer(@PathVariable("id") String id){
// return template.getForObject("http://127.0.0.1:8080/user/"+id, User.class);
List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
// 因为只有一个 UserService,因此我们直接 get(0)获取
ServiceInstance instance = instances.get(0);
//获取服务的 ip 地址和端口号
String serviceurl ="http://"+instance.getHost()+":"+instance.getPort()+"/user/"+id;
return template.getForObject(serviceurl, User.class);
}
}
4、测试
启动 Eureka,user-service,consumer。然后访问 consumer 可以得到如下结果:
4.2 服务消费者
获取服务列表,
当服务消费者启动是,会检测 eureka.client.fetch-registry=true 参数的值,如果为 true,则会从 Eureka Server 服务的列表只读备份,然后缓存在本地。并且每隔 30 秒会重新获取并更新数据。
我们可以通过下面的参数来修改:
eureka:
client:
#在开发环境下,可以修改为 5 秒获取一次。生产环境下一般不用修改
registry-fetch-interval-seconds: 5
4.5 失效剔除和自我保护
1、失效剔除
有些时候,我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server 需要将这样的服务剔除出服务列表。因此它会开启一个定时任务,每隔 60 秒对所有失效的服务(超过 90 秒未响应的服务)进行剔除。
可以通过 eureka.server.eviction-interval-timer-in-ms 参数对其进行修改,单位是毫秒,生产环境不要修改。但是这个时长会对我们开发带来极大的不变,对服务进行了重启,隔了60 秒 Eureka 才会反应过来。所以开发阶段可以适当调整,比如 5 秒
2、自我保护
我们关停一个服务,就会在 Eureka 面板看到一条警告
这是触发了 Eureka 的自我保护机制。当一个服务未按时进行心跳续约时,Eureka 会统计最近 15 分钟心跳失败的服务实例的比例是否超过了 85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka 就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。
但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭自我保护模式
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为 60*1000ms)
4.6 搭建 Eureka 集群
Eureka Server 即服务的注册中心,在上面的案例中,只有一个EurekaServer,如果这个Server 挂掉,那么就会导致整个系统不可用。所以 EurekaServer 在设计时就设计成为可以部署集群,形成高可用的注册中心。
多个 Eureka Server 之间也会互相注册为服务,当服务提供者注册到 Eureka Server 集群中的某个节点时,该节点会把服务的信息同步给集群中的每个节点,从而实现数据同步。因此,无论客户端访问到 Eureka Server 集群中的任意一个节点,都可以获取到完整的服务列表信息。
示例:搭建高可用的 EurekaServer。搭建包含两个 EurekaServer 的集群,端口分别为:
10114 和 11114
集群配置:
1、修改原来的 EurekaServer 配置
server:
port: 10114
spring:
application:
# 名字随便取 应用名称,会在 Eureka 中显示
name: user-eureka
eureka:
client:
register-with-eureka: false # 是否注册自己的信息到 EurekaServer,默认是 true
fetch-registry: false # 是否拉取其它服务的信息,默认是 true
service-url:
#配置成其他服务的地址,不是自己的地址。。
defaultZone: http://127.0.0.1:11114/eureka
2、修改另外一个服务的配置。配置与上面的配置刚好相反
server:
port: 11114
spring:
application:
# 名字随便取 应用名称,会在 Eureka 中显示
name: user-eureka
eureka:
client:
register-with-eureka: false # 是否注册自己的信息到 EurekaServer,默认是 true
fetch-registry: false # 是否拉取其它服务的信息,默认是 true
service-url:
#配置成其他服务的地址,不是自己的地址。。
defaultZone: http://127.0.0.1:10114/eureka
所谓的高可用注册中心,其实就是把 EurekaServer 自己也作为一个服务进行注册,这样
多个 EurekaServer 之间就能互相发现对方,从而形成集群。因此我们做了以下修改:
1、删除了 register-with-eureka=false 和 fetch-registry=false 两个配置。因为默认值是 true,
这样就会吧自己注册到注册中心了。
2、把 service-url 的值改成了另外一台 EurekaServer 的地址,而不是自己
这样就搭建好了一个 Eureka 集群。然后可以分别启动两个服务。这时候,服务的提供者和服务的消费者,只需要修改一下配置文件,指定多个 eurekaserver 的地址,就能将服务发布到集群中,或者从集群中获取服务了。
配置文件如下:
eureka:
client:
service-url: #EurekaServer 地址,集群配置
defaultZone:http://127.0.0.1:10114/eureka,http://127.0.0.1:11114/eureka