关键词:Eureka 服务发现 服务注册
Spring Cloud是基于Spring Boot做为开发框架的,在Spring Cloud的很多功能实现中,大量使用了Spring Boot的自动注入功能(AutoConfigure)。Spring Cloud中,服务与服务之间的通信使用的是http协议。
Eureka具有服务注册与服务发现,并且可以作为注册中心。Eureka分为两种角色,服务端(Eureka Server)和客户端(Eureka Client)。Eureka服务端可以看做是一个注册中心,只是这个注册中心需要依赖于Spring Boot项目,我们在使用Eureka服务端功能的时候,需要创建一个Spring Boot项目然后引入相关依赖,修改配置,使用注解,然后运行这个Spring Boot项目即可。关于我们项目自己的服务提供者和服务消费者,对Eureka来说,都是Eureka客户端。在我们自己的项目中引入相关依赖,然后使用注册,Eureka客户端功能会自动的将我们的服务注册到Eureka服务端中,并完成我们的服务消费者服务发现的功能。
Eureka服务端对自己来说,也是Eureka客户端。所以在spring-cloud-starter-netflix-eureka-server依赖中还包含了spring-cloud-netflix-eureka-client的内容,并且在Eureka Server自动注入的时候,还会依赖于client端自动注入的bean。
Eureka客户端通过调用http接口与Eureka服务端进行通信,完成服务注册与获取等功能。默认使用Jersey进行http通信。服务提供者使用HTTP POST请求(/eureka/apps/{appName})注册服务。每隔30秒,它必须使用HTTP PUT请求(/eureka/apps/{appName}/{id})刷新其注册,与Eureka服务端保持心跳联系。通过使用HTTP DELETE请求(/eureka/apps/{appName}/{id})或实例注册超时来删除注册。服务消费者可以通过使用HTTP GET请求(/eureka/apps或/eureka/apps/{appName})检索已注册的服务实例。
Eureke客户端调用请求接口:EurekaHttpClient、AbstractJerseyEurekaHttpClient及其实现类
Eureke服务端接口资源定义:ApplicationsResource、ApplicationResource、InstanceResource及其同一个包下的其他Resource
█ Eureka服务端
角色:注册中心
使用:
(1)创建maven项目,修改pom.xml文件,引入相应的依赖
<properties>
<java.version>1.8</java.version>
<!-- Spring Cloud版本 -->
<spring-cloud.version>Hoxton.RELEASE</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>
<!-- 添加eureka-server依赖,引入jar包 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
spring-cloud-starter-netflix-eureka-server依赖中已经包含了Spring Boot的依赖,为了避免版本冲突带来的问题,建议无需再在项目中单独引入Spring Boot依赖。spring-cloud-starter-netflix-eureka-server中包含了如下的依赖:
(2)编写application.yml配置
因为是Spring Boot项目,所以使用yaml文件作为配置文件。
# Eureka作为Spring Boot项目对外提供服务,需要设置端口号,默认为8080
# 因为在Spring Cloud中服务之间通信使用http协议,所以每个项目需要是web项目
server:
port: 8899
eureka:
client:
// 是否进行注册服务,当前服务不作为eureka 客户端,不进行服务注册
register-with-eureka: false
// 是否获取注册服务列表
fetch-registry: false
(3)编写Spring Boot启动类
package myeureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
// Spring Boot注解
@SpringBootApplication
// 开启Eureka server功能
@EnableEurekaServer
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
启动的时候,如果出现下图所示的错误,此时,删除本地maven仓库中对应的jar文件即可:
(4)启动成功之后,在浏览器访问:localhost:8899,会看见如下图所示页面。此页面为Eureka服务端的仪表盘,在里面可以看见已经注册的服务提供者的信息,以及Eureka服务集群的情况:
经过上面的几个简单的步骤,就能搭建一个Eureka服务作为注册中心了。
补充:将当前Eureka服务端也作为客户端服务进行注册,修改yml配置:
eureka:
client:
// 注册服务,默认为true
register-with-eureka: true
fetch-registry: false
service-url:
// eureka 服务端的服务地址,请求路径为/eureka/。因为此时的服务端就是本服务自己,所以这里的服务地址就是当前服务
defaultZone: http://localhost:${server.port}/eureka/
重启服务之后,访问:localhost:8899/,此时在服务列表会出现当前服务:
原理:
(1)从@EnableEurekaServer注解开始,通过@Import,在Spring启动的时候会被加载:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({EurekaServerMarkerConfiguration.class})
public @interface EnableEurekaServer {
}
(2)EurekaServerMarkerConfiguration就做了一件事情,创建Marker对象,Spring会扫描@Bean注解,将Marker对象注册成Spring bean,加入Spring容器中:
public class EurekaServerMarkerConfiguration {
public EurekaServerMarkerConfiguration() {
}
@Bean
public EurekaServerMarkerConfiguration.Marker eurekaServerMarkerBean() {
return new EurekaServerMarkerConfiguration.Marker();
}
class Marker {
Marker() {
}
}
}
(3)此时,如果你不熟悉Spring Boot的自动配置功能,可能会一头雾水,就这?熟悉Spring Boot自动配置的同学,应该了解在项目的META-INF文件夹下,如果存在一个文件为spring.factories,里面的内容是键值对形式,键为org.springframework.boot.autoconfigure.EnableAutoConfiguration,这样Spring Booot在启动的时候,会根据value的值,自动完成配置,创建Bean等等。找到spring-cloud-starter-netflix-eureka-server的jar包,可以看见:
spring.factories的内容为下面所示:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
(4)EurekaServerAutoConfiguration
EurekaServerAutoConfiguration类的内容比较多,主要完成的就是多个bean的创建。我们一段段来看:
- 类上注解:
// Spring会加载EurekaServerInitializerConfiguration
@Import({EurekaServerInitializerConfiguration.class})
// 当Spring 容器中存在Marker类型的bean时,Spring才会加载EurekaServerAutoConfiguration
// 因为在启动类上使用了@EnableEurekaServer注解,所以Spring容器中会存在Merker类型的bean
@ConditionalOnBean({Marker.class})
// 配置的加载,将yml中的配置,设置到EurekaDashboardProperties、InstanceRegistryProperties两个bean对应的属性上。
@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})
// 当前类可以使用类路径下/eureka/server.properties路径下的配置信息
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
......
}
- 通过@Autowired自动注入相应类型的bean
// ApplicationInfoManager是eureka-client jar包下面的类
// @Singleton
// public class ApplicationInfoManager
@Autowired
private ApplicationInfoManager applicationInfoManager;
@Autowired
private EurekaServerConfig eurekaServerConfig;
@Autowired
private EurekaClientConfig eurekaClientConfig;
@Autowired
private EurekaClient eurekaClient;
@Autowired
private InstanceRegistryProperties instanceRegistryProperties;
其中的ApplicationInfoManager、EurekaClientConfig、EurekaClient 是在EurekaClientAutoConfiguration里面完成bean的注册的。EurekaClientAutoConfiguration在spring-cloud-netflix-eureka-client jar包下面。从这里可以看出,Eureka Server依赖于Eureka Client,无法单独使用。
- 创建仪表盘页面的访问controller,即在使用步骤4中通过浏览器请求看到的页面。可通过eureka.dashboard.enabled=false关闭此功能,默认为true开启:
@Bean
@ConditionalOnProperty(
prefix = "eureka.dashboard",
name = {"enabled"},
matchIfMissing = true
)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}
访问路径可以通过eureka.dashboard.path配置,默认为/,所以我们在浏览器访问:localhost:8899可以获取到页面。若修改配置eureka.dashboard.path=myeureka,则访问路径为:localhost:8899/myeureka:
@Controller
@RequestMapping({"${eureka.dashboard.path:/}"})
public class EurekaController {
@Value("${eureka.dashboard.path:/}")
private String dashboardPath = "";
private ApplicationInfoManager applicationInfoManager;
public EurekaController(ApplicationInfoManager applicationInfoManager) {
this.applicationInfoManager = applicationInfoManager;
}
// get请求获取页面信息
@RequestMapping(
method = {RequestMethod.GET}
)
public String status(HttpServletRequest request, Map<String, Object> model) {
// 下面内容的主要工作就是获取相关值放进model中,这样在页面可以获取到
this.populateBase(request, model);
this.populateApps(model);
StatusInfo statusInfo;
try {
statusInfo = (new StatusResource()).getStatusInfo();
} catch (Exception var5) {
statusInfo = Builder.newBuilder().isHealthy(false).build();
}
model.put("statusInfo", statusInfo);
this.populateInstanceInfo(model, statusInfo);
this.filterReplicas(model, statusInfo);
// 这里返回到类路径下eureka文件夹下面的status视图页面
// 这一块是Spring MVC的知识了
return "eureka/status";
}
}
在jar包里面可以找到:
- 创建EurekaServerConfig类型的bean,配置Eureka服务端:
@Configuration(
proxyBeanMethods = false
)
protected static class EurekaServerConfigBeanConfiguration {
protected EurekaServerConfigBeanConfiguration() {
}
@Bean
@ConditionalOnMissingBean
public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
// @ConfigurationProperties("eureka.server")
// public class EurekaServerConfigBean implements EurekaServerConfig
// 在bean创建的时候,会通过eureka.server开头的配置对EurekaServerConfigBean中的属性进行设值
EurekaServerConfigBean server = new EurekaServerConfigBean();
// 通过配置:eureka.client.register-with-eureka设置。默认为true
if (clientConfig.shouldRegisterWithEureka()) {
// 注册失败重试5次
server.setRegistrySyncRetries(5);
}
return server;
}
}
- 创建编码解码器,在网络中数据的传输需要编码和解码,用于Eureka服务端与Eureka客户端数据传输时的编码和解码:
@Bean
public ServerCodecs serverCodecs() {
return new EurekaServerAutoConfiguration.CloudServerCodecs(this.eurekaServerConfig);
}
- 创建Eureka服务的副本的过滤器。用于多个Eureka服务时,即Eureka集群:
@Bean
@ConditionalOnMissingBean
public ReplicationClientAdditionalFilters replicationClientAdditionalFilters() {
return new ReplicationClientAdditionalFilters(Collections.emptySet());
}
- 用于服务注册功能。此服务注册包含了集群中副本的注册以及客户端服务的注册:
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {
this.eurekaClient.getApplications();
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
- Eureke节点集合:
@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs, ReplicationClientAdditionalFilters replicationClientAdditionalFilters) {
return new EurekaServerAutoConfiguration.RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.applicationInfoManager, replicationClientAdditionalFilters);
}
- Eureka服务的上下文:
@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.a