Eureka是Spring Cloud Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。功能类似于dubbo的注册中心,比如Zookeeper。
服务提供者向Eureka Serve注册,注册发生在第一次心跳,它提供关于自己的元数据(诸如主机和端口,健康指标URL等)Eureka Server。
Eureka客户会每隔30秒发送一次心跳来续约。 通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。 正常情况下,如果Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除。 建议不要更改续约间隔。
注册信息和续订被复制到集群中的Eureka Serve所有节点。 来自任何区域的Eureka Client都可以查找注册表信息(每30秒发生一次)。根据这些注册表信息,Application Client可以远程调用Applicaton Service来消费服务
Eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与Eureka客户端的缓存信息不同, Eureka客户端自动处理。
如果由于某种原因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,默认的情况下Eureka客户端使用压缩JSON格式来获取注册列表的信息。
Eureka客户端在程序关闭时向Eureka服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。
Eviction用来定期(默认为每60秒)在Eureka Server检测失效的服务,检测标准就是超过一定时间没有Renew的服务,默认失效时间为90秒,也就是如果有服务超过90秒没有向Eureka Server发起Renew请求的话,就会被当做失效服务剔除掉, Eureka服务器会将该服务实例从服务注册列表删除。
保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE
当一个新的Eureka Server出现时,它尝试从相邻节点获取所有实例注册表信息。如果从Peer节点获取信息时出现问题,Eureka Serve会尝试其他的Peer节点。如果服务器能够成功获取所有实例,则根据该信息设置应该接收的更新阈值。如果有任何时间,Eureka Serve接收到的续约低于为该值配置的百分比(默认为15分钟内低于85%),则服务器开启自我保护模式,即不再剔除注册列表的信息。
这样做的好处就是,如果是Eureka Server自身的网络问题,导致Eureka Client的续约不上,Eureka Client的注册列表信息不再被删除,也就是Eureka Client还可以被其他服务消费。
Eureka包含两个组件:Eureka Server和Eureka Client
1:Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到
2:Eureka Client是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)
3:Eureka Server之间将会通过复制的方式完成数据的同步。Eureka还提供了客户端缓存的机制,即使所有的Eureka Server都挂掉了,客户端依然可以利用缓存中的信息消费其它服务的API。Eureka通过心跳检测、健康检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
创建一个SpringBoot项目,用来当作Eureka Server
Springboot还是采用1.5.6.RELEASE
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> </parent>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>
在SpringBoot的启动类上添加: @EnableEurekaServer
可以采用properties或者yml,比如application.yml,示例如下:
server: port: 8761 eureka: instance: hostname: localhost #是否优选使用ip地址 prefer-ip-address: true client: #启动是否注册 registerWithEureka: false #是否获取注册列表 fetchRegistry: false serviceUrl: defaultZone: http://localhost:8761/eureka/
启动并查看注册中心: http://localhost :8761/,如果不能正常显示,可以把spring-cloud-netflix-eureka-server-1.3.0.RELEASE.jar里面的static和templates的内容添加到工程的resources下面来
依赖基本跟Eureka Server的项目一样,但不是依赖Eureka Server了,而是:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
配置
可以采用properties或者yml,比如application.yml,示例如下:
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8765 spring: application: name: userService
在启动类上添加 @EnableDiscoveryClient 或者 @EnableEurekaClient
SpringCloud中的“Discovery Service”有多种实现,比如:eureka, consul, zookeeper等。@EnableDiscoveryClient注解是基于spring-cloud-commons依赖,可以在不同的服务注册服务器上使用; @EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka用
开发服务对外提供REST服务的Controller,以及具体的服务实现
可以采用properties或者yml,比如application.yml,示例如下:
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8764 spring: application: name: userConsumer
在启动类上添加 @EnableDiscoveryClient 或者 @EnableEurekaClient
开发Controller
做一个配置Bean,用来提供RestTemplate
开发具体的服务实现,例如:
@Service public class UserService { @Autowired private RestTemplate restTemplate; @Autowired private EurekaClient discoveryClient; public String hiService(String name) { InstanceInfo instance = discoveryClient.getNextServerFromEureka("userService", false); String path = instance.getHomePageUrl(); String ret = restTemplate.getForObject(path+"/userServiceProvider?name="+name,String.class); return ret; } }
一致性(C):系统在执行过某项操作后仍然处于一致的,在分布式系统中,更新操作执行成功后所有的用户都应该读取到最新的值,这样的系统被认为具有强一致性。
可用性(A):每个请求都能接受到一个响应,无论响应成功或失败。
分区容错性(P):系统在存在网络分区的情况下仍然可以接受请求并处理,这里网络分区是指由于某种原因网络被分成若干个孤立区域,而区域之间互不相通。
由于分区容错性在是分布式系统中必须要保证的,因此只能在A和C之间进行权衡。Zookeeper保证的是CP, 而Eureka则是AP。
在任何时刻对ZooKeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性、但是它不能保证每次服务请求的可用性,也就是在极端环境下,ZooKeeper可能会丢弃一些请求,消费者程序需要重新请求才能获得结果,比如重新选举leader的时间太长,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
实际应用中,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用,也就是说,服务注册功能对可用性的要求要高于一致性。
Eureka服务节点在短时间里丢失了大量的心跳连接,那么这个 Eureka节点会进入“自我保护模式”,同时保留那些“心跳死亡”的服务注册信息不过期。此时,这个Eureka节点对于新的服务还能提供注册服务,对于“死亡”的仍然保留,以防还有客户端向其发起请求。当网络故障恢复后,这个Eureka节点会退出“自我保护模式”。Eureka的哲学是,同时保留“好数据”与“坏数据”总比丢掉任何数据要更好。
构建Eureka Server 集群
深入理解
实际应用中,因负载等原因,往往可能需要在生产环境构建多于两个的Eureka Server节点。那么对于如何配置serviceUrl来让集群中的服务进行同步,需要我们更深入的理解节点间的同步机制来做出决策。
Eureka Server的同步遵循着一个非常简单的原则:只要有一条边将节点连接,就可以进行信息传播与同步。
1:两两注册的方式可以实现集群中节点完全对等的效果,实现最高可用性集群,任何一台注册中心故障都不会影响服务的注册与发现
2:Eureka Server具备单方面有指向的服务传播与同步机制,在一些对服务发现有限制的情况下,可以利用这样的机制进行服务注册与发现的的单向控制
部署集群后同一路径访问:切换不同的服务返回结果
1:Eureka Client首先会尝试与同一区域的Eureka Server进行连接,如果出现问题,或者服务器不在同一个区域中,则客户端将故障转移到其他区域中的服务器。
2:一旦服务器开始接收流量,在服务器上执行的所有操作都将被复制到服务器知道的所有对等节点。如果某个操作由于某种原因而失败,那么该信息将在下一个在服务器之间复制的心跳信号进行核对。
3:当Eureka服务器启动时,它会尝试从邻居节点获取所有实例注册表信息。如果在从节点获取信息时出现问题,服务器在放弃之前尝试所有的对等点。如果服务器能够成功获取所有实例,则会根据该信息设置应该接收的更新阈值。如果任何时候,续订低于为该值配置的百分比(在15分钟内低于85%),则服务器将停止到期实例以保护当前实例注册表信息。也就是进入自我保护模式。
在这种情况下,如果服务器无法从邻居节点获取注册表信息,则会等待几分钟(5分钟),以便客户端可以注册其信息。此时注册可能发生在孤立的服务器上,有些客户端可能会反映新的注册,而其他客户端可能不会。在网络连接恢复到稳定状态后,情况会自动自动更正。当对等方能够正常通信时,注册信息会自动同步。
Service Provider只需要通知到任意一个Eureka Server后就能保证状态会在所有的Eureka Server中得到更新。
具体实现方式其实很简单,就是接收到Service Provider请求的Eureka Server,把请求再次转发到其它的Eureka Server,调用同样的接口,传入同样的参数,除了会在header中标记isReplication=true,从而避免重复的replicate。
Peer之间的状态是采用异步的方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的。
Eureka Server在启动后会调用EurekaClientConfig.getEurekaServerServiceUrls来获取所有的Peer节点,并且会定期更新。定期更新频率可以通过eureka.server.peerEurekaNodesUpdateIntervalMs配置。
这个方法的默认实现是从配置文件读取,所以如果Eureka Server节点相对固定的话,可以通过在配置文件中配置来实现。
如果希望能更灵活的控制Eureka Server节点,比如动态扩容/缩容,那么可以覆盖getEurekaServerServiceUrls方法,提供自己的实现,比如通过数据库读取Eureka Server列表
1:SpringCloud中的配置选项可以参看官方文档: http://cloud.spring.io/spring...
2:客户端的默认配置,在Eureka中是 https://github.com/Netflix/eu...
在SpringCloud结合Netflix中是: https://github.com/spring-clo...
3:服务端的默认配置,在Eureka中是: https://github.com/Netflix/eu...
在SpringCloud结合Netflix中是:
https://github.com/spring-clo...
n 添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
n 在配置文件中添加,示例如下:
security: basic: enbled: true user: name: admin password: admin
消费者的连接方式
也需要用户名和密码,否则会报错,示例如下:
defaultZone: http://admin :cc@localhost:8761/eureka/
1:Eureka实例的状态页面和运行状况指示器分别默认为“/info”和“/health”,它们是Spring Boot中端点的默认位置。
2:默认情况下,Spring CLoud Eureka中各个服务实例的健康检测并不是通过spring-boot-actuator模块的/health端点来实现的,而是依靠客户端心跳的方式来保持服务实例的存活。因此,默认的心跳方式作为健康检测并不保险,因为不能检测服务是否能有效提供服务
可以通过简单的配置,把Eureka客户端的健康检测交给spring-boot-actuator模块的/health端点,以实现更加全面的健康状态维护。 详细步骤如下:
(1)在pom.xml中引入spring-boot-starter-actuator模块的依赖
(2)在application.properties中增加参数配置eureka.client.healthcheck.enabled=true
3:如果要使用非默认上下文路径或servlet路径(例如server.servletPath=/foo)或管理端点路径(例如management.contextPath=/admin),则需要更改,如:
eureka: instance: statusPageUrlPath: ${management.context-path}/info healthCheckUrlPath: ${management.context-path}/health