3、SpringCloud Netflix,服务发现之Eureka详解
SpringCloud Netflix
与各种Netflix OSS组件(Eureka、Hystrix、Zuul、Archaius等)集成。
参考文档:https://spring.io/projects/spring-cloud-netflix
Spring Cloud Netflix通过自动配置和绑定到Spring环境和其他Spring编程模型习惯用法,为Spring Boot应用程序提供Netflix OSS集成。通过一些简单的注释,您可以在应用程序中快速启用和配置常见模式,并使用经过实战测试的Netflix组件构建大型分布式系统。提供的模式包括服务发现(Eureka)。
特点
服务发现(Service Discovery):可以注册Eureka实例,客户端可以使用Spring托管bean发现实例
服务发现(Service Discovery):可以使用声明性Java配置创建嵌入式Eureka服务器
入门
只要有Spring Cloud Netflix和Eureka Core依赖,任何带有@EnableEurekaClient 注解的Spring Boot应用程序都会尝试关联Eureka服务器 http://localhost:8761(eureka.client.serviceUrl.defaultZone的默认值)
@SpringBootApplication
@EnableEurekaClient
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello World";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
要运行自己的服务器,请使用spring-cloudstarter netflix eureka服务器依赖项和@EnableEurekaServer。
服务发现(Service Discovery)
参考:https://docs.spring.io/spring-cloud-netflix/docs/current/reference/html
服务发现是基于微服务的体系结构的关键原则之一。
尝试手动配置每个客户端或某种形式的约定可能很难做到,而且可能很脆弱。Eureka是Netflix服务发现服务器和客户端。可以将服务器配置和部署为高度可用,每个服务器都将注册服务的状态复制到其他服务器。
Eureka Server
如何包含Eureka服务端?
group ID of org.springframework.cloud
artifact ID of spring-cloud-starter-netflix-eureka-server
Maven示例
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
如果您的项目已经使用Thymelaf作为模板引擎,Eureka服务器的Freemark模板可能无法正确加载。在这种情况下,需要手动配置模板加载器:
application.yml
spring:
freemarker:
template-loader-path: classpath:/templates/
prefer-file-system-access: false
以下示例显示了最小Eureka服务器:
@SpringBootApplication
// 此注解代表Eureka服务器
@EnableEurekaServer
public class Application {
public static void main(String[] args) {
// 代码是官方教程内的,自己尝试后发现没有 web(boolean) 的函数
// new SpringApplicationBuilder(Application.class).web(true).run(args);
// 使用以下代码替代
SpringApplication.run(Application.class, args);
}
}
该服务器有一个主页,带有UI和HTTP API端点,用于/euroka/*下的正常Eureka功能。
以下链接有一些Eureka背景阅读:flux capacitor 和 google group discussion.
小提示
由于Gradle的依赖性解析规则和缺少父bom特性,依赖于spring云启动器netflix eureka服务器可能会导致应用程序启动失败。要解决此问题,请添加Spring Boot Gradle插件并导入Spring云启动器父bom,如下所示:
build.gradle
buildscript {
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}")
}
}
apply plugin: "spring-boot"
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"
}
}
defaultOpenForTrafficCount及其对EurekaServer预热时间的影响
Netflix Eureka的 waitTimeInMsWhenSyncEmpty
设置一开始在Spring Cloud Eureka服务器中没有考虑。为了启用预热时间,请将 eureka.server.defaultOpenForTrafficCount
设置为0。
高可用性、区域和地区
Eureka服务器没有后端存储,但注册表中的服务实例都必须发送心跳以保持其注册最新(因此这可以在内存中完成)。客户机还具有Eureka注册的内存缓存(因此他们不必为每个服务请求都去注册表)。
默认情况下,每个Eureka服务器也是Eureka客户端,需要(至少一个)服务URL来定位对等端。如果您不提供它,服务将运行并工作,但它会给您的日志带来很多无法向对等方注册的噪音。
独立模式
这两个缓存(客户端和服务器)和心跳的结合使得一个独立的Eureka服务器能够很好地应对故障,只要有某种监视器或弹性运行时(如CloudFoundry)保持其运行。在独立模式下,您可能更喜欢关闭客户端行为,这样它就不会一直尝试和无法到达对等端。以下示例显示了如何关闭客户端行为:
application.yml (独立Eureka Server)
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
application.properties
server.port=8761
eureka.instance.hostname=localhost
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
请注意,serviceUrl
指向与本地实例相同的主机。
eureka.client.registerWithEureka:false 和 fetchRegistry:false 来表明自己是一个eureka server.
运行项目
运行控制台输出大致如下
然后可以通过浏览器访问设置的serviceUrl地址进入eureka server界面,例如上面的示例地址为 http://localhost:8761
同伴意识(Peer Awareness)
通过运行多个实例并要求它们彼此注册,Eureka可以变得更具弹性和可用性。事实上,这是默认行为,因此要使其正常工作,只需向对等方添加有效的serviceUrl,如下例所示:
application.yml (Two Peer Aware Eureka Servers)
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: https://peer2/eureka/
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: https://peer1/eureka/
在上面的示例中,我们有一个YAML文件,通过在不同的Spring配置文件中运行,它可以用于在两个主机(peer1和peer2)上运行同一个服务器。您可以使用此配置通过操作/etc/hosts解析主机名来测试单个主机上的对等感知(在生产环境中这样做没有多大价值)。事实上,如果您运行的机器知道自己的主机名(默认情况下,使用java.net.InetAddress查找),则不需要eureka.instance.hostname。
您可以将多个对等体添加到系统中,只要它们都通过至少一个边缘彼此连接,它们就可以在自己之间同步注册。如果对等体在物理上是分开的(在一个数据中心内或多个数据中心之间),那么原则上,系统可以在“分裂大脑”类型的故障中幸存下来。您可以将多个对等体添加到系统中,只要它们都彼此直接连接,它们就会同步注册。
application.yml (Three Peer Aware Eureka Servers)
eureka:
client:
serviceUrl:
defaultZone: https://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
---
spring:
profiles: peer3
eureka:
instance:
hostname: peer3
何时首选IP地址
在某些情况下,Eureka最好公布服务的IP地址,而不是主机名。将 eureka.instance.preferIpAddress
设置为 true
,当应用程序向eureka注册时,将使用其IP地址而不是主机名。
小提示:
如果Java无法确定主机名,则将IP地址发送给Eureka。设置主机名的唯一明确方法是设置eureka.instance.hostname
属性。可以使用环境变量在运行时设置主机名 — 例如,eureka.instance.hostname=${HOST_NAME}
.
保护Eureka服务器
您只需通过 spring-boot-starter-security
将SpringSecurity添加到服务器的类路径中,即可保护Eureka服务器。默认情况下,当Spring Security在类路径上时,它将要求在向应用程序发送每个请求时发送一个有效的CSRF令牌。Eureka客户端通常不会拥有有效的跨站点请求伪造(CSRF)令牌,您需要禁用 /euroka/**
端点的此要求。例如:
@EnableWebSecurity
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}
JDK 11支持
Eureka服务器所依赖的JAXB模块在JDK 11中被删除。如果您打算在运行Eureka服务器时使用JDK 11,则必须在POM或Gradle文件中包含这些依赖项。
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
Eureka Clients
如何包含Eureka客户端?
要在项目中包含Eureka客户端,请使用 group ID为 org.springframework.cloud
和artifact ID为 spring-cloud-starter-netflix-eureka-client
。
有关使用当前SpringCloudRelease Train设置构建系统的详细信息,请参阅SpringCloudProject页面。
Maven示例
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
注册 Eureka
当客户端向Eureka注册时,它会提供有关自身的元数据 — 例如主机、端口、健康指示器URL、主页和其他详细信息。Eureka从属于服务的每个实例接收心跳消息。如果心跳在可配置的时间表上失败,则通常会从注册表中删除该实例。
以下示例显示了一个最小的Eureka客户端应用程序:
@SpringBootApplication
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello world";
}
public static void main(String[] args) {
// 代码是官方教程内的,自己尝试后发现没有 web(boolean) 的函数
// new SpringApplicationBuilder(Application.class).web(true).run(args);
// 使用以下代码替代
SpringApplication.run(Application.class, args);
}
}
注意,上面的示例显示了一个普通的Spring Boot应用程序。通过在类路径上使用spring-cloud-starter-netflix-eureka-client客户端,您的应用程序将自动向eureka服务器注册。需要配置才能找到Eureka服务器,如下例所示:
application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
application.properties
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
在上面的示例中,defaultZone是一个魔术字符串回退值,它为任何不表示偏好的客户端提供服务URL(换句话说,它是一个有用的默认值)。
注意:defaultZone区分大小写
默认应用程序名称(即服务ID)、虚拟主机和非安全端口(取自环境)分别为 ${spring.application.name}
、${spring.application.name]
和 ${server.port}
。
在类路径上安装 spring-cloud-starter-netflix-eureka-client
,使应用程序既成为一个eureka“实例”(即它自己注册),又成为一个“客户端”(它可以查询注册表以查找其他服务)。实例行为由 eureka.instance.*
配置键驱动,但如果您确保应用程序具有 spring.application.name
值(这是eureka服务ID或VIP的默认值),则默认值也可以。
后续服务与服务之间相互调用一般都是使用 spring.application.name 的值
有关可配置选项的更多详细信息,请参见 EurekaInstanceConfigBean 和 EurekaClientConfigBean。
要禁用Eureka Discovery Client,可以将 Eureka.Client.enabled
设置为 false
。当spring.cloud.Discovery.enabled
设置为 false
时,Eureka Discovery Client也将被禁用。
当前不支持将Spring Cloud Netflix Eureka服务器的版本指定为路径参数。这意味着不能在上下文路径(eurekaServerURLContext
)中设置版本。相反,您可以在服务器URL中包含版本(例如,可以设置defaultZone:localhost:8761/eureka/v2
)。
使用Eureka服务器进行身份验证
如果其中一个 eureka.client.serviceUrl.defaultZone
URL中嵌入了凭据(curl样式,如下所示:user:password@localhost:8761/eureka
)。对于更复杂的需求,您可以创建一个DiscoveryClientOptionalArgs
类型的 @Bean
,并将 ClientFilter
实例注入其中,所有这些都应用于从客户端到服务器的调用。
当Eureka服务器需要客户端证书进行身份验证时,可以通过财产配置客户端证书和信任存储,如下例所示:
application.yml
eureka:
client:
tls:
enabled: true
key-store: <path-of-key-store>
key-store-type: PKCS12
key-store-password: <key-store-password>
key-password: <key-password>
trust-store: <path-of-trust-store>
trust-store-type: PKCS12
trust-store-password: <trust-store-password>
eureka.client.tls.enabled
需要为true才能启用eureka客户端tls。当省略 eureka.client.tls.trust-store
时,将使用JVM默认信任存储。eureka.client.tls.key-store-type
和 eureka.client.tls.trust-store-type
的默认值为PKCS12。如果省略密码财产,则假定密码为空。
由于Eureka的限制,不可能支持每台服务器的基本身份验证凭据,因此只使用找到的第一组凭据。
如果您想要自定义Eureka HTTP客户端使用的RestTemplate,您可能需要创建EurekaClientHttpRequestFactorySupplier
的bean,并提供自己的逻辑来生成 ClientHttpRequestFactory
实例。
Eureka HTTP客户端使用的RestTemplate的所有默认超时相关财产都设置为3分钟(与Apache HC5默认RequestConfig
和 SocketConfig
保持一致)。因此,要指定超时值,必须使用eureka.client.rest-template-timeout
中的财产直接指定该值。(所有超时财产均以毫秒为单位。)
application.yml
eureka:
client:
rest-template-timeout:
connect-timeout: 5000
connect-request-timeout: 8000
socket-timeout: 10000
状态页和运行状况指示器
Eureka实例的状态页和健康指示器分别默认为 /info
和 /health
,这是Spring Boot Actuator应用程序中有用端点的默认位置。如果您使用非默认的上下文路径或servlet路径(例如 server.servletPath=/custom
),即使对于执行器应用程序,也需要更改这些设置。以下示例显示了两个设置的默认值:
application.yml
eureka:
instance:
statusPageUrlPath: ${server.servletPath}/info
healthCheckUrlPath: ${server.servletPath}/health
这些链接显示在客户端使用的元数据中,并在某些场景中用于决定是否向应用程序发送请求,因此如果它们是准确的,将非常有用。
在Dalston,在更改管理上下文路径时,还需要设置状态和健康检查URL。这一要求从Edgware开始被删除。
注册安全应用程序
如果您的应用程序希望通过HTTPS进行联系,可以在EurekaInstanceConfigBean中设置两个标志:
- eureka.instance.[nonSecurePortEnabled]=[false]
- eureka.instance.[securePortEnabled]=[true]
这样做会使Eureka发布实例信息,显示出对安全通信的明确偏好。对于这样配置的服务,Spring Cloud DiscoveryClient
始终返回以https
开头的URI。类似地,当以这种方式配置服务时,Eureka(本机)实例信息具有安全的健康检查URL。
由于Eureka内部的工作方式,它仍然为状态和主页发布一个非安全URL,除非您也显式覆盖这些URL。您可以使用占位符来配置eureka实例URL,如下例所示:
application.yml
eureka:
instance:
statusPageUrl: https://${eureka.hostname}/info
healthCheckUrl: https://${eureka.hostname}/health
homePageUrl: https://${eureka.hostname}/
(注意,${eureka.hostname}
是一个本机占位符,仅在eureka的更高版本中可用 — 例如,通过使用${eureka.instance.hostName}
)
如果您的应用程序在代理后面运行,并且SSL终止在代理中(例如,如果您在Cloud Foundry或其他平台上作为服务运行),那么您需要确保代理“转发”的标头被应用程序拦截和处理。如果嵌入在Spring Boot应用程序中的Tomcat容器对“X-Forwarded-*”标头进行了显式配置,则会自动发生这种情况。应用程序自身呈现的链接错误(主机、端口或协议错误)表明您的配置错误。
Eureka的健康检查
默认情况下,Eureka使用客户端心跳来确定客户端是否启动。除非另有规定,否则Discovery Client不会根据Spring Boot Actuator传播应用程序的当前健康检查状态。因此,在成功注册后,Eureka总是宣布应用程序处于“UP”状态。可以通过启用Eureka健康检查来更改此行为,从而将应用程序状态传播到Eureka。因此,每个其他应用程序都不会向处于“UP”状态之外的应用程序发送流量。以下示例显示如何为客户端启用运行状况检查:
application.yml
eureka:
client:
healthcheck:
enabled: true
eureka.client.healthcheck.enabled=true
只应在application.yml
中设置。在bootstrap.yml
中设定值会产生不希望的副作用,例如在eureka中以UNKNOWN
状态注册。
如果您需要对健康检查进行更多控制,请考虑实现您自己的 com.netflix.appinfo.HealthCheckHandler
。
Eureka实例和客户端元数据
值得花一点时间了解Eureka元数据的工作原理,这样您就可以在您的平台上以合理的方式使用它。主机名、IP地址、端口号、状态页和健康检查等信息有标准元数据。这些信息发布在服务注册表中,并由客户端以直接的方式联系服务。可以将其他元数据添加到 eureka.instance.metadataMap
中的实例注册中,并且可以在远程客户端中访问该元数据。通常,附加元数据不会改变客户端的行为,除非客户端知道元数据的含义。有一些特殊情况,如本文稍后所述,SpringCloud已经为元数据映射分配了意义。
在Cloud Foundry上使用Eureka
CloudFoundry有一个全局路由器,因此同一应用程序的所有实例都具有相同的主机名(具有类似架构的其他PaaS解决方案具有相同的布局)。这不一定是使用Eureka的障碍。但是,如果您使用路由器(建议使用或强制使用,取决于平台的设置方式),则需要明确设置主机名和端口号(安全或非安全),以便它们使用路由器。您可能还希望使用实例元数据,以便可以区分客户端上的实例(例如,在自定义负载平衡器中)。默认情况下,eureka.instance.instanceId
为 vcap.application.instance_id
,如下例所示:
application.yml
eureka:
instance:
hostname: ${vcap.application.uris[0]}
nonSecurePort: 80
根据在CloudFoundry实例中设置安全规则的方式,您可能能够注册并使用主机VM的IP地址进行直接服务到服务调用。此功能在Pivotal Web Services (PWS).上尚不可用。
在AWS上使用Eureka
如果计划将应用程序部署到AWS云,则必须将Eureka实例配置为支持AWS。您可以通过如下方式自定义EurekaInstanceConfigBean 来实现:
@Bean
@Profile("!default")
public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
EurekaInstanceConfigBean bean = new EurekaInstanceConfigBean(inetUtils);
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
bean.setDataCenterInfo(info);
return bean;
}
更改Eureka实例ID
一个普通的Netflix Eureka实例注册的ID等于其主机名(即每个主机只有一个服务)。Spring Cloud Eureka提供了一个合理的默认值,其定义如下:
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
例如:myhost:myappname:8080
通过使用Spring Cloud,可以通过在eureka.instance.instanceId
中提供唯一标识符来覆盖该值,如下例所示:
application.yml
eureka:
instance:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
使用上面示例中显示的元数据和部署在本地主机上的多个服务实例,随机值将插入其中以使实例唯一。在Cloud Foundry中,vcap.application.instance_id
在Spring Boot应用程序中自动填充,因此不需要随机值。
使用EurekaClient
一旦您有了作为发现客户端的应用程序,就可以使用它从Eureka服务器发现服务实例。一种方法是使用本机com.netflix.discovery.EurekaClient
(与Spring Cloud DiscoveryClient
相反),如下例所示:
@Autowired
private EurekaClient discoveryClient;
public String serviceUrl() {
InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
return instance.getHomePageUrl();
}
不要在
@PostConstruct
方法或@Scheduled
方法中使用EurekaClient
(或尚未启动ApplicationContext的任何地方)。它在一个SmartLifecycle中初始化(阶段=0),因此您最早可以依赖它在另一个具有更高阶段的SmartLifecycle中可用。
EurekaClient与Jersey
默认情况下,EurekaClient使用Spring的RestTemplate进行HTTP通信。如果希望改用Jersey,则需要将Jersey依赖项添加到类路径中。以下示例显示了需要添加的依赖项:
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client4</artifactId>
</dependency>
本地Netflix EurekaClient的替代方案
您不需要使用原始Netflix EurekaClient。此外,在某种包装器后面使用它通常更方便。Spring Cloud通过逻辑Eureka服务标识符(VIP)而不是物理URL支持Feign(REST客户端构建器)和Spring RestTemplate。
您还可以使用 org.springframework.cloud.client.discovery.DiscoveryClient
,它为发现客户端提供了一个简单的API(不特定于Netflix),如下例所示:
@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List<ServiceInstance> list = discoveryClient.getInstances("STORES");
if (list != null && list.size() > 0 ) {
return list.get(0).getUri();
}
return null;
}
为什么注册服务如此缓慢?
作为一个实例还需要定期向注册表发送心跳(通过客户端的serviceUrl),默认持续时间为30秒。在实例、服务器和客户端的本地缓存中都有相同的元数据之前,客户端无法发现服务(因此可能需要3次心跳)。您可以通过设置eureka.instance.releaseRenewalIntervalInSeconds来更改周期。将其设置为小于30会加快客户端连接到其他服务的过程。在生产中,可能最好坚持默认值,因为服务器中的内部计算会对租约续订期进行假设。
区域
如果您已将Eureka客户端部署到多个区域,则在尝试其他区域中的服务之前,您可能更希望这些客户端在同一区域中使用服务。要进行设置,您需要正确配置Eureka客户端。
首先,您需要确保每个区域都部署了Eureka服务器,并且它们是彼此的对等服务器。有关详细信息,请参见分区和区域部分。
接下来,您需要告诉Eureka您的服务所在的区域。您可以通过使用metadataMap属性来做到这一点。例如,如果服务1同时部署到区域1和区域2,则需要在服务1中设置以下Eureka财产:
Service 1 in Zone 1
eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true
Service 1 in Zone 2
eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true
刷新Eureka客户端
默认情况下,EurekaClientbean是可刷新的,这意味着可以更改和刷新Eureka客户端财产。当刷新发生时,客户端将从Eureka服务器注销,并且可能会有一段短暂的时间,给定服务的所有实例都不可用。消除这种情况的一种方法是禁用刷新Eureka客户端的功能。为此,设置 eureka.client.refresh.enable=false
。
将Eureka与Spring Cloud LoadBalancer一起使用
我们为Spring Cloud LoadBalancer ZonePreferencesServiceInstanceListSupplier提供支持。Eureka实例元数据(Eureka.instance.metadataMap.gone)中的区域值用于设置用于按区域过滤服务实例的spring云负载平衡器区域属性的值。
如果缺少该标志,并且spring.cloud.looadbalancer.eureka.approximateZoneFromHostname标志设置为true,则可以使用服务器主机名中的域名作为区域的代理。
如果没有其他区域数据源,则根据客户端配置(与实例配置相反)进行猜测。我们使用eureka.client.availabilityZones,它是从区域名称到区域列表的映射,并为实例自己的区域拉出第一个区域(即eureka.client区域,为与本地Netflix兼容,默认为“us-east-1”)。
AOT和本机映像支持
Spring Cloud Netflix Eureka客户端集成支持Spring AOT转换和本地图像,但仅在禁用刷新模式的情况下。
如果要在AOT或本机映像模式下运行Eureka客户端,请确保将spring.cloud.refresh.enabled设置为false
配置属性大全
要查看所有Spring Cloud Netflix相关配置财产的列表,请查看附录页面。
Eureka 总结+使用
服务注册中心
注册中心记录各服务单元提供的服务,包括地址、端口、版本等
新建一个SpringBoot项目,引入SpringCloud和Eureka Server的依赖
Maven
<?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>3.0.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>DemoServer</name>
<description>Eureka Server</description>
<properties>
<java.version>17</java.version>
<spring.cloud-version>2022.0.1</spring.cloud-version>
</properties>
<dependenicyManagement>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- eureka server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- JDK11支持 -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接下来进行配置
application.properties
server.port=8761
eureka.instance.hostname=localhost
# 是否将自己注册到服务中心
eureka.client.registerWithEureka=false
# 是否检索服务
eureka.client.fetchRegistry=false
# 服务中心地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
启动类加上 @EnableEurekaServer 注解启用服务中心
@SpringBootApplication
@EnableEurekaServer
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
高可用
考虑到出故障的情况,服务注册中心瘫痪将会造成整个系统瘫痪,因此需要保证服务注册中心的高可用
在 Eureka 中,所有节点既是服务的提供者也是服务的消费者,服务注册中心也不例外。
构建服务注册中心集群
Eureka Server的同步遵循着一个非常简单的原则:只要有一条边将节点连接,就可以进行信息传播与同步
创建两个 Boot,引入 Eureka Server,作为服务注册中心的节点,使其互相连接(加上之前的就三个连接),配置如下
Server1
application.properties
server.port=8761
eureka.instance.hostname=server1
# 是否将自己注册到服务中心,搭建集群则需要,默认true
#eureka.client.registerWithEureka=false
# 是否检索服务,搭建集群则需要,默认true
#eureka.client.fetchRegistry=false
# 首选IP地址
eureka.instance.preferIpAddress=true
# 禁用自我保护模式
eureka.server.enableSelfPreservation=false
# 服务中心地址,多个使用英文逗号(,)分隔,连接其他两个中心
eureka.client.serviceUrl.defaultZone=http://server2:8762/eureka/,http://server3:8763/eureka/
Server2
server.port=8762
eureka.instance.hostname=server2
eureka.instance.preferIpAddress=true
eureka.server.enableSelfPreservation=false
eureka.client.serviceUrl.defaultZone=http://server1:8761/eureka/,http://server3:8763/eureka/
Server3
server.port=8763
eureka.instance.hostname=server3
eureka.instance.preferIpAddress=true
eureka.server.enableSelfPreservation=false
eureka.client.serviceUrl.defaultZone=http://server1:8761/eureka/,http://server2:8762/eureka/
因为hostname为主机名,需要更改 hosts,将上面三个主机名都指向到本机,增加如下
127.0.0.1 server1
127.0.0.1 server2
127.0.0.1 server3
defaultZone指定的地址没有连接上则会一直尝试重连
进入 http://server1:8761 界面如下
使用 spring.application.name
指定程序名称,后续服务之间的调用一般使用指定的名称,未指定则默认 UNKNOWN,如上图所示
例如有两个服务名称都为 Test,当其中一个服务挂了后,依然可以正常使用
失效剔除
当服务进行正常关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线 了”。服务中心接受到请求之后,将该服务置为下线状态。
有些时候,我们的服务实例可能非正常下线,为了从服务列表中将这些无法提供服务的实例剔除,Eureka Server在启动的时候会创建一个定时任务,每隔一段时间 (默认为60秒) 将当前清单中超时 (默认为90秒)没有续约的服务剔除,这个操作被称为失效剔除。
可以通过 eureka.server.eviction-interval-timer-in-ms
参数进行修改,单位是毫秒。
自我保护
服务注册到Eureka Server后,会维护一个心跳连接
Eureka Server 运行期间会统计心跳失败的比例在15分钟以之内是否低于85%,在生产环境下,因为网络延迟等原因,心跳失败比例可能超标,此时把服务剔除列表并不妥当
开启自我保护,如果出现低于的情况,Eureka Server 会将当前实例注册信息保护起来,让这些实例不会过期。这样做会使客户端很容易拿到实际已经不存在的服务实例,会出现调用失败的情况。因此客户端要有容错机制,比如请求重试、断路器。
可以通过 eureka.server.enableSelfPreservation
来设置是否开启自我保护,默认开启
服务提供者(provider)
提供接口给其他服务,被其他服务调用的服务,是EurekaClient
创建一个新的项目,和之前一样,增加eureka-client依赖即可
Maven
<?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>3.0.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<spring.cloud-version>2022.0.1</spring.cloud-version>
</properties>
<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>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置如下
application.properties
# 是否注册到服务中心,默认true
# eureka.client.registerWithEureka=true
# 指定名称,方便后面服务之间的调用使用
spring.application.name=EurekaClientProvider
server.port=8081
# 服务注册中心地址,有多个则加多个,逗号分隔
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
Application
@SpringBootApplication
// EnableDiscoveryClient 与 EnableEurekaClient 功能一致,前者可用于其他注册中心,后者只适合Eureka注册中心
// 新版推荐使用EnableDiscoveryClient
// 从Spring Cloud Edgware开始,这两个注解可以省略,只要添加client依赖并进行配置即可
// @EnableDiscoveryClient
// @EnableEurekaClient
public class EurekaClientProviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientProviderApplication.class, args);
}
}
提供一个获取当前服务名称和端口的接口
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 信息获取.
* @author Shendi
*/
@RestController
@RequestMapping("/info")
public class InfoControl {
@Value("${spring.application.name}") String appName;
@Value("${server.port}") String port;
@GetMapping
public String info() {
return "name=" + appName + ", port=" + port;
}
}
启动服务后,注册中心输出如下日志
Registered instance EUREKACLIENTPROVIDER/xxx:EurekaClientProvider:8081 with status UP (replication=false)
访问网页,内容如下
ps:显示的名称为全大写,可以使用 - 来分隔名称提高可读性
服务消费者(consumer)
调用其他服务提供的接口的服务,是EurekaClient
新建一个boot项目,引入的依赖与提供者相同
配置如下
application.properties
# 是否注册到服务中心,默认true
# eureka.client.registerWithEureka=true
# 是否检索服务,默认true
# eureka.client.fetchRegistry=true
spring.application.name=eureka-client-consumer
server.port=8080
# 服务注册中心地址,有多个则加多个,逗号分隔
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。
通过配置 RestTemplate 来调用接口
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class BeanConfiguration {
@Bean
// 加上LoadBalanced,每次调用的时候会按照策略进行负载均衡(可以直接指定服务名称,没加上则会找不到服务)
@LoadBalanced
public RestTemplate getRestTemplate() { return new RestTemplate(); }
}
通过 RestTemplate 对象调用其他服务提供的接口
如下
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author Shendi
*/
@RestController
@RequestMapping("/test")
public class TestControl {
@Autowired private RestTemplate rt;
/** 服务提供者的名称地址 */
private String nameUrl = "http://EurekaClientProvider";
@GetMapping
public String test() {
// 调用提供者提供的info接口,getForObject是以get请求,还有postForObject...
// 第二个参数指定获取到的返回值类型,没特别需求使用String就可以了
String result = rt.getForObject(nameUrl + "/info", String.class);
return "call ok: " + result;
}
}
访问消费者接口,结果如下
END
本文链接:https://sdpro.top/blog/html/article/1020.html♥ 赞助 ♥
尽管去做,或许最终的结果不尽人意,但你不付出,他不付出,那怎会进步呢?