SpringCloud Netflix Eureka
Eureka是Netflix开源的服务发现组件,基于REST,SpringCloud将它集成在子项目Spring Cloud Netflix中,从而实现服务的注册、发现。
Eureka包含Server、Client两部分:
- Eureka Server 接收服务注册、保存各服务节点的信息
- Eureka Client 即各服务节点,内置Ribbon(实现负载均衡)。消费者通过Ribbon从提供该服务的节点列表中确定一个要使用的节点。
Eureka的架构
搭建单个Eureka Server
1、创建空项目,作为容器
因为微服务是独立的模块,单独开发部署,不建议使用父子工程。
2、新建模块eureka-server,作为eureka服务器
(1)pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> <version>2.2.1.RELEASE</version> </dependency> </dependencies>
(2)application.properties
### eureka server config ### #使用的端口 server.port=9001 #服务名,eureka集群时会作为一个服务注册到其它server上 #spring.application.name=eureka-server #这个eureka server的ip,不是预定义的key,下面要取出来用。上线时要改为真实的ip eureka.server.ipAddress=127.0.0.1 #erueka实例名,注册到eureka server上时,这个名称就代表该服务节点。集群使用,只有一个server时可不用 #eureka.instance.instance-id=${eureka.server.ipAddress}:${server.port} #这个eureka server的注册中心地址 eureka.client.serviceUrl.defaultZone=http://${eureka.server.ipAddress}:${server.port}/eureka/ #是否将自己作为Eureka Client注册到其它Eureka Server上,默认为true。因为只有一个eureka server,所以设置为false eureka.client.register-with-eureka=false #是否同步其它Eureka Server节点的数据(复制),默认为true。因为只有一个eureka server,所以设置为false eureka.client.fetch-registry=false #注册时以ip地址注册,默认以主机名注册。如果注册主机名,使用时还需要转换为ip,浪费时间 eureka.instance.prefer-ip-address=true #期待心跳间隔时间,默认30s #eureka.instance.lease-renewal-interval-in-seconds=30 #服务节点失效时间,默认为90s,即90s内没有接收到某个服务节点的心跳,就认为该节点失效 #eureka.instance.lease-expiration-duration-in-seconds=90 ###调试时可把上面2个值写小些### #是否启用服务的自我保护模式,默认为true,调试时设置为false,正式部署设置为true eureka.server.enableSelfPreservation=false#清理间隔,每隔60s清理一次无效节点,默认为60000(60s)#eureka.server.eviction-interval-timer-in-ms=60000 #设置缓存过期时间。eureka集群时,要从其它server拉取数据,此设置指定拉取的数据的有效期,即每隔多少秒拉取、更新一次 #注意是缓存,是从其他server拉取的,不是本机的注册中心的节点信息,默认180s更新一次 eureka.server.response-cache-auto-expiration-in-seconds=180 #数值设置大,可用性高,因为其它server下线,本server也可以维持某些节点一段时间,但节点信息更新慢,数据一致性差
(3)引导类
@SpringBootApplication @EnableEurekaServer //作为Eureka Server public class EurekaServer { public static void main(String[] args) { SpringApplication.run(EurekaServer.class, args); } }
启动之后,在 http://localhost:9001/ 查看eureka server的信息。
3、新建子模块order-server(服务提供者),作为eureka client
1、pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> </parent> <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> <version>2.0.0.RELEASE</version> </dependency> </dependencies>
2、application.properties
### eureka client config### #使用的端口 server.port=10001 #提供的服务,服务名称 spring.application.name=order-server #注册到哪些Eureka Server的注册中心。可配置多个值,逗号隔开即可 eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9001/eureka/ #本机地址,上线时要改为机器实际的ip eureka.client.ipAddress=127.0.0.1 #eureka实例名,以ip:port的形式注册到server上 eureka.instance.instance-id=${eureka.client.ipAddress}:${server.port} #注册节点时IP优先,默认为false——注册主机名 eureka.instance.prefer-ip-address=true #多久发送一次心跳,默认30s,调试时可设置短些 #eureka.instance.lease-renewal-interval-in-seconds=30
3、引导类
@SpringBootApplication @EnableEurekaClient //作为Eureka Client public class OrderServer { public static void main(String[] args) { SpringApplication.run(OrderServer.class, args); } }
4、我们再写一个controller作为测试
@Controller @RequestMapping("/order") public class OrderController { //模拟根据用户id查找该用户的所有订单 @GetMapping("/{user_id}") @ResponseBody public String findAllByUserId(@PathVariable Integer user_id){ return "this is orderList of " + user_id ; } }
4、新建子模块user-server(服务消费者),作为eureka client
1、pom.xml
都是eureka的客户端,引入的依赖和order-server的相同。
2.application.properties
都是eureka客户端,配置和order-server差不多,改一下端口号、服务名称就ok。(实际上线时还需要改ip)
3、引导类
@SpringBootApplication @EnableEurekaClient //作为Eureka Client public class UserServer { @Bean @LoadBalanced //使用Ribbon的负载均衡 public RestTemplate getRestTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(UserServer.class, args); } }
消费者、提供者相比,消费者要使用Ribbon的负载均衡。
4、再写一个controller作为测试
@Controller @RequestMapping("/user") public class UserController { //springcloud使用的消息是REST,通过RestTempalte来调用服务 private RestTemplate restTemplate; @Autowired public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } //模拟根据用户id查找用户所有订单 @GetMapping("/order/{user_id}") @ResponseBody public String findOrdersByUserId(@PathVariable Integer user_id){ //不写具体的ip:port,使用服务名代替,Ribbon会通过负载均衡找到该服务的合适的节点 String orders = restTemplate.getForObject("http://order-server/order/{user_id}", String.class, user_id); return orders; } }
启动order-server、user-server,看到已经注册到eureka-server上,
即使关闭eureka的自我保护机制,服务下线后需要90s才会删除节点信息,所以有时候会看到重复的节点信息。
页面上经常 红字提示 “自我保护机制.....“,不必管。
地址栏输入 http://localhost:10002/user/order/1 ,显示“this is orderList of 1”,搭建成功。
Eureka的集群
为了提供可用性,Eureka一般都要集群。
1、eureka server集群
application.properties修改如下:
### eureka server config ### #使用的端口 server.port=9001 #服务名,eureka集群时会作为一个服务注册到其它server上 spring.application.name=eureka-server #这个eureka server的ip,上线时要改为真实的ip eureka.server.ipAddress=127.0.0.1 #erueka实例名,以实例名注册到eureka server上。 eureka.instance.instance-id=${eureka.server.ipAddress}:${server.port} #要写其它注册中心的地址,有多少写多少 eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9002/eureka/,http://127.0.0.1:9003/eureka/ #把自己作为一个服务(eureka client)注册到其它Eureka Server上,默认就是true eureka.client.register-with-eureka=true #同步其它Eureka Server节点的数据(复制),默认为true eureka.client.fetch-registry=true #注册时优先以ip地址注册 eureka.instance.prefer-ip-address=true #多久更新一次从其它server拉取的数据 eureka.server.response-cache-auto-expiration-in-seconds=180 ##是否启用此Eureka Server的自我保护模式,默认为true,调试时设置为false,正式部署设置为true##eureka.server.enableSelfPreservation=false#清理间隔,每隔60s清理一次失效的节点,默认为60000#eureka.server.eviction-interval-timer-in-ms=60000 #期待心跳间隔时间,默认30s #eureka.instance.lease-renewal-interval-in-seconds=30 #服务节点失效时间,默认为90s,即90s内没有接收到某个服务节点的心跳,就认为该节点失效 #eureka.instance.lease-expiration-duration-in-seconds=90 ###调试时可把上面2个值写小些###
要部署多少个集群,就copy多少份这个子模块,把application.properties改下就ok。
如果是正式部署,部署到多台机器上,需要修改副本的ip、注册中心地址;
如果是在同一台机器上测试,直接运行此模块|项目的多个实例即可,修改下端口号(包括注册中心的端口号)就ok。
2、eureka client集群
要部署多少个集群,就copy多少份这个子模块,把application.properties改下就ok:
#注册到哪个注册中心,需配置多个注册中心,逗号隔开 eureka.client.serviceUrl.defaultZone=http://127.0.0.1:9001/eureka/,http://127.0.0.1:9002/eureka/,http://127.0.0.1:9003/eureka/
虽然配置了多个注册中心,但只会注册到第一个,第一个下线了才会选择注册到第二个,以此类推。(后面的全是备胎)
这样做是为了提高可用性,某些注册中心挂了,也没问题。
如果是正式部署到多台机器,改下ip就ok;
如果是在一台机器上进行测试,在IDEA下运行多个实例,改下端口号就行。
ps:可能指定的注册中心还没上线,控制台会报错“请求失败,拒绝连接”,问题不大,等注册中心上线即可。(注意灯是绿的,仍是运行状态;如果没在运行了,那就是其它错误)
微服务项目的部署
将各子模块打包为jar或war(基于SpringBoot,即可打包为jar,又可打包为war),安装、部署到各台机器上即可。
Eureka server 服务的自我保护模式
eureka server默认是开启服务的自我保护模式的。
90s内未接受到某个服务节点的心跳,就开启为期15min的心跳检测,如果15min接收到该节点的心跳小于85%(默认值0.85,可设置),就认为是网络故障导致的,一直保留该节点的信息,依然被使用;
如果大于85%,就认为是该节点自身除了问题,删除该节点的信息。
造成的问题:该节点可能出问题了(成为无效节点),但依然和正常节点一样被使用,就是说返回给client的可能含有一些无效节点。
关闭服务保护、手动清理无效节点:(90s内没收到过心跳即为无效节点)
# 关闭服务的自我保护 eureka.server.enableSelfPreservation=false # 清理间隔,每隔60s清理一次失效的节点 eureka.server.eviction.interval-timer-in-ms=60000
商业项目中不推荐关闭服务保护,因为网络不可靠很容易造成网络波动、延迟、断线。
在商业项目中,服务的数量一般是几十个、成百上千个,如果关闭服务保护,可能导致大量的服务反复注册、删除、再注册,会降低程序性能。
Eureka client的缓存机制
客户端会缓存eureka server返回的服务列表,缓存一直有效,直到此客户端下线。
第一次请求eureka server获取到服务列表后,之后调用该服务时都是从缓存中找。除非缓存的该服务的所有节点都失效,才会从server重新获取节点列表。
即便Eureka Server集群的所有节点都宕机失效,服务消费者、提供者依然能正常通信。