2、Nacos 服务注册服务端源码分析(一)

上篇说到Nacos客户端的注册流程(没看过的小伙伴可以点击这里1、Nacos 服务注册客户端源码分析),那注册发送到服务端,服务端是如何处理的呢?

本篇就通过源码来分析一下服务端的处理流程。

Nacos注册服务端源码解析

回顾一下,客户端注册代码。客户端通过com.alibaba.nacos.common.remote.client.RpcClient#request(com.alibaba.nacos.api.remote.request.Request, long)进行调用,那调用后服务端的代码入口在哪里呢?

我们知道Nacos注册服务是通过Grpc远程调用,现在客户端发起调用,肯定有个Grpc的服务端在接受服务。

Grpc 服务端

com.alibaba.nacos.core.remote.BaseRpcServer是Grpc服务端的一个抽象类,其有一个PostConstruct,表示在构造方法后执行,下面看看它具体做了什么事情

@PostConstruct
public void start() throws Exception {
    String serverName = getClass().getSimpleName();
    Loggers.REMOTE.info("Nacos {} Rpc server starting at port {}", serverName, getServicePort());
	// 启动Grpc的服务端
    startServer();

    Loggers.REMOTE.info("Nacos {} Rpc server started at port {}", serverName, getServicePort());
    // 添加一个关闭的钩子函数,当虚拟机接受关闭退出信号的时候关闭服务,具体的也就是关闭Grpc的服务端
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        Loggers.REMOTE.info("Nacos {} Rpc server stopping", serverName);
        try {
            BaseRpcServer.this.stopServer();
            Loggers.REMOTE.info("Nacos {} Rpc server stopped successfully...", serverName);
        } catch (Exception e) {
            Loggers.REMOTE.error("Nacos {} Rpc server stopped fail...", serverName, e);
        }
    }));

}

这个startServer()是一个抽象方法,具体由其子类进行实现,查看子类,发现是由BaseGrpcServer实现

@Override
public void startServer() throws Exception {
    final MutableHandlerRegistry handlerRegistry = new MutableHandlerRegistry();

    // server interceptor to set connection id.
    ServerInterceptor serverInterceptor = new ServerInterceptor() {
        @Override
        public <T, S> ServerCall.Listener<T> interceptCall(ServerCall<T, S> call, Metadata headers,
                                                           ServerCallHandler<T, S> next) {
            Context ctx = Context.current()
                .withValue(CONTEXT_KEY_CONN_ID, call.getAttributes().get(TRANS_KEY_CONN_ID))
                .withValue(CONTEXT_KEY_CONN_REMOTE_IP, call.getAttributes().get(TRANS_KEY_REMOTE_IP))
                .withValue(CONTEXT_KEY_CONN_REMOTE_PORT, call.getAttributes().get(TRANS_KEY_REMOTE_PORT))
                .withValue(CONTEXT_KEY_CONN_LOCAL_PORT, call.getAttributes().get(TRANS_KEY_LOCAL_PORT));
            if (REQUEST_BI_STREAM_SERVICE_NAME.equals(call.getMethodDescriptor().getServiceName())) {
                Channel internalChannel = getInternalChannel(call);
                ctx = ctx.withValue(CONTEXT_KEY_CHANNEL, internalChannel);
            }
            return Contexts.interceptCall(ctx, call, headers, next);
        }
    };
	// 注册服务
    addServices(handlerRegistry, serverInterceptor);
	// 创建grpc的server端
    server = ServerBuilder.forPort(getServicePort()).executor(getRpcExecutor())
        .maxInboundMessageSize(getInboundMessageSize()).fallbackHandlerRegistry(handlerRegistry)
        .compressorRegistry(CompressorRegistry.getDefaultInstance())
        .decompressorRegistry(DecompressorRegistry.getDefaultInstance())
        .addTransportFilter(new ServerTransportFilter() {
            @Override
            public Attributes transportReady(Attributes transportAttrs) {
                InetSocketAddress remoteAddress = (InetSocketAddress) transportAttrs
                    .get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
                InetSocketAddress localAddress = (InetSocketAddress) transportAttrs
                    .get(Grpc.TRANSPORT_ATTR_LOCAL_ADDR);
                int remotePort = remoteAddress.getPort();
                int localPort = localAddress.getPort();
                String remoteIp = remoteAddress.getAddress().getHostAddress();
                Attributes attrWrapper = transportAttrs.toBuilder()
                    .set(TRANS_KEY_CONN_ID, System.currentTimeMillis() + "_" + remoteIp + "_" + remotePort)
                    .set(TRANS_KEY_REMOTE_IP, remoteIp).set(TRANS_KEY_REMOTE_PORT, remotePort)
                    .set(TRANS_KEY_LOCAL_PORT, localPort).build();
                String connectionId = attrWrapper.get(TRANS_KEY_CONN_ID);
                Loggers.REMOTE_DIGEST.info("Connection transportReady,connectionId = {} ", connectionId);
                return attrWrapper;

            }

            @Override
            public void transportTerminated(Attributes transportAttrs) {
                String connectionId = null;
                try {
                    connectionId = transportAttrs.get(TRANS_KEY_CONN_ID);
                } catch (Exception e) {
                    // Ignore
                }
                if (StringUtils.isNotBlank(connectionId)) {
                    Loggers.REMOTE_DIGEST
                        .info("Connection transportTerminated,connectionId = {} ", connectionId);
                    connectionManager.unregister(connectionId);
                }
            }
        }).build();
	// 启动grpc server
    server.start();
}

注册服务的过程中就能看到添加了Grpc的处理方式

private void addServices(MutableHandlerRegistry handlerRegistry, ServerInterceptor... serverInterceptor) {

    // unary common call register.
    final MethodDescriptor<Payload, Payload> unaryPayloadMethod = MethodDescriptor.<Payload, Payload>newBuilder()
        .setType(MethodDescriptor.MethodType.UNARY)
        .setFullMethodName(MethodDescriptor.generateFullMethodName(REQUEST_SERVICE_NAME, REQUEST_METHOD_NAME))
        .setRequestMarshaller(ProtoUtils.marshaller(Payload.getDefaultInstance()))
        .setResponseMarshaller(ProtoUtils.marshaller(Payload.getDefaultInstance())).build();
	// 处理类
    final ServerCallHandler<Payload, Payload> payloadHandler = ServerCalls
        .asyncUnaryCall((request, responseObserver) -> grpcCommonRequestAcceptor.request(request, responseObserver));

    final ServerServiceDefinition serviceDefOfUnaryPayload = ServerServiceDefinition.builder(REQUEST_SERVICE_NAME)
        .addMethod(unaryPayloadMethod, payloadHandler).build();
    handlerRegistry.addService(ServerInterceptors.intercept(serviceDefOfUnaryPayload, serverInterceptor));

    // bi stream register.
    // 双向流处理类
    final ServerCallHandler<Payload, Payload> biStreamHandler = ServerCalls.asyncBidiStreamingCall(
        (responseObserver) -> grpcBiStreamRequestAcceptor.requestBiStream(responseObserver));

    final MethodDescriptor<Payload, Payload> biStreamMethod = MethodDescriptor.<Payload, Payload>newBuilder()
        .setType(MethodDescriptor.MethodType.BIDI_STREAMING).setFullMethodName(MethodDescriptor
                                                                               .generateFullMethodName(REQUEST_BI_STREAM_SERVICE_NAME, REQUEST_BI_STREAM_METHOD_NAME))
        .setRequestMarshaller(ProtoUtils.marshaller(Payload.newBuilder().build()))
        .setResponseMarshaller(ProtoUtils.marshaller(Payload.getDefaultInstance())).build();

    final ServerServiceDefinition serviceDefOfBiStream = ServerServiceDefinition
        .builder(REQUEST_BI_STREAM_SERVICE_NAME).addMethod(biStreamMethod, biStreamHandler).build();
    handlerRegistry.addService(ServerInterceptors.intercept(serviceDefOfBiStream, serverInterceptor));

}

而这两个类也就是实现了nacos_grpc_service.proto的两个子处理类。

public class GrpcRequestAcceptor extends RequestGrpc.RequestImplBase
public class GrpcBiStreamRequestAcceptor extends BiRequestStreamGrpc.BiRequestStreamImplBase

对应nacos_grpc_service.proto中的两个方法处理

service Request {
  // Sends a commonRequest
  rpc request (Payload) returns (Payload) {
  }
}

service BiRequestStream {
  // Sends a biStreamRequest
  rpc requestBiStream (stream Payload) returns (stream Payload) {
  }
}

我们再看下BaseRpcServer,这个类是一个抽象类,它有两个子类,分别是GrpcClusterServerGrpcSdkServer。这两个类是从哪里创建的呢?又是做什么用的呢?带着这两个问题,我们分析一下这两个Server类。

GrpcClusterServerGrpcSdkServer分析

Server的创建

这两个类其实很简单,这里直接贴一下源码

@Service
public class GrpcSdkServer extends BaseGrpcServer {
    
    @Override
    public int rpcPortOffset() {
        return Constants.SDK_GRPC_PORT_DEFAULT_OFFSET;
    }
    
    @Override
    public ThreadPoolExecutor getRpcExecutor() {
        return GlobalExecutor.sdkRpcExecutor;
    }
}

@Service
public class GrpcClusterServer extends BaseGrpcServer {
    
    @Override
    public int rpcPortOffset() {
        return Constants.CLUSTER_GRPC_PORT_DEFAULT_OFFSET;
    }
    
    @Override
    public ThreadPoolExecutor getRpcExecutor() {
        if (!GlobalExecutor.clusterRpcExecutor.allowsCoreThreadTimeOut()) {
            GlobalExecutor.clusterRpcExecutor.allowCoreThreadTimeOut(true);
        }
        return GlobalExecutor.clusterRpcExecutor;
    }
}

就两个方法,一个设置端口,一个是获取连接池。然后在类上有个@Servcice的注解。这个注解是spring的注解,只要归spring扫描到的话,就能够自动帮我们创建并放入到spring容器中。而Nacos的启动类是com.alibaba.nacos.Nacos,这里放一下源码。

@SpringBootApplication(scanBasePackages = "com.alibaba.nacos")
@ServletComponentScan
@EnableScheduling
public class Nacos {
    
    public static void main(String[] args) {
        SpringApplication.run(Nacos.class, args);
    }
}

可以看出这是一个Spring Boot工程,扫描的时候扫描了com.alibaba.nacos包,当然也包括其子包。这样这两个Server类均会被扫描到,进行注入,从而调用com.alibaba.nacos.core.remote.BaseRpcServer#start,进而进行一系列的创建操作。

Server的作用

这里创建了两个Server,从Server的名字上能看出,一个是SDK服务调用,一个是Cluster服务调用,但是这两个类并没有写的很清楚到底做啥用的。看方法也基本一致,那我们就从客户端入口,看客户端调用有啥区别来识别服务端的区别。

在两个类中,有个端口偏移量的处理,在GrpcSdkServer中的是SDK_GRPC_PORT_DEFAULT_OFFSET,也就是偏移1000,而GrpcClusterServer中的是CLUSTER_GRPC_PORT_DEFAULT_OFFSET为1001。从端口我们去搜索客户端代码,发现客户端的两个类,分别是GrpcSdkClientGrpcClusterClient。他们的创建在com.alibaba.nacos.common.remote.client.RpcClientFactory中,其代码如下

public static RpcClient createClient(String clientName, ConnectionType connectionType, Integer threadPoolCoreSize,Integer threadPoolMaxSize, Map<String, String> labels) {
    if (!ConnectionType.GRPC.equals(connectionType)) {
        throw new UnsupportedOperationException("unsupported connection type :" + connectionType.getType());
    }

    return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> {
        LOGGER.info("[RpcClientFactory] create a new rpc client of " + clientName);
        try {
            // 创建SDK客户端
            return new GrpcSdkClient(clientNameInner, threadPoolCoreSize, threadPoolMaxSize, labels);
        } catch (Throwable throwable) {
            LOGGER.error("Error to init GrpcSdkClient for client name :" + clientName, throwable);
            throw throwable;
        }

    });
}


public static RpcClient createClusterClient(String clientName, ConnectionType connectionType,Map<String, String> labels) {
    // 创建cluster的客户端
    return createClusterClient(clientName, connectionType, null, null, labels);
}

从这两段代码看不出区别,不过别急,马上就能看到区别了。

再看下是谁在调用这两个方法。

createClient是被ClientWorkerNamingGrpcClientProxy调用,这两个类是位于nacos-client当中的,也就是一般我们需要引入的客户端,由客户端发起调用。

2、Nacos 服务注册服务端源码分析(一)_第1张图片

createClusterClient是被ClusterRpcClientProxyRpcClientFactory调用,这两个类位于nacos-core中。nacos-core是服务端引用,在服务与服务端内部调用。

2、Nacos 服务注册服务端源码分析(一)_第2张图片
至此,这两个f类真相大白了。

如果是我们的业务工程代码,引入的是nacos-client,这样调用的服务端就是GrpcSdkServer,由这个服务来处理客户端请求,针对是外部服务的调用。而GrpcClusterServer不处理外部的请求,处理的内部的服务器端和服务器端的调用。

如果看不懂一个服务代码的调用,可以一直溯源,溯源到请求方,看是如果发起的请求,或者通过断点法,看IDE的堆栈来向上溯源,这两种都是很好的方法。

服务端处理方法分析

在上面我们已经分析,不过什么Grpc的调用,都会经过grpcCommonRequestAcceptor.request(request, responseObserver)grpcBiStreamRequestAcceptor.requestBiStream(responseObserver)的处理。而接下来重点讲解下com.alibaba.nacos.core.remote.grpc.GrpcRequestAcceptor#request的处理。

这个方法中,前面都是校验和转化,这里就不细看了,直接抓重点

Request request = (Request) parseObj;
try {
    // 获取连接
    Connection connection = connectionManager.getConnection(CONTEXT_KEY_CONN_ID.get());
    // 创建并设置请求的节本信息
    RequestMeta requestMeta = new RequestMeta();
    requestMeta.setClientIp(connection.getMetaInfo().getClientIp());
    requestMeta.setConnectionId(CONTEXT_KEY_CONN_ID.get());
    requestMeta.setClientVersion(connection.getMetaInfo().getVersion());
    requestMeta.setLabels(connection.getMetaInfo().getLabels());
    // 刷新活跃时间,根据这个时间看是否超时的
    connectionManager.refreshActiveTime(requestMeta.getConnectionId());
    // 重点!!!处理请求
    Response response = requestHandler.handleRequest(request, requestMeta);
    // 拿到请求解析,完成请求
    Payload payloadResponse = GrpcUtils.convert(response);
    traceIfNecessary(payloadResponse, false);
    responseObserver.onNext(payloadResponse);
    responseObserver.onCompleted();
} catch (Throwable e) {
    Loggers.REMOTE_DIGEST
        .error("[{}] Fail to handle request from connection [{}] ,error message :{}", "grpc", connectionId,
               e);
    Payload payloadResponse = GrpcUtils.convert(ErrorResponse.build(e));
    traceIfNecessary(payloadResponse, false);
    responseObserver.onNext(payloadResponse);
    responseObserver.onCompleted();
}

重点在网络处理requestHandle.handleRequest(request, requestMeta)的处理中。

 public Response handleRequest(T request, RequestMeta meta) throws NacosException {
     for (AbstractRequestFilter filter : requestFilters.filters) {
         try {
             Response filterResult = filter.filter(request, meta, this.getClass());
             if (filterResult != null && !filterResult.isSuccess()) {
                 return filterResult;
             }
         } catch (Throwable throwable) {
             Loggers.REMOTE.error("filter error", throwable);
         }
     }
     // 调用处理方法
     return handle(request, meta);
 }

// 抽象方法,由子类处理
public abstract S handle(T request, RequestMeta meta) throws NacosException;

这里采用了模板方法的设计模式。模板方法的设计模式是定义顶层的处理逻辑,而具体步骤的实现由子类去实现。

当我们想看下这个handle是怎么处理的时候,发现其子类非常多。

2、Nacos 服务注册服务端源码分析(一)_第3张图片

注册的方式的处理类是哪个呢?

看到这个类有个泛型T extends Request,肯定根Request有关,我们回去找下我们的Request具体是哪个子类。而Request是由客户端发起的,那就需要回到上一篇看下,具体的Request是怎么来的。大家可以自己找下。我相信聪明的各位肯定最后会找到com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy#doRegisterService方法。

public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException {
    // 构建Request对象
    InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName,
                                                  NamingRemoteConstants.REGISTER_INSTANCE, instance);
    requestToServer(request, Response.class);
    redoService.instanceRegistered(serviceName, groupName);
}

通过这个Request对象,我们就可以很快找到具体的处理类了。

2、Nacos 服务注册服务端源码分析(一)_第4张图片

没错,就是这个com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler。在这个类中,就有着com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler#handle方法。

@Override
@Secured(action = ActionTypes.WRITE)
public InstanceResponse handle(InstanceRequest request, RequestMeta meta) throws NacosException {
    Service service = Service
        .newService(request.getNamespace(), request.getGroupName(), request.getServiceName(), true);
    switch (request.getType()) {
        case NamingRemoteConstants.REGISTER_INSTANCE:
            // 处理注册
            return registerInstance(service, request, meta);
        case NamingRemoteConstants.DE_REGISTER_INSTANCE:
            // 处理下线
            return deregisterInstance(service, request, meta);
        default:
            throw new NacosException(NacosException.INVALID_PARAM,
                                     String.format("Unsupported request type %s", request.getType()));
    }
}

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 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));
}

我们看到注册逻辑好像并没有处理什么内容,实际都是在发布事件后,接受事件的处理逻辑进行处理的。而具体是怎么处理的呢?因为NotifyCenter是个很重要的类。分析起来又要花很多时间。所以,本篇就先讲解到这里,后续更新再进行讲解。

总结

本篇花了很大的篇幅讲解了Grpc服务端,包括启动的两个Server,GrpcClusterServerGrpcSdkServer。他们是如何启动的和他们各自的作用。后面还介绍了我们如何寻找处理的Handler类。如果有过源码阅读经验的小伙伴看到Handler这个词就会产生反射,这一定是一个网络处理类。因为从Netty(当然可能别的框架,或者更早的框架)都是用Handler来对网络请求进行处理的。最后留了一点悬念,就是其重点的发布订阅模式。敬请期待下次的更新。

你可能感兴趣的:(Nacos,源码分析,java,中间件,分布式)