nacos服务注册
- 序言
- 1.源码环境搭建
- 1.1idea运行源码
- 1.2 登录nacos
- 2.服务注册分析
- 2.1 客户端
- 2.1.1容器启动监听
- 2.1.2注册前初始化
- 2.1.3注册服务
- 2.2 服务端
- 2.2.1注册
- 2.2.2重试机制
- 3.注意事项
序言
本文章是分析的是nacos版本2.2
这次版本是一次重大升级优化,由原来(临时服务)服务注册tcp改成grpc方式,减少资源消耗,服务推送由udp也改为grpc
1.源码环境搭建
- 源码下载
1.1idea运行源码
- 导入
- clean-install-reimport
- 配置
- 启动
单机运行 启动项配置 VM option
1.2 登录nacos
2.服务注册分析
2.1 客户端
首先让我去实现一个服务注册,我们如何去做?
思考一:
是不是项目启动的时候进行服务注册
思考二:
springboot自动装载,是不是引入nacos依赖,就可以实现服务注册功能
按照上面的思路去分析注册大概过程就很清晰了
2.1.1容器启动监听
- 自动加载入口
@Bean
@ConditionalOnBean({AutoServiceRegistrationProperties.class})
public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration);
}
public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry, AutoServiceRegistrationProperties autoServiceRegistrationProperties, NacosRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
- web容器监听
public abstract class AbstractAutoServiceRegistration<R extends Registration> implements AutoServiceRegistration, ApplicationContextAware, ApplicationListener<WebServerInitializedEvent> {
- 綁定事件
public void onApplicationEvent(WebServerInitializedEvent event) {
this.bind(event);
}
/** @deprecated */
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (!(context instanceof ConfigurableWebServerApplicationContext) || !"management".equals(((ConfigurableWebServerApplicationContext)context).getServerNamespace())) {
this.port.compareAndSet(0, event.getWebServer().getPort());
this.start();
}
}
public void start() {
if (!this.isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
} else {
if (!this.running.get()) {
this.context.publishEvent(new InstancePreRegisteredEvent(this, this.getRegistration()));
this.register();
if (this.shouldRegisterManagement()) {
this.registerManagement();
}
this.context.publishEvent(new InstanceRegisteredEvent(this, this.getConfiguration()));
this.running.compareAndSet(false, true);
}
}
}
2.1.2注册前初始化
- 获取namingservice服务
public void register(Registration registration) {
//创建namingservice服务
NamingService namingService = this.namingService();
//注册
namingService.registerInstance(serviceId, group, instance);
}
- 判断服务是否存在
public NamingService getNamingService() {
//不存在进行创建
if (Objects.isNull(this.namingService)) {
this.buildNamingService(this.nacosDiscoveryProperties.getNacosProperties());
}
return this.namingService;
}
- 创建NamingService服务
NamingService naming = NamingFactory.createNamingService(properties);
public NacosNamingService(Properties properties) throws NacosException {
//nacosNamingService 客户端初始化
init(properties);
}
private void init(Properties properties) throws NacosException {
final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties);
ValidatorUtils.checkInitParam(nacosClientProperties);
//获取命名空间
this.namespace = InitUtils.initNamespaceForNaming(nacosClientProperties);
InitUtils.initSerialization();
InitUtils.initWebRootContext(nacosClientProperties);
initLogName(nacosClientProperties);
//通知时间范围
this.notifierEventScope = UUID.randomUUID().toString();
//改变消息通知者
this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope);
//消息中心注册事件
NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
//注册订阅者
NotifyCenter.registerSubscriber(changeNotifier);
//服务信息持有者
this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties);
//创建客户端代理 去委托NamingClientProxyDelegate
this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, changeNotifier);
}
2.1.3注册服务
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
instance);
//缓存实例数据,增强可靠性
redoService.cacheInstanceForRedo(serviceName, groupName, instance);
//注册
doRegisterService(serviceName, groupName, instance);
}
public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException {
InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
NamingRemoteConstants.REGISTER_INSTANCE, instance);
//请求注册
requestToServer(request, Response.class);
//设置缓存数据状态为已经注册状态
redoService.instanceRegistered(serviceName, groupName);
}
2.2 服务端
2.2.1注册
private InstanceResponse registerInstance(Service service, InstanceRequest request, RequestMeta meta)
throws NacosException {
clientOperationService.registerInstance(service, request.getInstance(), meta.getConnectionId());
NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(),
meta.getClientIp(), true, service.getNamespace(), service.getGroup(), service.getName(),
request.getInstance().getIp(), request.getInstance().getPort()));
return new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE);
}
@Override
public void registerInstance(Service service, Instance instance, String clientId) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
//获取当前的 Service (没有就新创建)
Service singleton = ServiceManager.getInstance().getSingleton(service);
if (!singleton.isEphemeral()) {
throw new NacosRuntimeException(NacosException.INVALID_PARAM,
String.format("Current service %s is persistent service, can't register ephemeral instance.",
singleton.getGroupedServiceName()));
}
Client client = clientManager.getClient(clientId);
if (!clientIsLegal(client, clientId)) {
return;
}
InstancePublishInfo instanceInfo = getPublishInfo(instance);
client.addServiceInstance(singleton, instanceInfo);
client.setLastUpdatedTime();
client.recalculateRevision();
// 发布通知注册时间
NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));
NotifyCenter
.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}
2.2.2重试机制
为了确保注册成功,会从缓存中,把注册状态为失败的注册实例进行重新注册
public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy) {
this.redoExecutor = new ScheduledThreadPoolExecutor(REDO_THREAD, new NameThreadFactory(REDO_THREAD_NAME));
this.redoExecutor.scheduleWithFixedDelay(new RedoScheduledTask(clientProxy, this), DEFAULT_REDO_DELAY,
DEFAULT_REDO_DELAY, TimeUnit.MILLISECONDS);
}
public void run() {
if (!redoService.isConnected()) {
LogUtils.NAMING_LOGGER.warn("Grpc Connection is disconnect, skip current redo task");
return;
}
try {
//重新注册
redoForInstances();
//重新订阅
redoForSubscribes();
} catch (Exception e) {
LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e);
}
}
3.注意事项
-
idea 运行源码找不到类
这时,你需要clean 再install 最后reimport,就不会问题
-
启动时nacos没有以单例模式启动也会报错