@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(VersionUtils.getFullClientVersion());
            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;
    }
@Test
    void testConnectToServerFailed() {
        assertNull(grpcClient.connectToServer(serverInfo));
    }