赞
踩
Eureka是Netflix公司开发的服务发现框架,SpringCloud将其集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。Eureka在微服务架构中扮演了关键角色,帮助解决服务之间的通信问题。
具体来说,Eureka通过服务注册与发现的机制,使得每个注册到Eureka的服务都可以在Eureka上找到其他注册的服务。服务提供者可以将自己的服务注册到Eureka服务器中,而服务消费者则可以从Eureka服务器中获取可用的服务实例列表。当服务消费者需要调用某个服务时,它可以根据负载均衡策略从Eureka提供的服务实例列表中选择一个实例进行请求转发。
此外,Eureka还提供了健康检查功能,确保只有健康的服务实例才会被注册和发现。当服务提供者宕机或下线时,Eureka服务器会自动将其从服务实例列表中移除,从而保证了服务的可用性和稳定性。
可以把Eureka分为客户端、服务端两部分,在非集群模式下,通常部署一个Eureka服务端(Eureka Server)来作为服务注册中心,同时可以有多个Eureka客户端(Eureka Client)来注册和发现服务。
Eureka客户端主要向Eureka服务端注册自己的IP地址和端口等信息,注意:Eureka的服务端也会把自己注册上去,也就是自己注册自己。
那么有了所有客户端的IP地址和端口等信息,自然也就可以实现调用功能了,Eureka主要使用RestFul风格进行服务之间的调用,Restful风格是一种基于HTTP协议的服务调用方式。
先来看看注册的大致流程:
客户端如果主动停掉了服务端还有这个客户端的信息吗?
当客户端主动停掉时,理想情况下,它会向Eureka Server发送一个注销请求,告知自己即将下线。这样,Eureka Server就能及时地从其注册表中移除该客户端的信息。然而,如果客户端因为某种原因异常终止,它可能无法发送这样的请求。因此,Eureka Server还依赖于其他机制来检测和处理这种情况。
如果客户端因为某种原因异常终止,服务端如何知道?
Eureka服务端会对注册表中所有的客户端进行一个健康检查,也可以叫做心跳机制,比如服务端每间隔十秒就会向客户端发起一个请求,客户端收到请求并回应,服务端收到客户端的回应就代表这个客户端还活着,如果接收不到回应那么就会将其注册信息删除。
健康检查的间隔时间是可以通过配置设置的,例如,通过配置eureka.instance.lease-renewal-interval-in-seconds
参数,可以设定客户端发送心跳信息的间隔时间。同样,服务端对于心跳请求的响应超时时间也是可配置的,以确保服务端能够及时处理客户端的心跳请求。这种机制确保了Eureka注册表中的信息始终是最新和准确的。
如果同一时间大量客户端都挂掉了,服务端会怎么做?
不会的。Eureka服务端会有一个阈值,比如这个阈值是80%,那么如果有80%的客户端在同一时间都没有响应服务端时Eureka服务端就不会采取删除方法,它会认为可能是网络问题或者其他问题导致自己收不到客户端的响应而采取其他措施(后面会讲到),这也确保了服务的可用性和稳定性。而Eureka服务端的这个阈值是可以通过配置自己控制的。
客户端每次通信都要先经过服务端再去找对应的其他客户端吗?
并不是。Eureka客户端通常会在启动时从Eureka Server拉取服务注册表的信息,并在本地缓存一份。这样,客户端在需要调用其他服务时,可以直接从本地缓存中获取服务信息,提高了通信效率。然而,这种缓存机制也需要注意缓存的更新和同步问题,以确保客户端获取的服务信息始终是最新的。在某些情况下,根据业务需求,客户端也可以选择实时查询Eureka Server来获取最新的服务信息。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>fan.demo</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-server</name> <description>eureka-server</description> <properties> <java.version>8</java.version> <spring-cloud.version>Hoxton.SR12</spring-cloud.version> </properties> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
@EnableEurekaServer
注解启动服务端。spring: application: name: eureka-server server: port: 8761 # eureka 服务端默认端口号 # eureka配置总共分为三大类:server、client、instance # eureka服务端配置,注意eureka服务端也属于客户端,它也会把自己注册上去。 eureka: server: # eureka检查实例是否存活的间隔时间,每隔10秒将认为不存活的实例剔除掉 eviction-interval-timer-in-ms: 10000 # 续约百分比,如果在检查实例是佛存活时,有百分之八十五的实例都认为不存活,则不进行剔除。 renewal-percent-threshold: 0.85 instance: hostname: localhost # eureka服务端保存实例的InstanceId展示格式,一般为:localhost:eureka-server:8761 instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port} prefer-ip-address: true # 将localhost解析为真是的IP地址,默认为 false # 作为实例,设置想服务端续约间隔时间,单位秒 lease-renewal-interval-in-seconds: 5
暂时我们只需要关注下面这一行展示的内容分别是什么,其他的可以先不关注:
spring.application.name: eureka-server
这个配置,只不过变成了全大写的。-[eureka-server:8761](http://192.168.0.108:8761/actuator/info)
表示的就是我们前面设置的instance-id
配置(截图中没有localhost是因为我去掉了)。当鼠标悬浮到上面还可以在左下角看到对应的IP地址和端口号,这是因为我们将prefer-ip-address:
设置为true的结果。<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>fan.demo</groupId> <artifactId>eureka-client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-client</name> <description>eureka-client</description> <properties> <java.version>8</java.version> <spring-cloud.version>Hoxton.SR12</spring-cloud.version> </properties> <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-test</artifactId> <scope>test</scope> </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> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring: application: name: eureka-client server: port: 8080 # eureka配置总共分为三大类:server、client、instance # eureka客户端配置。 eureka: client: fetch-registry: true # 是否从服务端拉取实例到本地,默认为 true registry-fetch-interval-seconds: 10 # eureka每隔10秒从服务端拉取一次实例自己本地,默认是30秒 register-with-eureka: true # 是否向服务端进行注册,默认为 true service-url: # 不写默认 http://localhost:8761/eureka defaultZone: http://localhost:8761/eureka instance: hostname: localhost # eureka服务端保存实例的InstanceId展示格式,一般为:localhost:eureka-server:8761 instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port} prefer-ip-address: true # 将localhost解析为真是的IP地址,默认为 false lease-renewal-interval-in-seconds: 10 # 作为实例,设置想服务端续约间隔时间,单位秒
@EnableEurekaClient
注解表示开启eureka客户端。到此,我们的客户端就已经写好了,然后再来看下eureka服务端页面展示:
eureka客户端成功注册到了服务端上。
既然现在eureka服务端上面已经注册两个服务了,因为服务端也是一个客户端嘛。那么我们现在就可以实现服务之间的调用了,具体做法呢?看下面代码例子:
@RestController public class TestController { @Autowired private DiscoveryClient discoveryClient; // 这是springCloud提供的,用来获取注册到eureka-server上的服务信息。 /** * 测试服务调用 * @param serviceName 首先需要一个服务名称,来获取该服务下的所有实例信息 * @param path 要调用的服务接口,因为我们不知道要调用哪个接口,所以需要告诉服务 * @return */ @GetMapping("test") public String test(String serviceName, String path) { // 用来进行http调用的对象 RestTemplate restTemplate = new RestTemplate(); List<ServiceInstance> instances = discoveryClient.getInstances(serviceName); // 因为我们目前eureka-server这个服务名称下仅有一个实例,所以直接取第一个即可。 ServiceInstance instance = instances.get(0); System.out.println("serviceName 下的实例信息:" + instance.toString()); String host = instance.getHost(); // 获取实例的ip地址 int port = instance.getPort(); // 获取端口号 // 拼接请求链接 String url = "http://" + host + ":" + port + "/" + path; // 发起get请求 String forObject = restTemplate.getForObject(url, String.class); return "调用" + serviceName + "服务的" + path + "接口,返回结果:" + forObject; } }
@RestController
public class TestController {
@GetMapping("test")
public String test()
{
return "eureka-client 200";
}
}
都写好以后,然后我们如果在eureka-server端需要调用eureka-client端对外提供的接口,就需要首先知道两个东西,一个是eureka-client端的服务名称,一个是需要调用的路径地址。
发起请求:http://localhost:8761/test?serviceName=eureka-client&path=test
请求结果:
需要注意的是,在实际使用中我们并不是以上面那种方式去进行服务之间的调用的,SpringCloud提供了Ribbon组件和OpenFegin组件,这两个组件任意一个都能实现服务调用以及负载均衡效果。
可以理解为Ribbon集成了Eureka,而OpenFegin集成了Ribbon,后面这两个组件以及源码都会讲到。
ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>
类型的 Map,它的第一个 key 对应的 value 还是一个 Map 对象,第二个 key 对应的 value 就是我们的服务列表。gMap.put(registrant.getId(), lease);
这一步就完成了服务的注册。其他的代码我们暂时都先不管,只关注我们看的懂得,到了这一步我们的服务注册就已经完成了。registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
向服务端发起了一个请求:leaseToCancel = gMap.remove(id);
从服务列表中根据实例ID直接删除,到此就完成了服务的下线操作。经历了一番Eureka的探险之旅,我们终于从它的迷雾中走出,手中握着满满的实战经验和源码秘籍。Eureka,这位微服务世界中的“红娘”,不仅帮我们牵线搭桥,让服务们相亲相爱,还通过其独特的机制保证了整个微服务家族的和谐稳定。
实战中,Eureka的易用性让我们不禁感叹:“哇,这也太简单了吧!”就像我们在微信上摇一摇就能找到附近的人一样,Eureka让我们的服务也能轻松找到彼此。而源码分析则让我们看到了Eureka背后的“黑科技”,那些复杂的算法和机制,就像魔术师的秘密道具,让Eureka能够如此高效、稳定地工作。
然而,就像所有伟大的魔术师一样,Eureka也不是万能的。随着微服务数量的增加,Eureka也开始面临压力和挑战。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。