Nacos源码学习计划-Day19-Nacos2.x-客户端gRPC发起注册请求
ZealSinger 发布于 阅读:253 技术文档
但是Nacos依旧在更新,其中在Nacos 2.x的版本上,进行了不少的更新包括但不局限于
-
Nacos集群内部的通讯由HTTP方式变更为了gRPC
-
Nacos的Raft协议使用了蚂蚁内部的JRaft且优化
-
Nacos1.x版本中对于注册表采用双层Map的结构,而在Nacos2.x中进行了轻量化处理
-
Nacos在1.x版本中允许一个服务同时存在持久化和非持久化实例,持久化属性只是作为实例的一个元数据进行存储和识别,这就导致了实际情况下运维人员很苦恼且从系统架构层面看来存在矛盾。所以在Nacos 2.x中简化了Nacos的服务数据模型,是否持久化的数据抽象至服务级别且不再允许一个服务同时存在持久化和非持久化实例,实例的是否持久化属性配置继承服务的是否持久化属性配置
-
Nacos配置管理中,SDK和Server之间的一致性协议是通过判断MD5值是否一致来判断的。
Nacos1.4版本中,采用HTTP1.1的短链接模拟长连接,每30s发一个心跳和Server对比SDK配置的MD5值是否和Server保持一致,如果一直则hold住链接,如果有不一致配置,则把不一致的配置返回,返回SDK获取最新配置值 Nacos2.x版本中,不再使用30s一次的长轮询,而是升级成为了长链接模式,配置变更,启动建立长链接,配置变更后服务端推送变更配置列表,然后SDK拉去配置更新,通讯效率大大提高
通过阿里自己的测试报告,面对大规模注册情况下,Nacos2.0在稳定场景下的能力至少是Nacos1.x的9倍,达到稳定状态后,Nacos2.0的表现也更加优秀,在客户端和实例约10倍数量的情况下却能有更小的CPU消耗,这个大幅度的优化,在代码层面肯定是做了不少优化的,所以Nacos2.x版本也是十分值得我们学习的。
客户端发起gRPC进行服务注册ss
大伙儿还记得在Nacos1.4中客户端是如何实现的自动发起注册,且注册逻辑是怎么处理的么? 不记得的同学可以回去看一下
在Nacos1.4的版本中,客户端会利用Spring的Event事件通知机制,当Spring容器初始化完毕,会发送一个WebServerInitializedEvent初始化完毕事件,Nacos中定义了监听这个事件的监听器,当监听到这个Event就可以开启发送注册的逻辑,即NacosServiceRegistry 类的 register方法,其底层就是通过HTTP请求调用Nacos服务端的注册接口
那我们现在来看看Nacos2.x版本下,这个register方法的逻辑,可以看到,register整体的逻辑和Nacos1.4版本是一样的
public void register(Registration registration) {
if (StringUtils.isEmpty(registration.getServiceId())) {
log.warn("No service to register for nacos client...");
return;
}
NamingService namingService = namingService();
// 获取服务ID、分组
String serviceId = registration.getServiceId();
String group = nacosDiscoveryProperties.getGroup();
// 创建 instance 对象
Instance instance = getNacosInstanceFromRegistration(registration);
try {
// 发起服务注册,核心方法
namingService.registerInstance(serviceId, group, instance);
log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,
instance.getIp(), instance.getPort());
}
catch (Exception e) {
if (nacosDiscoveryProperties.isFailFast()) {
log.error("nacos registry, {} register failed...{},", serviceId,
registration.toString(), e);
rethrowRuntimeException(e);
}
else {
log.warn("Failfast is false. {} register failed...{},", serviceId,
registration.toString(), e);
}
}
}
那我们继续往下看,注册的核心方法registerInstance()方法,可以看到在2.x版本中的registerInstance()方法和1.4版本中的内容是不一样的了,这里把1.4中的代码贴出来对比一下
可以看到1.4中还会判断是否为临时实例,而很明显2.x版本中不会进行区分判断
// 2.x版本
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);
// 调用注册方法
clientProxy.registerService(serviceName, groupName, instance);
}
// 1.4.x版本
public void registerInstance(String serviceName, String groupName, Instance instance) throws Nacos Exception {
NamingUtils.checkInstanceIsLegal(instance);
String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName);
if (instance.isEphemeral()) {
BeatInfo beatInfo = beatReactor.buildBeatInfo(groupedServiceName, instance);
beatReactor.addBeatInfo(groupedServiceName, beatInfo);
}
serverProxy.registerService(groupedServiceName, groupName, instance);
}
接着往下看,也就是去看registerService()方法的逻辑,这个是接口中的方法,有三个实现,怎么找对应的实现类,我们已经介绍过很多方法了无非就是三种
-
暴力法:直接每个实现类都去看一下
-
直接法:运行对应的服务debug追踪一下
-
观察法:找到对应的调用方法的对象,看看其初始化是如何进行的
我们这里其实任何一种方式都可以,如下,我是看到了调用registerService方法的是NacosNamingService类中的clientProxy成员进行的调用,而该成员的初始化是在同类下的init方法中,从init方法块中我们可以看到其对应的类型是NamingClientProxyDelegate

代码我们跟到 NamingClientProxyDelegate 类的 registerService 方法,在方法中我们看到了调用了 getExecuteClientProxy 方法,然后判断该实例是不是临时实例,如果是就返回 grpcClient、否则就返回 httpClient 对象,代码如下:
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
}
private NamingClientProxy getExecuteClientProxy(Instance instance) {
return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
}
从这里我们也可以看出Nacos对于临时实例和非临时实例的不同处理:如果注册的实例是临时实例,那么就会走 gRPC 的请求,非临时实例还是会走 HTTP 的方式
HTTP方式的我们就不多说的了,之前1.4中分析过了,我们这次主要来看gRPC的逻辑
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);
}
接下来看到requestToServer方法
private <T extends Response> T requestToServer(AbstractNamingRequest request, Class<T> responseClass)
throws NacosException {
try {
request.putAllHeader(
getSecurityHeaders(request.getNamespace(), request.getGroupName(), request.getServiceName()));
Response response =
requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout);
if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) {
throw new NacosException(response.getErrorCode(), response.getMessage());
}
if (responseClass.isAssignableFrom(response.getClass())) {
return (T) response;
}
NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'",
response.getClass().getName(), responseClass.getName());
} catch (Exception e) {
throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e);
}
throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response");
}
看到rpcClient.request(request),调用了 rpcClient 对象的请求方法。源码分析到这里,我们也就明白了,Nacos 它在 RPC 的基础之上封装了一层 GrpcClient 对象,底层还是调用了 RPC 那一套。可以看到如下RequestGrpc.RequestFutureStub grpcFutureServiceStub,这个成员的类型就是gRPC中的stub对象
不了解的小伙伴可以事后学习一下 RPC 相关的基本理论知识

文章标题:Nacos源码学习计划-Day19-Nacos2.x-客户端gRPC发起注册请求
文章链接:https://zealsinger.xyz/?post=45
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明来自ZealSinger !
如果觉得文章对您有用,请随意打赏。
您的支持是我们继续创作的动力!
微信扫一扫
支付宝扫一扫