Eureka解决了什么问题 这个问题本质是问服务注册中心的价值。很多年前,那个时候微服务还没有兴起,大家用Nginx+Tomcat就搞定了,加机器加Tomcat时候,直接在线改Nginx配置就搞定,这叫手动服务注册。但是在现今的微服务架构中,服务可能动不动就几十个,上百个,算上冗余就几百个,而且微服务的发布很频繁,不断有新的服务加入进来;在应对大流量场景时,还需要扩容缩容。这如果还依靠Nginx那种原始的手工配置方式,是不可行的,成本太高了。所以,就出现了服务注册发现,即服务实例自己会在启动后注册到服务注册中心,这叫服务注册,然后每个服务实例又从服务注册中心拉取服务注册表,动态的感知到服务的上下线,这叫服务发现。所以,服务注册中心这种技术是在微服务架构中自然而然就过渡过来的,并不突兀。eureka作为Netflix全家桶中的服务注册中心实现,其意义也就不言自明了。
快速体验Eureka 1、构建一个Eureka Server 创建一个maven工程,加入如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.2.1.RELEASE</version > </parent > <properties > <maven.compiler.source > 8</maven.compiler.source > <maven.compiler.target > 8</maven.compiler.target > <spring.cloud-version > Hoxton.RELEASE</spring.cloud-version > </properties > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-client</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-server</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-ribbon</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > ${spring.cloud-version}</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement >
创建一个application.yml
文件,配置如下内容:
1 2 3 4 5 6 7 server: port: 8761 eureka: client: register-with-eureka: false fetch-registry: false
#1 不要将此服务注册到eureka。因为当前服务已经是eureka server了
#2 不要拉取服务注册表。
创建一个启动类:
1 2 3 4 5 6 7 8 9 10 @Slf 4j@EnableEurekaServer @SpringBootApplication public class EurekaServer { public static void main (String[] args) { SpringApplication.run(EurekaServer.class, args); log.info("eureka server 启动成功" ); } }
启动后,见到如下输出即为成功:
2、开发一个服务提供者 1 2 3 4 5 6 7 8 @RestController public class ServiceAController { @GetMapping ("/sayHello/{name}" ) public String sayHello (@PathVariable("name" ) String name) { return "hello," + name; } }
1 2 3 4 5 6 7 8 9 @Slf 4j@EnableEurekaClient @SpringBootApplication public class ServiceAApplication { public static void main (String[] args) { SpringApplication.run(ServiceAApplication.class, args); log.info("ServiceA 启动成功" ); } }
创建一个application.yml
文件,配置如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 server: port: 8080 spring: application: name: serviceA eureka: instance: hostname: loacalhost client: service-url: defaultZone: http://localhost:8761/eureka
3、开发一个服务调用者 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @RestController @Configuration public class ServiceBController { @Bean @LoadBalanced public RestTemplate getRestTemplate () { return new RestTemplate(); } @Resource private RestTemplate restTemplate; @GetMapping ("/greeting/{name}" ) public String greeting (@PathVariable("name" ) String name) { return restTemplate.getForObject("http://serviceA/sayHello/" + name, String.class); } }
1 2 3 4 5 6 7 8 9 10 @Slf 4j@EnableEurekaClient @SpringBootApplication public class ServiceBApplication { public static void main (String[] args) { SpringApplication.run(ServiceBApplication.class, args); log.info("ServiceB 启动成功" ); } }
创建一个application.yml
文件,配置如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 server: port: 9000 spring: application: name: serviceB # eureka client config eureka: instance: hostname: loacalhost client: service-url: defaultZone: http:
启动服务
4、进行实验 浏览器访问localhost:8761
打开Eureka注册服务控制台,可发现ServiceA和B都注册到Eureka Server上了
在浏览器访问http://localhost:9000/greeting/jack
,就可以看到结果了
5、画图讲解Hello Word原理
服务A和B的启动类上都有@EnableEurekaClient
注解,此注解会将服务变为Eureka客户端应用,将自己注册到Eureka Server上
服务B和服务A都会从Eureka Server拉取服务注册表
服务B发起调用服务A的请求时,Ribbon根据拉取到本地的服务注册表,将服务名替换为hostname+port并发起服务调用
Eureka集群实战 1、eureka注册中心集群 eureka如此重要的服务注册中心,肯定不可能是单点的。下面我们来构建一个eureka server集群。
分别使用如下配置各启动一个eureka server,组成一个集群。
1 2 3 4 5 6 7 8 9 10 11 server: port: 8762 eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://peer2:8761/eureka instance: hostname: peer2
1 2 3 4 5 6 7 8 9 10 11 server: port: 8761 eureka: client: register-with-eureka: false fetch-registry: false service-url: defaultZone: http://peer2:8762/eureka instance: hostname: peer1
小技巧:可以同一个工程,分别启动一次即可。
如果是IDEA
的话,可能不让你同时启动两个,按照如下配置一下即可,在Allow parallel run
前打上勾即可
随意访问一个eureka控制台,可发现两个节点已经组成集群。DS Replicas指的是当前eureka server的副本
小提示:假如访问peer2节点,会发现没有服务实例。不用着急,下面我们改造后就OK了。
2、将服务改造为注册到eureka server集群 分别将服务A和B的配置改为如下这样,变更部分为最后一行
1 2 3 4 5 6 eureka: instance: hostname: localhost client: service-url: defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka
改完重启后访问eureka控制台页面即可看到都有服务实例了。
服务健康自检机制 默认情况下,你的所有服务,比如服务A和服务B,都会自动给eureka注册中心同步心跳,续约。如果eureka server一段时间内没有感知到某个服务的心跳,就会把那个服务下线。
1、基于spring-boot-actuator
的服务健康检查 服务加入如下依赖
1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency >
重启后访问http://localhost:port/actuator/health
可以看到返回的服务状态,默认是UP
2、spring-cloud-eureka
整合spring-boot-actuator
实现自定义服务健康检查 1 2 3 4 5 6 7 8 9 10 11 12 13 @Component public class ServiceAHealthIndicator implements HealthIndicator { @Override public Health health () { return new Health.Builder(Status.UP).build(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Component public class ServiceAHealthCheckHandler implements HealthCheckHandler { @Autowired private ServiceAHealthIndicator indicator; @Override public InstanceInfo.InstanceStatus getStatus (InstanceInfo.InstanceStatus currentStatus) { Status status = indicator.health().getStatus(); return InstanceInfo.InstanceStatus.UP; } }
eureka client会定时调用getStatus
方法来判断服务实例的状态,如果状态变化了,就会通知eureka server。当服务实例挂掉了,那么eureka server就会感知到,然后下线这个服务实例。
3、生产经验分享 一般情况下很少使用这第二种方式自定义健康状态检查器,原因如下:
对于eureka eureka默认是client通过心跳与eureka server保持心跳通信,如果心跳不及时或者没有心跳了,那么就说明那个服务挂了,然后eureka server就会摘除这个服务实例,这个机制足够用了
对于服务 在大规模部署中,每个服务都很复杂,不可能去一个个搞一堆健康检查的。大部分情况下,我们就是会对外暴露一个/health接口,然后专门外部定时调用各个服务的/health接口来判断当前这个服务状态就足够了。
Eureka常见配置 心跳检测 eureka客户端,默认每隔30秒发送一次心跳到eureka server(以后简称注册中心)。
1 2 3 4 5 6 eureka: instance: # 配置eureka client与注册中心心跳间隔时间,默认是30秒。在eureka中,心跳被称为续约(renew) lease-renewal-interval-in-seconds: # 配置如果多少秒内没有收到一个服务实例的心跳,就摘除(expiration 失效)这个服务,默认是90秒。 lease-expiration-duration-in-seconds:
一般情况下,这俩参数建议不要修改
注册表抓取 默认情况下,客户端每隔30秒去注册中心抓取最新的注册表,然后缓存到本地。通过下面参数可修改时间间隔
1 2 3 4 eureka: client: registry-fetch-interval-seconds:
自定义元数据 通过下面的metadata-map定义服务的元数据,反正就是你自己需要的一些东西,不过一般挺少使用的
1 2 3 eureka: instance: metadata-map:
自我保护模式 如果在eureka控制台看到下面这样的东西:
这就是eureka进入了自我保护模式。如果客户端的心跳失败超过了一定的比例,或者说在一定时间内(默认15分钟)接收到的服务续约低于85%,那么就会认为是自己网络故障了,这个时候会导致人家client无法发送心跳,如果不加以控制,eureka服务实例摘除机制会将实例一个个的下掉。为了避免这种情况的发生,eureka这个时候会一种保护模式,保护服务注册表,不会立即把失效的服务实例摘除。在测试的时候一般会关闭 这个自我保护模式:
1 2 3 eureka: server: enable-self-preservation: false
总结 本篇博客,我们已经初步体会如何使用eureka,剖析了eureka的常见配置,后续我们马上进入源码剖析环节。