新的任务
“我们现有的系统太笨重了,要拆成多个小系统来进行重构,系统之间通过Http通信交换数据......”架构师一上午噼里啪啦说了一堆事情,大家都哈欠都打到天上去了。“小徐,你这边负责一下各个系统的配置信息。”架构师大锤看了一眼角落里刚毕业的小徐。“哦,好的,没问题。”小徐满满自信,因为这不很简单么,一共才拆成三个服务能有多少信息。
刚开始的时候还没什么问题,之后突然有一天CustService服务调不通了,排查了好久的问题才发现CustService的端口变了,之后每当有变动的时候各个小组就把变动发给小徐,小徐修改完之后,再发给各个小组。一来二去,也挺麻烦的,有的时候也会不小心发错文件T__T。更要命的是在开会讨论之后,又新增了10个服务,这下可把小徐整懵逼了。还是问下大锤吧,大锤笑了笑,让小徐先自己整理存在问题,顺便想一套解决的方案,进行自动化管理。
需要解决的问题
- 新服务可以自动加入服务列表并且其他服务能够及时响应
- 已有服务信息变化时自动更新服务列表并且其他服务能够及时响应
- 服务正常下线之后,通知服务列表,剔除该服务
- 服务异常下线,服务列表能够短时间内觉察到,剔除该服务
- 各个开发小组手上总是有一份最新的服务列表
解决方案
核心方案:建立一个新的中心服务用来管理服务列表,其他服务作为客户端进行通信,如下:
注册中心
- 建立一个注册中心,注册中心进行各个服务信息的维护,由于考虑高可用,所以注册中心为HA,那么就要考虑文件的一致性,考虑使用Zookeeper作为注册元信息的存储。
- 注册中心开放接口允许其他服务(也可以有鉴权)进行更新注册信息
- 注册中心开放接口允许其他服务进行注册服务的下线
- 注册中心定时和其他服务检测心跳,对无响应的服务进行服务的下线
- 注册中心开放接口允许服务进行所有注册列表信息的拉取
其他服务
- 工程启动时向注册中心进行服务信息的注册
- 工程正常下线时向注册中心发起下线申请
- 定时向注册中心发送心跳
- 定时拉取服务注册列表,使用新的注册列表信息
反馈
小徐使出了浑身解数,整理了以上的内容,大锤看过之后说:“不错,就交给你开发了”。“啊?!”小徐一脸错愕,这下牛皮吹大了,不得了了。“不用你写,有现成的,你去了解下Spring Cloud的Eureka吧。”大锤摸了摸秃秃的头顶。
如何使用Eureka
Eureka Server
- 引入pom文件(根据实际需要选择适当的版本)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
- 启动类加入@EnableEurekaServer注解
@EnableEurekaServer
@SpringBootApplication
public class CenterBootstrap {
public static void main(String[] args) {
SpringApplication.run(CenterBootstrap.class, args);
}
}
- application.yml配置文件中加入相应的配置
spring:
application:
name: Venus-Center
server:
#启动端口
port: 8761
eureka:
instance:
preferIpAddress: true
client:
# 表示是否将自己注册的eureka,由于当前节点就是注册中心所以为false default=true
registerWithEureka: false
# 是否从Eureka Server拉取注册列表,单点的Eureka Server不需要,default=true
fetchRegistry: false
server:
enable-self-preservation: false
Eureka Client
- 引入pom文件
<!--eureka客户端依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- 启动类加入@EnableEurekaClient注解(新版本中不加入此注解也可以,只要引入jar包设置eureka.client.enabled=true,默认为true)或者@EnableDiscoveryClient注解,两者的区别是注册中心有很多实现,比如zookeeper、eureka等。如果注册中心使用eureka推荐使用EnableEurekaClient,如果是其他实现使用EnableDiscoveryClient。
@SpringBootApplication
@EnableEurekaClient
public class AresOrderBootstrap {
public static void main(String[] args) {
SpringApplication.run(AresOrderBootstrap.class, args);
}
}
- application.yml配置文件中加入相应的配置
spring:
application:
name: ares-order
server:
port: 10000
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://localhost:8761/eureka/
常见配置
以上只是常见的一些配置,Spring Cloud本身提供了一套最简配置,使用者也可以进行配置。比如注册中心可以配置多个以达到高可用;比如注册中心可以配置鉴权(也支持用户个性化),客户端只有通过鉴权才能进行注册;比如可以根据Region以及Zone,注册中心位于不同的机房,而客户端服务注册到离其最近的注册中心,服务与服务之间进行Ribbon调用时也会优先物理上同处于一个Zone的服务......
服务基本配置
- spring.application.name
应用名称会展示在eureka界面上 - server.port
服务所占用端口号
eureka.instance
- appname
服务名,默认取 spring.application.name 配置值(此配置的优先级更高),如果没有则为 unknown - instance-id
实例ID,这个值决定了eureka界面上的显示值 - hostname
应用实例主机名 - prefer-ip-address(false)
客户端在注册时使用自己的IP而不是主机名,false时采用hostname的值,为true时有限用ip-address地址,其为空时根据当前网络环境选择第一个非回环IP地址进行注册 - ip-address
应用实例IP - lease-expiration-duration-in-seconds(90)
服务失效时间,失效的服务将被剔除。单位:秒 - lease-renewal-interval-in-seconds(30)
服务续约(心跳)频率,单位:秒 - status-page-url-path(/info)
状态页面的URL,相对路径,默认使用 HTTP 访问,如需使用 HTTPS则要使用绝对路径配置 - health-check-url-path(/health)
健康检查页面的URL,相对路径,默认使用 HTTP 访问,如需使用 HTTPS则要使用绝对路径配置
eureka.client
- service-url.defaultZone(http://localhost:8761/eureka)
Eureka服务器的地址,类型为HashMap,缺省的Key为 defaultZone;如果服务注册中心为高可用集群时,多个注册中心地址以逗号分隔。 - register-with-eureka(true)
是否向注册中心注册自己 - fetch-registry(true)
是否从Eureka获取注册信息 - registry-fetch-interval-seconds(30)
客户端拉取服务注册信息间隔,单位:秒 - health-check.enabled(true)
是否启用客户端健康检查 - eureka-server-connect-timeout-seconds(5)
连接Eureka服务器的超时时间,单位:秒 - eureka-server-read-timeout-seconds(8)
从Eureka服务器读取信息的超时时间,单位:秒 - filter-only-up-instances(true)
获取实例时是否只保留状态为 UP 的实例 - eureka-connection-idle-timeout-seconds(30)
Eureka服务端连接空闲时的关闭时间,单位:秒 - eureka-server-total-connections(200)
从Eureka客户端到所有Eureka服务端的连接总数 - eureka-server-total-connections-per-host(50)
从Eureka客户端到每个Eureka服务主机的连接总数
源码导读
Eureka Server
入口类@EnableEurekaServer
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
继续看EurekaServerMarkerConfiguration
/**
* Responsible for adding in a marker bean to activate
* {@link EurekaServerAutoConfiguration}
*
* @author Biju Kunjummen
*/
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
这里注入了一个Marker,仔细一看Marker类还是空的,我当时就在想是不是逗我呢,看作者上面的注释,加入一个marker bean目的是去激活{@link EurekaServerAutoConfiguration},so 我们就去看这个类
@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter{
......
}
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)的意思就是如果Spring上下文中存在EurekaServerMarkerConfiguration.Marker的Bean的话就会注入EurekaServerAutoConfiguration否则不进行注入,看到这里先不往下看了,后面我们再仔细的去看。
Eureka Client
Eureka client找入口可就费劲了,我使用的是@EnableEurekaClient注解,打开这个类:
/**
* Convenience annotation for clients to enable Eureka discovery configuration
* (specifically). Use this (optionally) in case you want discovery and know for sure that
* it is Eureka you want. All it does is turn on discovery and let the autoconfiguration
* find the eureka classes if they are available (i.e. you need Eureka on the classpath as
* well).
*
* @author Dave Syer
* @author Spencer Gibb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableEurekaClient {
}
不知道是我英文水平不好,还是咋滴,这个注释是没看出门道。通过一番查阅资料之后也没有找到,只好自己进行摸索。
打开我们经常引用的@SpringBootApplication注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
着重看@EnableAutoConfiguration,这个注解会引入当前jar下/META-INF/spring.factories下的配置类,具体原理可以参考SpringBoot之@EnableAutoConfiguration注解
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
一眼就看到了EurekaClientAutoConfiguration这个类,看起来特别像
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@Import(DiscoveryClientOptionalArgsConfiguration.class)
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
public class EurekaClientAutoConfiguration {
}
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)这一句是,如果application.yml(代指配置文件,可能不是这个名称)文件中存在eureka.client.enabled=true的话就注入这个类,默认是true
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)这一句是,如果存在EurekaDiscoveryClientConfiguration.Marker注入的话就注入这个类
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
public class EurekaDiscoveryClientConfiguration {
.......
}
可以看到这个类注入的条件也有eureka.client.enabled属性,上面的@ConditionalOnClass(EurekaClientConfig.class)是指jar包中存在EurekaClientConfig这个类的情况。
其中关于@Conditional注解的内容可以参考SpringBoot重点详解--@Conditional注解。
至此,我们貌似已经找到了源码的入口,下面我们会有时间继续进行源码的阅读,在源码中找到我们原始构想的实现。
Stay hungry,stay foolish.