深入理解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状态,表示起 ...
随机推荐
- GitHub 搭建博客,出现 hexo g -d 报错
想搭建一个个人博客,但是在将博客推送到Github上的时候在git bash 下运行hexo g -d命令出现错误: 错误如下: fatal: HttpRequestException encoun ...
- git的HEAD指针操作
学习操作HEAD指针,具体如下: - 查看Git版本信息 - 移动指针 - 通过移动HEAD指针恢复数据 - 合并版本 拓扑图:
- 【改】shell 判断文件中有无特定子串方法(grep)
转自:https://blog.csdn.net/zhuguiqin1/article/details/79160923 利用grep执行的命令结束代码$?的值来判断是否已经grep到特定的值. 当$ ...
- CAS实现SSO单点登录
环境 cas-server-4.1.8,cas-client-3.4.0,Java-8,Maven-3,Tomcat-7.0.72 CAS Server 安装 点此进入 CAS 下载列表,选择下载 c ...
- Java8 的一些新特性的学习理解
近期在学习队列相关的一些知识,在学习过程中发现Iterable<T>接口中新增了两个新的方法,出于好奇,就想知道这是什么东东,干什么用的.俗话说:实践出真知,所以就有了以下反复的测试. 先 ...
- [sqlmap 源码阅读] heuristicCheckSqlInjection 探索式注入
上面是探索式注入时大致调用过程,注入 PAYLOAD 1.)("'(.((. , 数据库报错,通过报错信息获取网站数据库类型(kb.dbms),并将保存报错(lasterrorpage). ...
- 解决安装mysql-connector-odbc-5.3.2 错误1918……不能加载安装或转换器库……的BUG
还是在虚拟机Windows Server 2003上安装mysql-connector-odbc-5.3.2,装着装着就报错了,大致是“错误1918……不能加载安装或转换器库……”,问我Retry,I ...
- Python3-unittest测试框架之DDT数据驱动
unittest测试框架之DDT数据驱动 ddt的使用 DDT数据驱动 DDT:Data Driver Test(数据驱动测试) 数据驱动思想:数据和用例进行分离,通过外部数据去生成测试用例 安装 p ...
- 使用 Visual Studio 调试器附加到运行的进程
为什么调试附加进程? Visual Studio 调试器可以附加到在 Visual Studio 外运行的进程. 可以使用此附加功能执行以下操作: 调试并非在 Visual Studio 中创建的应用 ...
- 【Nacos】Nacos安装
1.Nacos简介 Nacos是阿里巴巴集团开源的一个易于使用的平台,专为动态服务发现,配置和服务管理而设计.它可以帮助您轻松构建云本机应用程序和微服务平台. Nacos基本上支持现在所有类型的服务, ...