nacos通讯方式学习《gRPC》

准备

源码链接:gitee.com/mirrors/Nac...

使用分支:v2.x-develop

核心类

RpcClient: gRpc的客户端

BaseGrpcServer:gRpc的服务端 BaseRpcServer:抽象的rpc服务端

源码解读

  • 客户端与服务端创建链接源码
  1. client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java
java 复制代码
@BeforeEach
void before() throws NoSuchFieldException, NacosException, IllegalAccessException {
    Properties prop = new Properties();
    prop.setProperty("serverAddr", "localhost");
    prop.put(PropertyKeyConst.NAMESPACE, "test");
    client = new NacosNamingService(prop);//※创建命名服务
    injectMocks(client);
}
  1. client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java
java 复制代码
public NacosNamingService(Properties properties) throws NacosException {
    init(properties);//※开始初始化
}

private void init(Properties properties) throws NacosException {
    PreInitUtils.asyncPreLoadCostComponent();
    final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties);
    NAMING_LOGGER.info(ParamUtil.getInputParameters(nacosClientProperties.asProperties()));
    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);
    this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties,
            changeNotifier);//※客户端代理初始化
}
  1. client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java
java 复制代码
public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder,
        NacosClientProperties properties, InstancesChangeNotifier changeNotifier) throws NacosException {
    this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this,
            changeNotifier);
    this.serverListManager = new NamingServerListManager(properties, namespace);
    this.serverListManager.start();
    this.serviceInfoHolder = serviceInfoHolder;
    this.securityProxy = new SecurityProxy(this.serverListManager,
            NamingHttpClientManager.getInstance().getNacosRestTemplate());
    initSecurityProxy(properties);
    this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties);
    this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties,
            serviceInfoHolder);//※创建客户端的代理(Grpc)
}
  1. client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java
java 复制代码
public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory,
        NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder) throws NacosException {
    super(securityProxy);
    this.namespaceId = namespaceId;
    this.uuid = UUID.randomUUID().toString();
    this.requestTimeout = Long.parseLong(properties.getProperty(CommonParams.NAMING_REQUEST_TIMEOUT, "-1"));
    Map<String, String> labels = new HashMap<>();
    labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK);
    labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_NAMING);
    labels.put(Constants.APPNAME, AppNameUtils.getAppName());
    GrpcClientConfig grpcClientConfig = RpcClientConfigFactory.getInstance()
            .createGrpcClientConfig(properties.asProperties(), labels);
    this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, grpcClientConfig);
    this.redoService = new NamingGrpcRedoService(this, properties);
    this.enableClientMetrics = Boolean.parseBoolean(
            properties.getProperty(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true"));
    NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid);
    start(serverListFactory, serviceInfoHolder);//※grpc客户端的启动入口
}

private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder) throws NacosException {
    rpcClient.serverListFactory(serverListFactory);
    rpcClient.registerConnectionListener(redoService);
    rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder));
    rpcClient.start();//※grpc客户端启动
    NotifyCenter.registerSubscriber(this);
}
  1. common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java
java 复制代码
static {
    PayloadRegistry.init();
}


/**
     * Start this client.
     */
    public final void start() throws NacosException {

        boolean success = rpcClientStatus.compareAndSet(RpcClientStatus.INITIALIZED, RpcClientStatus.STARTING);
        if (!success) {
            return;
        }
        //-------------------忽略-----------------------

        // connect to server, try to connect to server sync retryTimes times, async starting if failed.
        Connection connectToServer = null;
        rpcClientStatus.set(RpcClientStatus.STARTING);

        int startUpRetryTimes = rpcClientConfig.retryTimes();
        while (startUpRetryTimes >= 0 && connectToServer == null) {
            try {
                startUpRetryTimes--;
                ServerInfo serverInfo = nextRpcServer();

                LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to connect to server on start up, server: {}",
                        rpcClientConfig.name(), serverInfo);

                connectToServer = connectToServer(serverInfo);//※链接服务端
            } catch (Throwable e) {
                LoggerUtils.printIfWarnEnabled(LOGGER,
                        "[{}] Fail to connect to server on start up, error message = {}, start up retry times left: {}",
                        rpcClientConfig.name(), e.getMessage(), startUpRetryTimes, e);
            }

        }
        if (connectToServer != null) {
            LoggerUtils
                    .printIfInfoEnabled(LOGGER, "[{}] Success to connect to server [{}] on start up, connectionId = {}",
                            rpcClientConfig.name(), connectToServer.serverInfo.getAddress(),
                            connectToServer.getConnectionId());
            this.currentConnection = connectToServer;
            rpcClientStatus.set(RpcClientStatus.RUNNING);
            eventLinkedBlockingQueue.offer(new ConnectionEvent(ConnectionEvent.CONNECTED, currentConnection));
        } else {
            switchServerAsync();
        }
        //-------------------忽略--------------------------

    }
  1. common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClient.java
java 复制代码
@Override
public Connection connectToServer(ServerInfo serverInfo) {
    // the newest connection id
    String connectionId = "";
    try {
        if (grpcExecutor == null) {
            this.grpcExecutor = createGrpcExecutor(serverInfo.getServerIp());
        }
        int port = serverInfo.getServerPort() + rpcPortOffset();
        ManagedChannel managedChannel = createNewManagedChannel(serverInfo.getServerIp(), port);
        RequestGrpc.RequestFutureStub newChannelStubTemp = createNewChannelStub(managedChannel);
        
        Response response = serverCheck(serverInfo.getServerIp(), port, newChannelStubTemp);
        if (!(response instanceof ServerCheckResponse)) {
            shuntDownChannel(managedChannel);
            return null;
        }
        // submit ability table as soon as possible
        // ability table will be null if server doesn't support ability table
        ServerCheckResponse serverCheckResponse = (ServerCheckResponse) response;
        connectionId = serverCheckResponse.getConnectionId();
        
        BiRequestStreamGrpc.BiRequestStreamStub biRequestStreamStub = BiRequestStreamGrpc.newStub(
                newChannelStubTemp.getChannel());
        GrpcConnection grpcConn = new GrpcConnection(serverInfo, grpcExecutor);
        grpcConn.setConnectionId(connectionId);
        // if not supported, it will be false
        if (serverCheckResponse.isSupportAbilityNegotiation()) {
            // mark
            this.recAbilityContext.reset(grpcConn);
            // promise null if no abilities receive
            grpcConn.setAbilityTable(null);
        }
        
        //create stream request and bind connection event to this connection.
        StreamObserver<Payload> payloadStreamObserver = bindRequestStream(biRequestStreamStub, grpcConn);
        
        // stream observer to send response to server
        grpcConn.setPayloadStreamObserver(payloadStreamObserver);
        grpcConn.setGrpcFutureServiceStub(newChannelStubTemp);
        grpcConn.setChannel(managedChannel);
        //send a  setup request.
        ConnectionSetupRequest conSetupRequest = new ConnectionSetupRequest();
        conSetupRequest.setClientVersion(getClientVersion());
        conSetupRequest.setLabels(super.getLabels());
        // set ability table
        conSetupRequest.setAbilityTable(
                NacosAbilityManagerHolder.getInstance().getCurrentNodeAbilities(abilityMode()));
        conSetupRequest.setTenant(super.getTenant());
        grpcConn.sendRequest(conSetupRequest);
        // wait for response
        if (recAbilityContext.isNeedToSync()) {
            // try to wait for notify response
            recAbilityContext.await(this.clientConfig.capabilityNegotiationTimeout(), TimeUnit.MILLISECONDS);
            // if no server abilities receiving, then reconnect
            if (!recAbilityContext.check(grpcConn)) {
                return null;
            }
        } else {
            // leave for adapting old version server
            // registration is considered successful by default after 100ms
            // wait to register connection setup
            Thread.sleep(100L);
        }
        return grpcConn;
    } catch (Exception e) {
        LOGGER.error("[{}]Fail to connect to server!,error={}", GrpcClient.this.getName(), e);
        // remove and notify
        recAbilityContext.release(null);
    }
    return null;
}
  • 向服务端发送请求
  1. common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java
java 复制代码
/**
 * send request.
 *
 * @param request request.
 * @return response from server.
 */
public Response request(Request request) throws NacosException {
    return request(request, rpcClientConfig.timeOutMills());
}

/**
 * send request.
 *
 * @param request request.
 * @return response from server.
 */
public Response request(Request request, long timeoutMills) throws NacosException {
    int retryTimes = 0;
    Response response;
    Throwable exceptionThrow = null;
    long start = System.currentTimeMillis();
    while (retryTimes <= rpcClientConfig.retryTimes() && (timeoutMills <= 0
            || System.currentTimeMillis() < timeoutMills + start)) {
        boolean waitReconnect = false;
        try {
            if (this.currentConnection == null || !isRunning()) {
                waitReconnect = true;
                throw new NacosException(NacosException.CLIENT_DISCONNECT,
                        "Client not connected, current status:" + rpcClientStatus.get());
            }
            response = this.currentConnection.request(request, timeoutMills);//※发送请求
            if (response == null) {
                throw new NacosException(SERVER_ERROR, "Unknown Exception.");
            }
            if (response instanceof ErrorResponse) {
                if (response.getErrorCode() == NacosException.UN_REGISTER) {
                    synchronized (this) {
                        waitReconnect = true;
                        if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) {
                            LoggerUtils.printIfErrorEnabled(LOGGER,
                                    "Connection is unregistered, switch server, connectionId = {}, request = {}",
                                    currentConnection.getConnectionId(), request.getClass().getSimpleName());
                            switchServerAsync();
                        }
                    }

                }
                throw new NacosException(response.getErrorCode(), response.getMessage());
            }
            // return response.
            lastActiveTimeStamp = System.currentTimeMillis();
            return response;

        } catch (Throwable e) {
            if (waitReconnect) {
                try {
                    // wait client to reconnect.
                    Thread.sleep(Math.min(100, timeoutMills / 3));
                } catch (Exception exception) {
                    // Do nothing.
                }
            }

            LoggerUtils.printIfErrorEnabled(LOGGER,
                    "Send request fail, request = {}, retryTimes = {}, errorMessage = {}", request, retryTimes,
                    e.getMessage());

            exceptionThrow = e;

        }
        retryTimes++;

    }

}
  1. common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConnection.java
java 复制代码
@Override
public Response request(Request request, long timeouts) throws NacosException {
    Payload grpcRequest = GrpcUtils.convert(request);
    ListenableFuture<Payload> requestFuture = grpcFutureServiceStub.request(grpcRequest);//※ 发送请求
    Payload grpcResponse;
    try {
        if (timeouts <= 0) {
            grpcResponse = requestFuture.get();
        } else {
            grpcResponse = requestFuture.get(timeouts, TimeUnit.MILLISECONDS);
        }
    } catch (Exception e) {
        throw new NacosException(NacosException.SERVER_ERROR, e);
    }
    
    return (Response) GrpcUtils.parse(grpcResponse);
}
  1. api/src/main/java/com/alibaba/nacos/api/grpc/auto/RequestGrpc.java
java 复制代码
/**
 * <pre>
 * Sends a commonRequest
 * </pre>
 */
public com.google.common.util.concurrent.ListenableFuture<com.alibaba.nacos.api.grpc.auto.Payload> request(
    com.alibaba.nacos.api.grpc.auto.Payload request) {
  return futureUnaryCall(
      getChannel().newCall(getRequestMethod(), getCallOptions()), request);
}
  • 服务端源码
  1. core/src/main/java/com/alibaba/nacos/core/remote/BaseRpcServer.java
java 复制代码
static {
    PayloadRegistry.init();//初始化扫描注册请求与响应
}
/**
 * 开启服务
 */
@PostConstruct
public void start() throws Exception {
    String serverName = getClass().getSimpleName();
    Loggers.REMOTE.info("Nacos {} Rpc server starting at port {}", serverName, getServicePort());

    startServer();//※开启服务的具体实现接口

    if (RpcServerSslContextRefresherHolder.getSdkInstance() != null) {
        RpcServerSslContextRefresherHolder.getSdkInstance().refresh(this);
    }

    if (RpcServerSslContextRefresherHolder.getClusterInstance() != null) {
        RpcServerSslContextRefresherHolder.getClusterInstance().refresh(this);
    }

    Loggers.REMOTE.info("Nacos {} Rpc server started at port {}", serverName, getServicePort());
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        Loggers.REMOTE.info("Nacos {} Rpc server stopping", serverName);
        try {
            //服务停止时先关闭rpc
            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);
        }
    }));

}
  1. core/src/main/java/com/alibaba/nacos/core/remote/grpc/BaseGrpcServer.java
java 复制代码
 @Override
    public void startServer() throws Exception {
        //gRPC 的服务方法注册表,用于存储所有可调用的服务方法
        final MutableHandlerRegistry handlerRegistry = new MutableHandlerRegistry();
        //将业务实现类注册到注册表
        addServices(handlerRegistry, getSeverInterceptors().toArray(new ServerInterceptor[0]));
        //netty的服务构建
        NettyServerBuilder builder = NettyServerBuilder.forPort(getServicePort()).executor(getRpcExecutor());
        
        Optional<InternalProtocolNegotiator.ProtocolNegotiator> negotiator = newProtocolNegotiator();
        if (negotiator.isPresent()) {
            InternalProtocolNegotiator.ProtocolNegotiator actual = negotiator.get();
            Loggers.REMOTE.info("Add protocol negotiator {}", actual.getClass().getCanonicalName());
            builder.protocolNegotiator(actual);
        }
        
        for (ServerTransportFilter each : getServerTransportFilters()) {
            builder.addTransportFilter(each);
        }
        server = builder.maxInboundMessageSize(getMaxInboundMessageSize()).fallbackHandlerRegistry(handlerRegistry)
                .compressorRegistry(CompressorRegistry.getDefaultInstance())
                .decompressorRegistry(DecompressorRegistry.getDefaultInstance())
                .keepAliveTime(getKeepAliveTime(), TimeUnit.MILLISECONDS)
                .keepAliveTimeout(getKeepAliveTimeout(), TimeUnit.MILLISECONDS)
                .permitKeepAliveTime(getPermitKeepAliveTime(), TimeUnit.MILLISECONDS).build();
        
        server.start();
    }
    
    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(GrpcServerConstants.REQUEST_SERVICE_NAME,
                                GrpcServerConstants.REQUEST_METHOD_NAME))
                .setRequestMarshaller(ProtoUtils.marshaller(Payload.getDefaultInstance()))
                .setResponseMarshaller(ProtoUtils.marshaller(Payload.getDefaultInstance())).build();
        
        final ServerCallHandler<Payload, Payload> payloadHandler = ServerCalls.asyncUnaryCall(
                (request, responseObserver) -> {
                    handleCommonRequest(request, responseObserver);
                });//※处理客户端的请求并响应
        
        final ServerServiceDefinition serviceDefOfUnaryPayload = ServerServiceDefinition.builder(
                GrpcServerConstants.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(GrpcServerConstants.REQUEST_BI_STREAM_SERVICE_NAME,
                                GrpcServerConstants.REQUEST_BI_STREAM_METHOD_NAME))
                .setRequestMarshaller(ProtoUtils.marshaller(Payload.newBuilder().build()))
                .setResponseMarshaller(ProtoUtils.marshaller(Payload.getDefaultInstance())).build();
        
        final ServerServiceDefinition serviceDefOfBiStream = ServerServiceDefinition.builder(
                GrpcServerConstants.REQUEST_BI_STREAM_SERVICE_NAME).addMethod(biStreamMethod, biStreamHandler).build();
        handlerRegistry.addService(ServerInterceptors.intercept(serviceDefOfBiStream, serverInterceptor));
        
    }

一句话概括

没啥好说的, 服务端先启动,客户端启动时向服务端请求创建链接,然后就发消息了。

ps:说着简单,但是内容很多。本来想按官网说的模块学习,但是不了解下2. 版本后不先了解grpc。看着太费劲了。

相关推荐
Larcher30 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐43 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭1 小时前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程