百度百科描述:Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka 内部包含两个组件:Eureka Server和Eureka Client
Eureka Server:即注册中心
Eureka Client:既可以是服务提供者也可以是服务消费者
Eureka Client 启动后会向 Eureka Server 注册自己,这样其他的 Eureka Client 访问 Eureka Server 即可拿到注册了的 Eureka Client 的信息。
Eureka 架构
上图是官网给的基于集群部署的 Eureka 架构图,先看一下图上各个部件的表示含义以及它们之间的交互:
- Eureka Server:Eureka 服务端,多个 Eureka Server 可构成集群,集群中各节点完全对等。
- Eureka Client:Eureka 客户端,业务服务依赖它实现服务注册和服务发现功能。
- Application Service:服务提供者,依赖 Eureka Client 实现服务注册功能。
- Application Client:服务消费者,依赖 Eureka Client 实现服务发现功能。
- Register:Eureka Client 启动时会发起 Register 请求向 Eureka Server 注册自己。
- Renew:Eureka Client 会周期性的向 Eureka Server 发送心跳来续约,默认30s。
- Cancel:Eureka Client 关闭时会发送 Cancel 下线请求。
- Get:Eureka Client 会周期性的发送 Get 请求,从 Eureka Server 获取注册表信息,默认30s。
- Make Remote Call:服务消费者通过 Make Remote Call 访问服务提供者。
- Replicate:Eureka Server 之间通过 Replicate 实现数据同步。当 Eureka Client 有请求(Heartbeat, Register, Cancel, StatusUpdate, DeleteStatusOverride)到某一个 Eureka Server 节点,该节点完成自身对应的操作后,会通过 Replicate 将本次请求同步到其他节点。
服务治理
-
服务注册:
在服务治理框架中,通常都会构建一个服务注册中心,每个服务提供者需要向注册中心,登记自己的注册信息。比如IP,端口号,版本号,服务名,通信协议等等附加信息告知注册中心,注册中心会根据服务名,进行分类服务清单。
-
服务发现 :
由于在服务治理框架下运作程序,服务间的调用不在是通过指定具体的实例地址来实现,服务间的调用通过服务名来调用的。所以当服务调用方 想要调用服务提供方的接口时,服务调用方是不知道服务提供方的具体实例地址的,因此调用方也需要向注册中心Eureka注册服务,并获取所有服务的实例清单。
-
服务同步 :
当Eureka实现高可用后,服务提供者可分别注册到集群中的不同服务注册中心上,也就是说 它们的信息分别被不同的注册中心维护,此时由于服务中心互相注册为服务(高可用),当服务提供者发送注册请求到一个服务注册中心时,它会将该请求转发同步给集群中的其他注册中心,从而实现服务注册中心的服务同步。通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心中的任意一台获取到。
-
服务续约 :
在注册完服务之后,服务提供者会维护一个心跳用来持续告诉Eureka“我还活着”,以防止Eureka的剔除服务将该服务从列表清单中排除出去,我们称该操作为服务续约。
关于服务续约有两个重要的属性:
##用于定义服务续约任务的调用间隔时间,默认为30秒 eureka.instance.lease-renewal-interval-in-seconds=30 ##参数用于定义服务失效的时间,默认为90秒 eureka.instance.lease-expiration-duration-in-seconds=90
-
获取服务 :
当服务消费者程序启动时,它会发送一个Rest请求给注册中心,来获取上面注册的服务清单,为了性能考虑,Eureka会维护一份只读的服务清单来返回给客户端,同时该缓存清单会每隔30秒更新一次。
获取服务是服务消费者的基础,所以必须确保
eureka.client.fetch-registry=true
默认为true
,若希望修改缓存清单的更新时间,可以通过eureka.client.registry-fetch-interval-seconds=30
进行修改,默认30秒,参数单位秒 -
服务调用 :
获取服务清单后,通过服务名可以获取实例的名和实例的元数据(ip,port等等)利用Ribbon实现轮询方式调用服务提供方。
-
服务下线 :
当服务下线后,正常情况下 我们是不希望,服务消费方继续调用已经下线的服务的,所以再服务提供方正常关闭时,会触发一个服务下线的REST请求,告诉Eureka “我要下线了”。Eureka收到请求后,会将此服务设置为(DOWN),并将此消息广播到出去,更新缓存清单。
-
服务中心的失效-剔除
当服务非正常关闭时,比如机器Down了,断电,内存溢出,网络故障等等。这时候是不会触发服务下线的REST的请求,为了将这些已经无法提供的服务剔除,Eureka Server会在启动时,创建一个定时任务,默认每隔60秒将清单中超时没有续约的剔除。
Eureka 的 RestAPI
操作 | http动作 | 描述 |
---|---|---|
注册新的应用实例 | POST/eureka/apps/{appId} | 可以输入json或xml格式的body,成功返回204 |
注销应用实例 | DELETE/eureka/apps/{appId}/{instanceId} | 成功返回200 |
应用实例发送心跳 | PUT/eureka/apps/{appId}/{instanceId} | 成功返回200,如果instanceId不存在返回404 |
查询所有实例 | GET/eureka/apps | 成功返回200,输出json或xml格式 |
查询指定appId的实例 | GET/eureka/apps/{appId} | 成功返回200,输出json或xml格式 |
根据指定appId和instanceId查询 | GET/eureka/apps/{appId}/{instanceId} | 成功返回200,输出json或xml格式 |
根据指定instanceId查询 | GET/eureka/instances/{instanceId} | 成功返回200,输出json或xml格式 |
暂停应用实例 | PUT/eureka/apps/{appId}/{instnceId}/status?value=OUT_OF_SERVICE | 成功返回200,失败返回500 |
恢复应用实例 | DELETE/eureka/apps/{appId}/{instnceId}/status?value=UP(value参数可不传) | 成功返回200,失败返回500 |
更新元数据 | PUT/eureka/apps/{appId}/{instnceId}/metadata?key=value | 成功返回200,失败返回500 |
根据vip地址查询 | GET/eureka/vips/{vipAddress} | 成功返回200,输出json或xml格式 |
根据svip地址查询 | GET/eureka/svips/{svipAddress} | 成功返回200,输出json或xml格式 |
InstanceInfo服务实例信息
Eureka使用InstanceInfo(com.netflix.appinfo.InstanceInfo.java)来代表注册的服务实例。
字段 | 说明 |
---|---|
instanceId | 实例 id |
app | 应用名 |
appGroupName | 应用所属群组 |
ipAddr | ip地址 |
sid | 被废弃的属性,默认na |
port | 端口号 |
securePort | https的端口号 |
homePageUrl | 应用实例的首页url |
statusPageUrl | 应用实例的的状态页 |
healthCheckUrl | 应用实例健康检查的rul |
secureHealthCheckUrl | 应用实例健康检查https的rul |
vipAddress | 虚拟ip地址 |
secureVipAddress | https的虚拟ip地址 |
countryId | 被废弃的属性,属性为1,代表US |
dataCenterInfo | dataCenter信息,Netflix或Amazon或者MyOwn |
hostName | 主机名称 |
status | 实例状态,如UP,DOWN,STARTING,OUT_OF_SERVICE,UNKNOWN |
overridenstatus | 外界需要强制覆盖的状态值,默认为UNKOWN |
leaseInfo | 租约信息 |
isCoordinatingDiscoveryServer | 首先标识是否是DiscoveryServer,其次标识该DiscoveryServer是否是响应你请求的实例 |
metadata | 应用实例的元数据信息 |
lastUpdatedtimestamp | 状态信息最后更新时间 |
lastDrityTimestamp | 实例信息最新的过期时间,在Client端用于标识 |
actionType | 标识Eureka Server对该实例执行的操作,包括ADDED,MODIFIED,DELETE这三类 |
asgName | 在AWS的autoscaling group的名称 |
LeaseInfo租约信息
Eureka使用LeaseInfo(com.netflix.appinfo.LeaseInfo.java)来标识应用实例的租约信息。
以下这些参数主要用于标识应用实例的心跳情况,比如约定的心跳周期,租约有效期,最近一次续约时间等。
字段 | 说明 |
---|---|
renewalIntervalInSecs | Client端续约的间隔周期 |
durationInSecs | Client端需要设定的租约的有效时长 |
registrationTimestamp | Server端设置的该租约第一次注册时间 |
lastRenewalTimestamp | Server端设置的该租约最后一次续约时间 |
evictionTimestamp | Server端设置的该租约被剔除的时间 |
serviceUpTimestamp | Server端设置的该服务实例标记为UP的时间 |
ServiceInstance实例信息的抽象接口
ServiceInstance(import com.netflix.appinfo.LeaseInfo.java)是spring cloud对service discovery的实例信息的抽象接口,约定了服务发现的实例应用有哪些通用的信息。
方法 | 说明 |
---|---|
getServiceId() | 服务id |
getHost() | 实例的host |
getPort() | 实例的端口 |
isSecure() | 实例是否开启https |
getUri() | 实例的uri地址 |
getMetadata() | 实例的元数据信息 |
getScheme() | 实例的scheme |
由于spring cloud discovery适配了zookeeper,consul,netflix eureka等注册中心,因此其ServiceInstance定义更为抽象和和通用,而且采取的是定义方法的方式。spring cloud对该接口的实现类为EurekaRegistration(org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration.java),EurekaRegistration实现了ServiceInstance接口,同时还实现了Closeable接口,它的作用之一就是在close的时候调用eurekaClient.shutdown()方法,实现优雅关闭Eureka Client。
EurekaServer的安全配置
在实际生产部署过程中,往往需要考虑一个安全问题,比如Eureka Server自己有暴露Rest API,如果没有安全认证,别人就可以通过REST API随意修改信息,造成异常。Eureka 为我们提供了一个比较简单的解决方案。
-
要启动Eureka Server的HTTP Basic认证,则需要引入一个依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
-
在配置文件中指定账户密码
#配置基本的保护 spring.security.basic.enabled=true #配置用户名 spring.security.user.name=admin #配置密码 spring.security.user.password=admin
-
由于spring-boot-starter-security默认开启了csrf校验,对于Client端这类非界面应用来说不合适,但是有没有配置文件的方式可以禁用,需要自己通过Javad的配置类文件禁用
@EnableWebSecurity public class EurekaSecurity extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); //禁用csrf校验 http.csrf().disable(); } }