搭建单节点Eureka注册中心
<!--
spring-boot版本:2.3.7.RELEASE
spring-cloud版本:Hoxton.SR9
-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
spring:
application:
name: eureka-server1
server:
port: 7000
eureka:
client:
service-url:
defaultZone: http://192.168.0.103:7000/eureka-server1 #设置服务注册中心的URL,用于client和server端交流
register-with-eureka: false #否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
fetch-registry: false #是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
instance:
appname: eureka-server1 # 服务实例名称,可覆盖spring定义名称
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Eureka客户端
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
server:
port: 8000
eureka:
instance:
appname: eurka-client
client:
service-url:
defaultZone: http://127.0.0.1:7000/eureka
@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
Eureka整体介绍
注册中心实现服务的注册与发现,包含服务器端与客户端两部分。https://github.com/Netflix/Eureka
- 服务器端 Server:为所有客户端提供服务注册与服务发现功能,维护注册到自身的服务客户端的相关信息,同时提供接口给客户端获取注册列表中其他服务的信息,使得客户端之间能够相互调用。
- Server注册中心功能
- 服务注册表:记录各个微服务信息,例如服务名称,ip,端口等。注册表提供 查询API(查询可用的微服务实例)和管理API(用于服务的注册和注销)。
- 服务注册与发现:
- 注册:将微服务信息注册到注册中心。
- 发现:查询可用微服务列表及其网络地址。
- 服务检查:定时检测已注册的服务,如发现某实例长时间无法访问,就从注册表中移除。
- Server注册中心功能
- 服务客户端 Client:客户端将自己的服务通过一定方式登记在服务端,并在正常范围维护自己信息的一致性,方便其他服务发现自己。同时可以通过服务端拉取到自己依赖的其他服务的信息,完成服务调用。客户端还内置了负载均衡,用于实现基本的负载均衡。
- Client功能
- 注册:每个微服务启动时,将自己的网络地址等信息注册到注册中心,注册中心会存储(内存中)这些信息。
- 获取服务注册表:服务消费者从注册中心,查询服务提供者的网络地址,并使用该地址调用服务提供者,为了避免每次都查注册表信息,所以client会定时去server拉取注册表信息到缓存到client本地。
- 心跳:各个微服务与注册中心通过某种机制(心跳)通信,若注册中心长时间和服务间没有通信,就会注销该实例。
- 调用:实际的服务调用,通过注册表,解析服务名和具体地址的对应关系,找到具体服务的地址,进行实际调用。
- Client功能
Eureka Server与Eureka Client之间主要通过心跳方式联系,心跳(HeartBeat)是Eureka Client 定时向Eureka Server汇报本服务实例当前的状态,维护本服务在注册表中租约的有消息。EurekaClient将定时从Eureka Server中拉取注册表中的信息,并将这些信息缓存到本地,用于服务发现。
Eureka高可用
- 方式1:通过运行多个EurekaServr实例并相互注册的方式实现。Server节点之间会彼此增量地同步信息,从而保证节点中数据一致。(需要注意,多个EurekaServer实例名称需要相同)
- 方式2:运行多个Eureka Server实例,Server节点之间没有关系,Eureka Client同时向多个Server注册信息。
采用方式1搭建Eureka集群方案:
- 本地搭建Eureka服务集群,直接使用Ip会有问题,故采用“主机名:端口号的方式”。
修改hosts文件:
127.0.0.1 eureka1
127.0.0.1 eureka2
- 节点1 eureka-server1 ```java spring: application: name: eureka
server: port: 7001
eureka: client: service-url: defaultZone: http://eureka2:7002/eureka #设置服务注册中心的URL,用于client和server端交流 instance: hostname: eureka1 appname: eureka
3. 节点2 eureka-server2
```java
spring:
application:
name: eureka
server:
port: 7002
eureka:
client:
service-url:
defaultZone: http://eureka1:7001/eureka #设置服务注册中心的URL,用于client和server端交流
instance:
hostname: eureka2
appname: eureka
添加启动项,使用2个配置文件
启动后分别查看2个Server,可以看到 available-replicas 项(可用的复制节点)
Eureka原理
Register 服务注册
想要进行服务注册的实例,需向Eureka Server注册信息。注册在第一次心跳(HeartBeat)时提交。
Renew 续租
续租、更新状态,Eureka客户端通过向Eureka客户端发送心跳来表示当前服务是活动可用的,30秒内发送一次心跳,如果90秒(3个周期)Eureka Client没有想Eureka Server发送信息,Eureka Server会认为该服务不可用,会从服务注册表中将其删除。如果每分钟client续约数小于总数的85%,那么Eureka Server 会有自我保护机制,认为是网络问题,不会删除服务)
Fetch Registry 拉取注册表
Eureka Client从Eureka Server获取注册表并将其缓存在本地,之后通过这些信息来查找其他服务。
通过获取上一个获取周期和当前获取周期之间的增量进行更新,可以定期(30s)更新此信息。
节点信息在服务器中保存的时间更长(大约3分钟),因此获取节点信息时可能会再次返回相同的实例。Eureka客户端自动处理重复的信息。
在获得增量之后,Eureka客户机通过比较服务器返回的实例计数来与服务器协调信息,如果由于某种原因信息不匹配,则再次获取整个注册表信息。
Cancel 取消请求
Eureka客户端在关闭时向Eureka服务器发送取消请求。这将从服务器的实例注册表中删除实例,从而有效地将实例从通信量中取出。
Time Lag 同步延迟
同步延迟,来自Eureka客户端的所有操作可能需要一段时间才能反映到Eureka服务器上,然后反映到其他Eureka客户端上。这是因为eureka服务器上的有效负载缓存,它会定期刷新以反映新信息。Eureka客户端还定期地获取增量。因此,更改传播到所有Eureka客户机可能需要2分钟。
communication mechanism 通讯机制
Http协议下的Rest请求,默认情况下Eureka使用Jersey和Jackson以及JSON完成节点间的通讯
Eureka 单独使用
RESTful服务调用
官方API文档:https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
Operation | HTTP action | Description |
---|---|---|
注册一个新的服务 | POST /eureka/v2/apps/appID |
Input: JSON/XML payload HTTP Code: 204 on success |
移除一个服务 | DELETE /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success |
发送renew心跳 | PUT /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success 404 if instanceID doesn’t exist |
查看全部服务信息 | GET /eureka/v2/apps |
HTTP Code: 200 on success Output: JSON/XML |
通过appID 查看服务全部实例信息 | GET /eureka/v2/apps/appID |
HTTP Code: 200 on success Output: JSON/XML |
通过appID/instanceID查看具体服务实例信息 | GET /eureka/v2/apps/appID/instanceID | HTTP Code: 200 on success Output: JSON/XML |
通过instanceID查看实例信息 | GET /eureka/v2/instances/instanceID |
HTTP Code: 200 on success Output: JSON/XML |
使实例退出服务 | PUT /eureka/v2/apps/appID/instanceID/status?value=OUT_OF_SERVICE | HTTP Code: 200 on success 500 on failure |
将服务移回服务 | DELETE /eureka/v2/apps/appID/instanceID/status?value=UP (The value=UP is optional, it is used as a suggestion for the fallback status due to removal of the override) | HTTP Code: 200 on success 500 on failure |
更新元数据 | PUT /eureka/v2/apps/appID/instanceID/metadata?key=value | HTTP Code: 200 on success 500 on failure |
查询特定vipAddress下的所有实例 | GET /eureka/v2/vips/vipAddress |
HTTP Code: 200 on success Output: JSON/XML 404 if the vipAddress does not exist. |
查询特定svipAddress下的所有实例 | GET /eureka/v2/svips/svipAddress |
HTTP Code: 200 on success Output: JSON/XML 404 if the svipAddress does not exist. |
查看服务状态
get: /eureka/status 使用浏览器请求url会返回服务器状态信息
查看注册到eureka中的服务信息
查看注册到eureka的具体的服务
get: {ip:port}/eureka/apps/{appname}/{id}
服务续约
put:{ip:port}/eureka/apps/{appname}/{id}?lastDirtyTimestamp={}&status=up
更改服务状态
put:{ip:port}/eureka/apps/{appname}/{id}/status?lastDirtyTimestamp={}&value={UP/DOWN}
对应eureka源码的:InstanceResource#statusUpdate
删除状态更新
delete:{ip:port}/eureka/apps/{appname}/{id}/status?lastDirtyTimestamp={}&value={UP/DOWN}
删除服务
delete: {ip:port}/eureka/apps/{appname}/{id}
元数据 Metadata
Eureka元数据有2种,标准元数据,用户自定义元数据
- 标准元数据:主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。
- 自定义元数据:可以使用 e ureka.instance.metadata-map 配置,这些元数据可以在远程客户端中访问,但是一般不改变客户端行为,除非客户端知道该元数据的含义。
Eureka Client
EurekaClient 可以在客户端获取eureka服务器上的注册列表信息
org.springframework.cloud.client.DiscoveryClient与com.netflix.discovery.DiscoveryClient
org.springframework.cloud.client.DiscoveryClient是SpringCloud对注册中心client的抽象封装,提供公用功能
org.springframework.cloud.client.DiscoveryClient定义用来服务发现的客户端接口,是客户端进行服务发现的核心接口,是spring cloud用来进行服务发现的顶级接口,在common中可以看到其地位。在Netflix Eureka和Consul中都有具体的实现类。
public String test() {
List<ServiceInstance> instances = new ArrayList<>();
// getService,获取Eureka注册中心全部服务名
for (String service : discoveryClient.getServices()) {
if ("provider".equals(service)) {
// 根据具体的服务名获取全部实例
instances = discoveryClient.getInstances(service);
}
}
System.out.println(instances);
String response = "远程访问出错啦";
if (instances != null && instances.size() > 0) {
// 随机获取一个实例进行访问
int i = (int) (Math.random() * instances.size());
ServiceInstance serviceInstance = instances.get(i);
URI uri = serviceInstance.getUri();
String path = null;
try {
path = uri.toURL().toString().concat("/provider/test");
} catch (MalformedURLException e) {
e.printStackTrace();
}
System.out.println();
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.getForObject(path, String.class);
}
return response;
}
多网卡选择
- ip注册
```yaml
eureka:
instance:
表示将自己的ip注册到EurekaServer上。不配置或false,表示将操作系统的hostname注册到server
prefer-ip-address: true ip-address: 127.0.0.1 # 实际能够访问到的ip
2. 服务器有多个网卡,eh0,eh1,eh2,只有eh0可以让外部其他服务访问进来,而Eureka client将eh1和eh2注册到Eureka server上,这样其他服务就无法访问该微服务了。
<a name="s3QYv"></a>
## Actuator 监控应用
若需使用Actuator监控,需在Eureka Server端导入依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
通过EurekaServer 地址+”/actuator” 访问:http://localhost:7000/actuator
actuator默认开启info和health端点,如需开启所有端点,需配置management.endpointswebexposure.include
management:
endpoints:
web:
exposure:
include: '*'
配置后壳查看所有端点
{"_links":
{"self":{"href":"http://localhost:7000/actuator","templated":false},
"archaius":{"href":"http://localhost:7000/actuator/archaius","templated":false},
"beans":{"href":"http://localhost:7000/actuator/beans","templated":false},
"caches-cache":{"href":"http://localhost:7000/actuator/caches/{cache}","templated":true},
"caches":{"href":"http://localhost:7000/actuator/caches","templated":false}
,"health":{"href":"http://localhost:7000/actuator/health","templated":false},
"health-path":{"href":"http://localhost:7000/actuator/health/{*path}","templated":true},
"info":{"href":"http://localhost:7000/actuator/info","templated":false},
"conditions":{"href":"http://localhost:7000/actuator/conditions","templated":false},
"configprops":{"href":"http://localhost:7000/actuator/configprops","templated":false},
"env":{"href":"http://localhost:7000/actuator/env","templated":false},
"env-toMatch":{"href":"http://localhost:7000/actuator/env/{toMatch}","templated":true},
"loggers":{"href":"http://localhost:7000/actuator/loggers","templated":false},
"loggers-name":{"href":"http://localhost:7000/actuator/loggers/{name}","templated":true},
"heapdump":{"href":"http://localhost:7000/actuator/heapdump","templated":false},
"threaddump":{"href":"http://localhost:7000/actuator/threaddump","templated":false},
"metrics-requiredMetricName":{"href":"http://localhost:7000/actuator/metrics/{requiredMetricName}","templated":true},
"metrics":{"href":"http://localhost:7000/actuator/metrics","templated":false},
"scheduledtasks":{"href":"http://localhost:7000/actuator/scheduledtasks","templated":false},
"mappings":{"href":"http://localhost:7000/actuator/mappings","templated":false},
"refresh":{"href":"http://localhost:7000/actuator/refresh","templated":false},
"features":{"href":"http://localhost:7000/actuator/features","templated":false},
"service-registry":{"href":"http://localhost:7000/actuator/service-registry","templated":false}
}
}
api端点功能
health
shutdown
用来关闭节点
开启远程关闭功能
management.endpoint.shutdown.enabled=true
使用Post方式请求端点 shutdown,远程关闭当前服务
返回:{“message”: “Shutting down, bye…”}
beans
configprops
env
mappings
info
metrics
返回应用的各类重要度量指标信息
metrics节点并没有返回全量信息,我们可以通过不同的key去加载我们想要的值
例:metrics/jvm.memory.max
threaddump
Eureka健康检查
Eureka Client 通过发送心跳与Eureka Server保持通信Renew,但是远程调用一个服务,需要该服务同时是”UP”状态才表示服务可用。
开启服务健康手动控制
client:
service-url:
defaultZone: http://127.0.0.1:7000/eureka
registry-fetch-interval-seconds: 5
healthcheck:
enabled: true
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
@Service
public class HealthStatusService implements HealthIndicator {
private boolean health = true;
@Override
public Health getHealth(boolean includeDetails) {
return HealthIndicator.super.getHealth(includeDetails);
}
@Override
public Health health() {
if (health) {
return new Health.Builder().up().build();
} else {
return new Health.Builder().down().build();
}
}
public void setHealth(boolean health) {
this.health = health;
}
}
@PutMapping("/status/{status}")
public Health changeStatus(@PathVariable("status") Boolean status) {
healthStatusService.setHealth(status);
return healthStatusService.health();
}
安全配置
Eureka Server端
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
spring:
application:
name: eureka
security:
user:
name: ixiaoyu2
password: ixiaoyu2
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
super.configure(http);
}
}
Eureka Client
server:
port: 8000
spring:
application:
name: provider
eureka:
instance:
appname: provider
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 60
metadata-map:
microserviceId: 9
prefer-ip-address: true
ip-address: 127.0.0.1
client:
service-url:
defaultZone: http://ixiaoyu2:ixiaoyu2@127.0.0.1:7000/eureka
registry-fetch-interval-seconds: 5
healthcheck:
enabled: true
management:
endpoint:
shutdown:
enabled: true #开启远程服务下线management:
endpoints:
web:
exposure:
include: '*'
Eureka 的优化
自我保护机制
- 当服务多时,开启自我保护;当服务少时,关闭自我保护
清除服务的间隔时间(快速下线)
- timer:多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题
- 关闭从readOnly缓存度注册表
- 优化readWrite和readOnly缓存的同步时间间隔
server: # 自我保护,看服务多少。 enable-self-preservation: false # 自我保护阈值 renewal-percent-threshold: 0.85 # 剔除服务时间间隔 eviction-interval-timer-in-ms: 1000 # 关闭从readOnly读注册表 use-read-only-response-cache: false # readWrite 和 readOnly 同步时间间隔。 response-cache-update-interval-ms: 1000
Eureka实现AP,是弱一致性
Eureka Regin 和 zone配置
参考:https://juejin.cn/post/6844903928178409479
如图所示,有一个 region:华北地区,下面有两个机房,机房A 和机房B
每个机房内有一个 Eureka Server 集群 和两个服务提供者 ServiceA 和 ServerB
现在假设 serverA 需要调用 ServerB 服务,按照就近原则,serverA 会优先调用同一个 zone 内的 ServiceB,当 ServiceB 不可用时,才会去调用另一个 zone 内的 ServiceB
- 服务注册:要保证服务注册到同一个zone内的注册中心,因为如果注册到别zone的注册中心的话,网络延时比较大,心跳检测很可能出问题。
- 服务调用:要保证优先调用同一个zone内的服务,只有在同一个zone内的服务不可用时,才去调用别zone的服务。
server 配置:
```yaml应用名称
spring: application: name: eureka-service
eureka 配置
eureka: client: fetch-registry: true register-with-eureka: true prefer-same-zone-eureka: true region: chengdu # 成都区域 availability-zones: chengdu: z1,z2 # 成都区域有两个可用空间 service-url:
# z1 空间 集群
z1: http://127.0.0.1:8011/eureka,http://127.0.0.1:8012/eureka
# z2 空间 集群
z2: http://127.0.0.1:8021/eureka,http://127.0.0.1:8022/eureka
server: enable-self-preservation: false # 关闭自我保护,默认true开启 response-cache-update-interval-ms: 1000 # 缓存更新时间 eviction-interval-timer-in-ms: 1000 # 服务剔除间隔时间 use-read-only-response-cache: false # 关闭only read缓存使用
server: port: 8011
<a name="FAoME"></a>
### client-provider配置
```yaml
server:
port: 8088
spring:
application:
name: api-passenger-zone
eureka:
client:
register-with-eureka: true
fetch-registry: true
prefer-same-zone-eureka: true
region: chengdu
availability-zones:
chengdu: z1,z2 # z1空间 z1放前面
service-url:
z1: http://127.0.0.1:8011/eureka,http://127.0.0.1:8012/eureka
z2: http://127.0.0.1:8021/eureka,http://127.0.0.1:8022/eureka
instance:
lease-renewal-interval-in-seconds: 30
# 设置服务空间
metadata-map:
zone: z1
appname: api-passenger-zone
prefer-ip-address: true
ip-address: 127.0.0.1
zone:
name: cd-z1
server:
port: 8089
spring:
application:
name: api-passenger-zone
eureka:
client:
register-with-eureka: true
fetch-registry: true
prefer-same-zone-eureka: true
region: chengdu
availability-zones:
chengdu: z2,z1 # z2空间优先 z2放前面
service-url:
z2: http://127.0.0.1:8021/eureka,http://127.0.0.1:8022/eureka
z1: http://127.0.0.1:8011/eureka,http://127.0.0.1:8012/eureka
instance:
lease-renewal-interval-in-seconds: 30
# 空间 z2
metadata-map:
zone: z2
appname: api-passenger-zone
prefer-ip-address: true
ip-address: 127.0.0.1
zone:
name: cd-z2
client-consumer配置
# 应用名称
spring:
application:
name: eureka-client
server:
port: 8000
eureka:
client:
region: chengdu
availability-zones:
chengdu: z1,z2 # z1空间优先,z1放前面
service-url:
z1: http://127.0.0.1:8011/eureka,http://127.0.0.1:8012/eureka
z2: http://127.0.0.1:8021/eureka,http://127.0.0.1:8022/eureka
instance:
lease-renewal-interval-in-seconds: 30
# z1空间
metadata-map:
zone: z1
# 应用名称
spring:
application:
name: eureka-client
server:
port: 9000
eureka:
client:
region: chengdu
availability-zones:
chengdu: z2,z1 #z2优先,z2放前面
service-url:
z2: http://127.0.0.1:8021/eureka,http://127.0.0.1:8022/eureka
z1: http://127.0.0.1:8011/eureka,http://127.0.0.1:8012/eureka
instance:
lease-renewal-interval-in-seconds: 30
# z2空间
metadata-map:
zone: z2
EurekaClient
只会向第一个url注册,只会从第一个url拉取,为避免第一个url压力过大,所以向Eureka Clien 要把后面的url随机打乱
Eurek注册只会重试3次(可配置)