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。看着太费劲了。

相关推荐
雪兽软件1 小时前
构建强大的物联网架构所需了解的一切
物联网·架构
薛定谔的算法3 小时前
# 从0到1构建React项目:一个仓库展示应用的架构实践
前端·react.js
Tina学编程3 小时前
HTML基础P1 | HTML基本元素
服务器·前端·html
消失的旧时光-19434 小时前
Android模块化架构:基于依赖注入和服务定位器的解耦方案
android·java·架构·kotlin
一只小风华~5 小时前
Web前端:JavaScript和CSS实现的基础登录验证功能
前端
90后的晨仔5 小时前
Vue Router 入门指南:从零开始实现前端路由管理
前端·vue.js
LotteChar5 小时前
WebStorm vs VSCode:前端圈的「豆腐脑甜咸之争」
前端·vscode·webstorm
90后的晨仔5 小时前
零基础快速搭建 Vue 3 开发环境(附官方推荐方法)
前端·vue.js
洛_尘5 小时前
Java EE进阶2:前端 HTML+CSS+JavaScript
java·前端·java-ee
孤独的根号_5 小时前
Vite背后的技术原理🚀:为什么选择Vite作为你的前端构建工具💥
前端·vue.js·vite