上一篇笔记写到的两个问题,Netfilx-Eureka和Ribbo会给我们一一解决。如果有看过dubbo 应该知道zookeeper,Eureka的角色就是zookeeper。
Eureka和Zookeeper主要都是作为微服务的注册和发现。
Eureka和Zookeeper不一样的是,Zookeeper是作为一个单独的应用程序,而Eureka是直接嵌套在SpringCloud当中的。所以 Eureka Server本身就是一个SpringBoot的项目。
一、服务注册和发现概念
上面的图是我网上找的。由于微服务的地址变革可能会影响很大,所以就引入了服务注册中心的角色。所有服务提供者必须将IP地址或者主机名注册到服务器注册中心当中,我们使用注册中心就是Eurika Server。服务的消费者在调用服务器之前,先要到服务注册中心查询服务提供者的网络地址信息,然后进行调用,这个原理非常相似于我们的DNS。获得服务提供者的网络地址的操作,我们称为服务发现。服务注册中心一般提供一下功能组件:
1、服务注册表:记录各个微服务的信息,例如名称、网络地址、端口号等。服务器注册表提供查询和管理的API,用于管理和查询可用的微服务实例。
2、服务注册和发现:刚刚已经说过了,微服务在启动时,将自己的信息注册到服务器注册中心当中。在消费者可以通过服务注册中心发现服务实例。
3、服务检查:检查微服务的健康情况,如果微服务实例在一段时间没有心跳信息,就会从服务实例注册表中移除该实例。
二、Eureka
Eureka是Netfilx开源的服务发现组件,本身是基于REST的服务。包含Server和Client两个部分。SpingCloud 将其集成到SpringCloud Netflix子项目中。
Eureka有两个概念分别 Region和Availability Zone。他们都是Amazon AWS的概率。其中,Region表示AWS中的地理位置,每个Region都有多个Availability Zone,各个Region之间完全隔离。
Spring Cloud 默认使用的Region是us-east-1,在非AWS环境下,可以将Availability Zone理解成机房,将Region理解为夸机房的Eureka集群。
三、Eureka Server 和 Client
Eureka Server 提供服务器的发现的能力,各个微服务启动时,都会在Eureka Server中注册自己的信息,Eureka Server 会将这些信息存储到注册表当中。
Eureka Client 这个就是一个JAVA客户端,用于简化于Eureka Server的交互。当然如果你的微服务是跨平台的,各个服务存在不同的开发语言,那就只能通过REST的方式去与Eureka Server进行交互了。REST不是我们的重点,因为我们作为一个JAVA狗,Eureka Client足够简化我们的操作。
以下是笔者的从参考书中摘录的重点概念:
1、微服务启动后,会周期性向Eureka Server 发送心跳信息默认是30秒。如果Eureka在一定的时间内(默认90秒)无法获得实例的心跳信息,Eureka Server 将会注册该实例。
2、默认情况下,Eureka Server 同时也是Eureka Client。多个Eureka Server实例,互相之间通过复制的方式,来实现服务注册表的同步。
3、Eureka Client 会缓存服务注册表中的信息。这样有非常多的好处,微服务无需每次都请求查询Eureka Server,从而降低Eureka Server的服务压力和网络压力;即使所有Eureka Server都挂了,Eureka Client还是可以根据自己的缓存注册表自给自足。
四、动手编写Eureka Server
笔者此事已经开了第三个IDE了,我的MacBook的内存开始吃不消了。建议这些还是在内存比较大的计算机上进行测试,而且同时去编译和加载会导致非常耗时间。唉··· 一会还要再加多一个Eureka Server 做高可用,呵呵!搞事情!
在Maven上我们的依赖配置如下:
然后在配置类当中打上@EnableEurekaServerorg.springframework.cloud spring-cloud-starter-eureka-server
@SpringBootApplication @EnableEurekaServer public class EurekaserveraApplication { public static void main(String[] args) { SpringApplication.run(EurekaserveraApplication.class, args); } }最后配置一下我们的application.properties
server.port=8761 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://lcoalhost:8761/eureka/
端口就不说了看看其他配置是什么回事:
1、register-with-eureka 这个配置是否将自己的注册到Eureka Server当中,默认true。由于当前应用就是Eureka Server 所以为false
2、fetch-registry 表示是否从Eureka Server 中获得注册信息,默认为true。目前是单点Eureka Server 所以不需要和其他Eureka Server 节点做同步,所以为false
3、service-url.default-zone 设置与Eureka Server交互的地址,查询服务和注册服务都依赖这个地址。如果多个地址即多个Eureka Server节点需要用“,”分隔。默认是http://localhost:8761/eureka
配置完成,跑起!
需要注意的是,笔者之前是使用最新的SpringCloud版本1.5.4.RELEASE 但是实际当我打开页面的过程当中发现无法打开上面的UI界面,只是有一个XML。笔者在查找了很多网上的资料后,也无法解决。但是在stackoverflow中我找到了一个帖子,说将其降级到1.5.2.RELEASE就正常出现页面了。至于1.5.4为什么不可以显示,可能后续我会有所补充。但是1.5.4其他的功能还是正常可以使用的。
改成:org.springframework.boot spring-boot-starter-parent 1.5.4.RELEASE
org.springframework.boot spring-boot-starter-parent 1.5.2.RELEASE
五、Eureka Server 高可用
Eureka Server作为一个服务注册中心当然是不允许出现单点异常的,所以Eureka Server 做高可用是一个必须的操作。上面已经介绍过Eureka Server高可用的理论了,现在就操作一下。环境上笔者就不搞两台设备来做了,笔者会在hosts文件当中添加两条记录,表示当前的两台计算机的主机名:
127.0.0.1 eurekaservera eurekaserverb
然后在同一个项目当中配置两个profile,然后通过不同的profile分别代表eureka-servera、eureka-serverb的配置,然后分别启动。不清楚profile的可以看看我之前的SpringBoot的笔记SpringBoot系列(5)---SpringBoot-Web和SpringBoot基础 搜索profile即可。
application-servera.properties文件配置如下:
server.port=8761 eureka.instance.hostname=eurekaservera eureka.client.service-url.defaultZone=http://eurekaserverb:8762/eureka/端口号8761 实例名称的主机名称为 eurekaservera。 register-with-eureka 和 fetch-registry 默认为true 所以直接不用配置 default-zone设置为其他的eureka server 这里设置是eureka-serverb 由于是同一台计算机 所以我用了不同的端口号。
application-serverb.properties文件配置如下:
server.port=8762 eureka.instance.hostname=eurekaserverb eureka.client.service-url.defaultZone=http://eurekaservera:8761/eureka/
就算端口号成8762 然后实例主机名称改成 eureka-serverb 然后设置default-zone为刚刚设置的eureka-servera
然后application.properties 修改profile.active 分别启动
spring.application.name=tony-mall-discovery-eureka-ha spring.profiles.active=servera
可以见到UI界面上出现如下的信息:
高可用已经配置生效了!
六、Eureka Cilent
说了这么久Eureka Server 连高可用都配置了,总得要用起来吧··· Eureka Client 真的非常简单。你机会除了简单的配置,你就感觉不到它的存在了。
我们分别在 用户服务系统 和 商品服务系统 添加相应的Eureka Client 配置 主要为了将自身信息随SpringBoot项目开启 注册到 Eureka Server当中。
用户服务系统的application.properties 文件如下:
server.port=8801 server.context-path=/user spring.datasource.url=jdbc:mysql://localhost:3306/shop_mall?charset=utf8mb4 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=tonyyan spring.application.name=tony-mall-provider-user eureka.client.service-url.defaultZone=http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/ eureka.instance.prefer-ip-address=trueservice-url.defaultZone 我们将两个Eureka Server 都注册了。当然你可以注册一个,然后通过指定的Eureka Server 自动获得其他Eureka Server,同样也可以实现高可用。但是最好也是配上,如果正好你指定的那台Eureka Server 挂了,就呵呵了。
prefer-ip-address 为true 这里是代表注册自身的ip地址 而不是主机名称。
商品服务系统的application.properties 文件如下,跟用户差不多:
server.port=8802 server.context-path=/product spring.datasource.url=jdbc:mysql://localhost:3306/shop_mall?charset=utf8mb4 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=tonyyan spring.application.name=tony-mall-provider-product eureka.client.service-url.defaultZone=http://eurekaservera:8761/eureka/,http://eurekaserverb:8762/eureka/ eureka.instance.prefer-ip-address=true
最后在两个项目的配置类当中分别打上@EnableDiscoveryClient的 annotation
@SpringBootApplication @EnableDiscoveryClient public class ProductsystemApplication {
@SpringBootApplication @EnableDiscoveryClient public class UsersystemApplication {
将两个微服务启动,并查看Eureka Server 上的实例信息:
如果你配置了HA,你会发现两台注册中心都会出现同样的实例列表。OK!现在都配置好了,可以调用测试~
@GetMapping("/getProduct/{productId}/userId/{userId}") public Map可以发现调用其实都一样,只不过URL 从IP地址改成了实例名称了。其实这里就是一个虚拟主机名称。最后测试通过~ 我这里就不贴出返回的JSON了。, Object> getProduct(@PathVariable int productId, @PathVariable int userId) { Map , Object> map = new HashMap<>(); String url = "http://tony-mall-provider-user/user/getUserById/" + userId; User user = this.restTemplate.getForEntity(url, User.class).getBody(); Product product = this.productRepository.getByProductId(productId); map.put("user",user); map.put("product",product); return map; }
六、Eureka Cilent 元数据和实例信息
Eureka Client 还可以获得其他实例的信息:
@GetMapping("/showProvider") public ListshowInfo(){ return this.discoveryClient.getInstances("tony-mall-provider-user"); }
返回如下的JSON:
[
{
"host": "192.168.28.100",
"port": 8801,
"uri": "http://192.168.28.100:8801",
"serviceId": "TONY-MALL-PROVIDER-USER",
"metadata": {},
"secure": false,
"instanceInfo": {
"instanceId": "192.168.28.100:tony-mall-provider-user:8801",
"app": "TONY-MALL-PROVIDER-USER",
"appGroupName": null,
"ipAddr": "192.168.28.100",
"sid": "na",
"homePageUrl": "http://192.168.28.100:8801/",
"statusPageUrl": "http://192.168.28.100:8801/info",
"healthCheckUrl": "http://192.168.28.100:8801/health",
"secureHealthCheckUrl": null,
"vipAddress": "tony-mall-provider-user",
"secureVipAddress": "tony-mall-provider-user",
"countryId": 1,
"dataCenterInfo": {
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo",
"name": "MyOwn"
},
"hostName": "192.168.28.100",
"status": "UP",
"leaseInfo": {
"renewalIntervalInSecs": 30,
"durationInSecs": 90,
"registrationTimestamp": 1500002506247,
"lastRenewalTimestamp": 1500002506247,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1500002506248
},
"isCoordinatingDiscoveryServer": false,
"metadata": {},
"lastUpdatedTimestamp": 1500002506248,
"lastDirtyTimestamp": 1500002506142,
"actionType": "ADDED",
"asgName": null,
"overriddenStatus": "UNKNOWN"
}
}
]
除了一些服务信息之外,还看到了这个属性metadata 这个是元数据,又服务提供者提供。以下我们尝试在用户服务系统的application.properties中添加元数据:
eureka.instance.metadata-map.developer=TONY eureka.instance.metadata-map.provider_city=GZ重新调用我们写的showProvider接口,发现metadata已经添加相应的元数据信息:
。。。省略。。。
"isCoordinatingDiscoveryServer": false,
"metadata": {
"provider_city": "GZ",
"developer": "TONY"
},
"lastUpdatedTimestamp": 1500003756538,
。。。省略。。。
七、Eureka Server 的自我保护模式
以下摘录于《SpringCloud和Docker微服务架构实战》
默认情况下,如果Eureka Sever在一定时间内没有接收到某个微服务实例的心跳信息,Eureka Server 会直接注销该实例。但是当网络分区发生故障时,微服务与Eureka Server之间无法通信,直接注销微服务实例就会非常危险。服务本身而言是健康的,不应该直接注销微服务。
Eureka Server 通过 自我保护模式 来解决这个问题。当Eureka Server 节点在短时间内丢失多个客户端时,那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server 就会保护服务注册表的信息,不会再删除注册表信息(不会注销实例信息)。当网络故障恢复后,该Eureka Server 节点会自动退出自我保护模式。
自我保护模式是一种应对网络异常的安全保护措施。
在Spring Cloud中,可以使用以下启用或禁用安全保护措施
eureka.server.enable-self-preservation=false
默认为true
八、多网卡环境选择IP
对于多个网卡的服务器,指定IP的需求非常常见。例如某台服务器有多张网卡,但是只有eth1可以被其他服务器访问;如果Eureka Client将其他网卡的IP注册到Eureka Server上,其他服务消费者将无法方位服务提供实例的服务。
各个微服务注册到Eureka Server 上的IP可以有以下操作进行指定:
忽略指定网卡:
eureka.instance.prefer-ip-address=true spring.cloud.inetutils.ignored-interfaces=docker0
使用正则表达式指定使用的网络地址:
spring.cloud.inetutils.preferred-networks=192.168
我还是最喜欢这个方式,直接指定IP地址,简单粗暴:
eureka.instance.ip-address=127.0.0.1
只使用站点的本地地址
spring.cloud.inetutils.use-only-site-local-interfaces=true
九、服务健康检查
SpringBoot Actuator 提供了很多监控REST端点。方位这些断点就可以获得相应的服务运行状况。
以下是SpringBoot Actuator 提供的端点 摘录于SpringBoot官方文档
ID | Description | Sensitive Default |
---|---|---|
|
Provides a hypermedia-based “discovery page” for the other endpoints. Requires Spring HATEOAS to be on the classpath. |
true |
|
Exposes audit events information for the current application. |
true |
|
Displays an auto-configuration report showing all auto-configuration candidates and the reason why they ‘were’ or ‘were not’ applied. (显示自动配置信息) |
true |
|
Displays a complete list of all the Spring beans in your application. (显示所有Bean信息) |
true |
|
Displays a collated list of all |
true |
|
Performs a thread dump. (显示线程活动快照) |
true |
|
Exposes properties from Spring’s |
true |
|
Shows any Flyway database migrations that have been applied. |
true |
|
Shows application health information (when the application is secure, a simple ‘status’ when accessed over an unauthenticated connection or full message details when authenticated).(显示应用程序的健康指标,这些值由HealthIndicator的实现类提供) |
false |
|
Displays arbitrary application info.(显示应用程序的新,可以用info.*属性自定义info端点公开的数据) |
false |
|
Shows and modifies the configuration of loggers in the application. (显示和修改Logger配置) |
true |
|
Shows any Liquibase database migrations that have been applied. |
true |
|
Shows ‘metrics’ information for the current application.(显示应用程序的度量标准信息) |
true |
|
Displays a collated list of all |
true |
|
Allows the application to be gracefully shutdown (not enabled by default). (关闭应用 默认不启用,如果启用需要配置endpoints.shutdown.enabled=true) |
true |
|
Displays trace information (by default the last 100 HTTP requests).(显示跟踪信息,默认情况下为最近100个HTTP请求) |
true |
之后的笔记也会有相关Actuator的介绍,先整合到SpringCloud当中:
然后就没有然后了,非常简单~ 添加个依赖就将actuator整合到Spring当中。当我们启用了Eureka之后 health接口会出现如下信息:org.springframework.boot spring-boot-starter-actuator
http://localhost:8802/product/health
{
"description": "Remote status from Eureka server",
"status": "DOWN"
}
注销所有Eureka配置之后重新访问http://localhost:8802/product/health获得如下JSON
{"status":"UP"}
info接口会出现如下信息:
http://localhost:8802/product/info
{}
info默认没有返回任何数据,需要我们配置info属性来定义info公开的端点数据。
info.app.name=@project.artifactId@ info.app.encoding=@project.build.sourceEncoding@ info.app.java=@java.version@
再次调用接口:http://localhost:8802/product/info
{
"app": {
"name": "productsystem",
"java": "1.8.0_121",
"encoding": "UTF-8"
}
}
事实上Eureka 已经集成了对实例的健康检查
Status 还有其他取值:DOWN、OUT_OF_SERVICE 、UNKNOWN、UP
当Eureka Server 和 Eureka Client 之间使用心跳机制来确定Eureka Client 的状态。默认情况下,如果Server 和Client 的心跳保持正常,应用程序就会始终保持UP状态。
如果微服务器的数据源发现了问题,根本无法工作。我们希望Eureka Server 可以获得 Client 的SpringBoot Actuator 提供的health端点,可以在Client客户端当中的application.properties配置如下参数:
eureka.client.healthcheck.enable=true