深入理解DiscoveryClient
Spring Cloud Commons 提供的抽象
最早的时候服务发现注册都是通过DiscoveryClient来实现的,随着版本变迁把DiscoveryClient服务注册抽离出来变成了ServiceRegistry抽象,专门负责服务注册,DiscoveryClient专门负责服务发现。还提供了负载均衡的发现LoadBalancerClient抽象。
DiscoveryClient通过@EnableDiscoveryClient的方式进行启用。
自动向Eureka服务端注册
ServiceRegistry使用的式EurekaServiceRegistry 来做的注册, 注册信息放在EurekaRegistration中
源码:
public interface ServiceRegistry<R extends Registration> {
/**注册
*/
void register(R registration);
/**
* 取消注册
*
*/
void deregister(R registration);
/**
* 关闭服务
*/
void close();
/**
*设置状态
*/
void setStatus(R registration, String status);
/**
* 获取状态
*/
<T> T getStatus(R registration);
}
EurekaServiceRegistry的实现
import java.util.HashMap; import com.netflix.appinfo.InstanceInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import static com.netflix.appinfo.InstanceInfo.InstanceStatus.UNKNOWN; /**
* @author Spencer Gibb
*/
public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> { private static final Log log = LogFactory.getLog(EurekaServiceRegistry.class);
// 获取到EurekaRegistration 信息
@Override public void register(EurekaRegistration reg) {
// 初始化信息
maybeInitializeClient(reg);
if (log.isInfoEnabled()) {
log.info("Registering application "
+ reg.getApplicationInfoManager().getInfo().getAppName()
+ " with eureka with status "
+ reg.getInstanceConfig().getInitialStatus());
}
// 设置实例状态
reg.getApplicationInfoManager()
.setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
// 如果有healthCheckHandler 设置 healthCheck
reg.getHealthCheckHandler().ifAvailable(healthCheckHandler -> reg
.getEurekaClient().registerHealthCheck(healthCheckHandler));
}
private void maybeInitializeClient(EurekaRegistration reg) {
// force initialization of possibly scoped proxies
reg.getApplicationInfoManager().getInfo();
reg.getEurekaClient().getApplications();
}
@Override
public void deregister(EurekaRegistration reg) {
if (reg.getApplicationInfoManager().getInfo() != null) {
if (log.isInfoEnabled()) {
log.info("Unregistering application "
+ reg.getApplicationInfoManager().getInfo().getAppName()
+ " with eureka with status DOWN");
}
//将状态设置为DOWN
reg.getApplicationInfoManager()
.setInstanceStatus(InstanceInfo.InstanceStatus.DOWN);
// shutdown of eureka client should happen with EurekaRegistration.close()
// auto registration will create a bean which will be properly disposed
// manual registrations will need to call close()
}
}
@Override
public void setStatus(EurekaRegistration registration, String status) {
InstanceInfo info = registration.getApplicationInfoManager().getInfo();
// TODO: howto deal with delete properly?
if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) {
registration.getEurekaClient().cancelOverrideStatus(info);
return;
}
// TODO: howto deal with status types across discovery systems?
InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus
.toEnum(status);
registration.getEurekaClient().setStatus(newStatus, info);
}
@Override
public Object getStatus(EurekaRegistration registration) {
String appname = registration.getApplicationInfoManager().getInfo().getAppName();
String instanceId = registration.getApplicationInfoManager().getInfo().getId();
InstanceInfo info = registration.getEurekaClient().getInstanceInfo(appname,
instanceId);
HashMap<String, Object> status = new HashMap<>();
if (info != null) {
status.put("status", info.getStatus().toString());
status.put("overriddenStatus", info.getOverriddenStatus().toString());
}
else {
status.put("status", UNKNOWN.toString());
}
return status;
}
public void close() {
}
}
DefaultLifecycleProcessor中start的调用
public void start() {
startBeans(false);
this.running = true;
}
private void startBeans(boolean autoStartupOnly) {
// 找到所有声明周期中的bean
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
Map<Integer, LifecycleGroup> phases = new HashMap<>();
lifecycleBeans.forEach((beanName, bean) -> {
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
int phase = getPhase(bean);
LifecycleGroup group = phases.get(phase);
if (group == null) {
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
phases.put(phase, group);
}
group.add(beanName, bean);
}
});
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
// 只要不为空,每一个调用start方法
phases.get(key).start();
}
}
}
public void start() {
if (this.members.isEmpty()) {
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Starting beans in phase " + this.phase);
}
Collections.sort(this.members);
for (LifecycleGroupMember member : this.members) {
doStart(this.lifecycleBeans, member.name, this.autoStartupOnly);
}
}
private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) {
Lifecycle bean = lifecycleBeans.remove(beanName);
if (bean != null && bean != this) {
String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
for (String dependency : dependenciesForBean) {
doStart(lifecycleBeans, dependency, autoStartupOnly);
}
if (!bean.isRunning() &&
(!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) {
if (logger.isTraceEnabled()) {
logger.trace("Starting bean '" + beanName + "' of type [" + bean.getClass().getName() + "]");
}
try {
bean.start();
}
catch (Throwable ex) {
throw new ApplicationContextException("Failed to start bean '" + beanName + "'", ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Successfully started bean '" + beanName + "'");
}
}
}
}
DiscoveryClient
public interface DiscoveryClient extends Ordered {
/**
* Default order of the discovery client.
*/
int DEFAULT_ORDER = 0;
/**
* A human-readable description of the implementation, used in HealthIndicator. 提供一些描述信息
* @return The description.
*/
String description();
/**
* Gets all ServiceInstances associated with a particular serviceId.
* @param serviceId The serviceId to query.
* @return A List of ServiceInstance. 根据serviceid 找到ServiceInstance
*/
List<ServiceInstance> getInstances(String serviceId);
/**
* @return All known service IDs. 获取所有的服务
*/
List<String> getServices();
/**
* Default implementation for getting order of discovery clients.
* @return order
*/
@Override
default int getOrder() {
return DEFAULT_ORDER;
}
}
Eureka的实现
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map; import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications; import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.core.Ordered;
import org.springframework.util.Assert; import static com.netflix.appinfo.InstanceInfo.PortType.SECURE; /**
* A {@link DiscoveryClient} implementation for Eureka.
*
* @author Spencer Gibb
* @author Tim Ysewyn
*/
public class EurekaDiscoveryClient implements DiscoveryClient { /**
* Client description {@link String}.
*/
public static final String DESCRIPTION = "Spring Cloud Eureka Discovery Client"; private final EurekaClient eurekaClient; private final EurekaClientConfig clientConfig; @Deprecated
public EurekaDiscoveryClient(EurekaInstanceConfig config, EurekaClient eurekaClient) {
this(eurekaClient, eurekaClient.getEurekaClientConfig());
} public EurekaDiscoveryClient(EurekaClient eurekaClient,
EurekaClientConfig clientConfig) {
this.clientConfig = clientConfig;
this.eurekaClient = eurekaClient;
} @Override
public String description() {
return DESCRIPTION;
}
// 通过serviceid 获取到 instanceInfo信息
@Override
public List<ServiceInstance> getInstances(String serviceId) {
List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId,
false);
List<ServiceInstance> instances = new ArrayList<>();
for (InstanceInfo info : infos) {
instances.add(new EurekaServiceInstance(info));
}
return instances;
} @Override
public List<String> getServices() {
Applications applications = this.eurekaClient.getApplications();
if (applications == null) {
return Collections.emptyList();
}
List<Application> registered = applications.getRegisteredApplications();
List<String> names = new ArrayList<>();
for (Application app : registered) {
if (app.getInstances().isEmpty()) {
continue;
}
names.add(app.getName().toLowerCase()); }
return names;
} @Override
public int getOrder() {
return clientConfig instanceof Ordered ? ((Ordered) clientConfig).getOrder()
: DiscoveryClient.DEFAULT_ORDER;
} /**
* An Eureka-specific {@link ServiceInstance} implementation.
*/
public static class EurekaServiceInstance implements ServiceInstance { private InstanceInfo instance; public EurekaServiceInstance(InstanceInfo instance) {
Assert.notNull(instance, "Service instance required");
this.instance = instance;
} public InstanceInfo getInstanceInfo() {
return instance;
} @Override
public String getInstanceId() {
return this.instance.getId();
} @Override
public String getServiceId() {
return this.instance.getAppName();
} @Override
public String getHost() {
return this.instance.getHostName();
} @Override
public int getPort() {
if (isSecure()) {
return this.instance.getSecurePort();
}
return this.instance.getPort();
} @Override
public boolean isSecure() {
// assume if secure is enabled, that is the default
return this.instance.isPortEnabled(SECURE);
} @Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
} @Override
public Map<String, String> getMetadata() {
return this.instance.getMetadata();
} } }
总结:
自动配置注册:
EurekaAutoServiceRegistration::start(在DefaultLifecycleProcessor中调用):
1. 先检查port;
2. 如果没有启动过并且非安全的端口大于0,则进行注册(通过调用org.springframework.cloud.client.serviceregistry.ServiceRegistry接口的实例).
3. 注册事件;
4. 设置运行状态
注册:
org.springframework.cloud.client.serviceregistry.ServiceRegistry->注册和取消注册
->
EurekaServiceRegistry::register从参数获取EurekaRegistration,
并初始化EurekaRegistration,通过ApplicationInfoManager设置实例的状态,
如果有healthCheck则注册healthCheck.
自动配置取消注册:
1. 调用org.springframework.cloud.client.serviceregistry.ServiceRegistry接口的实例的deregister方法.
2. 设置状态
取消注册(EurekaServiceRegistry::deregister):
将状态设置成DOWN.
ServiceInstance
深入理解DiscoveryClient的更多相关文章
- SpringCloud学习之DiscoveryClient探究
当我们使用@DiscoveryClient注解的时候,会不会有如下疑问:它为什么会进行注册服务的操作,它不是应该用作服务发现的吗?下面我们就来深入的来探究一下其源码. 一.Springframewor ...
- 深入理解Ribbon之源码解析
什么是Ribbon Ribbon是Netflix公司开源的一个负载均衡的项目,它属于上述的第二种,是一个客户端负载均衡器,运行在客户端上.它是一个经过了云端测试的IPC库,可以很好地控制HTTP和TC ...
- 深入理解Eureka - Eureka Client获取注册信息机制
深入理解Eureka - Eureka Client获取注册信息机 Eureka Client提供了定时获取注册信息的机制.Eureka Client获取注册信息的所有逻辑都在DiscoveryCli ...
- 深入理解Eureka之源码解析
转载请标明出处: http://blog.csdn.net/forezp/article/details/73017664 本文出自方志朋的博客 Eureka的一些概念 Register:服务注册 当 ...
- 深入理解@LoadBalanced注解的实现原理与客户端负载均衡
前提 在阅读这篇博客之前,希望你对SpringCloud套件熟悉和理解,更希望关注下微服务开发平台 概述 在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate ...
- SpringCloud的入门学习之概念理解、Eureka服务注册与发现入门
1.微服务与微服务架构.微服务概念如下所示: 答:微服务强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题.提供落地对应服务的一个服务应用,狭意的看,可以看作Eclipse里面的一个个微服务 ...
- 关于MQ 消息队列的通俗理解和 rabbitMQ 使用
消息队列,一听很高大上,现在很多分布式系统都在用这个消息中间件 网上一搜, 说的都是些原理. 说下我的通俗理解, 你网上买了, 快递员给你投递, 会出现什么问题呢? 1 你不定时在家, 快递员 来了 ...
- 理解CSS视觉格式化
前面的话 CSS视觉格式化这个词可能比较陌生,但说起盒模型可能就恍然大悟了.实际上,盒模型只是CSS视觉格式化的一部分.视觉格式化分为块级和行内两种处理方式.理解视觉格式化,可以确定得到的效果是应 ...
- 彻底理解AC多模式匹配算法
(本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...
随机推荐
- vue.js(18)--父组件向子组件传值
子组件是不能直接使用父组件中数据的,需要进行属性绑定(v-bind:自定义属性名=“msg”),绑定后需要在子组件中使用props[‘自定义属性名’]数组来定义父组件的自定义名称. props数组中的 ...
- 2018-5-5-UWP-和-WPF-对比
title author date CreateTime categories UWP 和 WPF 对比 lindexi 2018-05-05 17:23:33 +0800 2018-04-16 20 ...
- linux测试 Sersync 是否正常
[root@SERSYNC web]# for i in {1..10000};do echo 123456 > /data/web/$i &>/dev/null;do ne [r ...
- [译]深度学习(Yann LeCun)
深度学习 严恩·乐库 约书亚•本吉奥 杰弗里·希尔顿 摘要深度学习是计算模型,是由多个处理层学习多层次抽象表示的数据.这些方法极大地提高了语音识别.视觉识别.物体识别.目标检测和许多其他领域如药物 ...
- Tree命令安装和使用
Tree命令简介 tree是一种递归目录列表命令,产生一个深度缩进列表文件,这是彩色的ALA dircolors如果ls_colors设置环境变量和输出是TTY.树已经被移植和报道以下操作系统下工作: ...
- 2019ICPC网赛南京站B题 super_log(欧拉降幂
https://nanti.jisuanke.com/t/41299 题意:让算a^(a^(a^(...))),一共b个a, (mod p)的结果. 思路:这是个幂塔函数,用欧拉降幂公式递归求解. # ...
- uboot学习之五-----uboot如何启动Linux内核
uboot和内核到底是什么?uboot实质就是一个复杂的裸机程序:uboot可以被配置也可以做移植: 操作系统内核本身就是一个裸机程序,和我们学的uboot和其他裸机程序没有本质的区别:区别就是我们操 ...
- vue @import css
@import '~@/assets/scss/helpers/_mixin'; 原理:CSS loader 会把把非根路径的url解释为相对路径, 加~前缀才会解释成模块路径.
- ORM大结局
1.添加记录: # 一对多的添加方式: #pub_obj=Publish.objects.filter(name="橙子出版社").first() # book=Book.obje ...
- java源码生成可运行jar
参考资料:https://blog.csdn.net/whatday/article/details/54767187 源码目录层级如下: