前言

本文素材來源朋友學習nacos2.1.1踩到的坑。直接上正菜

坑點一:出現端口被佔用

因為是學習使用,朋友就在物理機搭建了搭建了nacos偽集羣,即ip都一樣,端口分別為8848,8847,8849。然而啓動nacos服務器後,一台正常啓動,其他兩台都報了端口被佔用

出現這種情況的原因,官網有做了解釋

nacos報rpc端口衝突_微服務


通過官網我們可以很容易得知,這個端口被佔用主要是因為grpc引起的,因為他端口的生成方式,是由主端口+1000、主端口+1001生成。

解決方法

集羣的端口不要採用相鄰數字,步長儘量搞大點。比如設置為7777、8888、9999之類的

坑二:微服務項目啓動出現com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING異常

這個問題出現在朋友在項目中配置的nacos地址為nginx地址,配置示例如下

spring.cloud.nacos.discovery.server-addr=nginx ip

一開始朋友nginx的配置示例如下

upstream nacos-cluster { 
		server 127.0.0.1:7777;
		server 127.0.0.1:8888;
		server 127.0.0.1:9999;
   }
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass http://nacos-cluster;
        }
        }

瀏覽器通過nginx訪問沒問題,但是項目中把nacos服務地址配置為nginx ip就報了

com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING

這個異常信息,後面朋友查資料,官網上有寫

nacos報rpc端口衝突_微服務_02


於是他就將轉發方式改為TCP,他的nginx版本是1.9+以上版本,默認就支持TCP代理了,不用額外安裝stream模塊。nginx配置TCP的示例形如下

stream {
	upstream nacos-cluster-grpc{
	    # nacos2版本,grpc端口與要比主端口多1000,主端口為7777、8888、9999
	    server 127.0.0.1:8777;
		server 127.0.0.1:9888;
		server 127.0.0.1:10999; 
	}
	server{
	   listen 9848;
        proxy_pass nacos-cluster-grpc;
	}
}

當朋友配置好nginx tcp代理轉發後,通過telnet命令

telnet 127.0.0.1 9848

來看是否能正常轉發給nacos服務端,經過驗證,網絡可以連通。接着朋友在微服務項目的nacos配置填寫如下地址

spring.cloud.nacos.discovery.server-addr=127.0.0.1:9848 #nginx代理tcp的地址

本來以為萬事大吉,結果項目一啓動,仍然報

com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING

於是朋友懵了,啥情況?就來找我交流一下

nacos報rpc端口衝突_微服務_03


其實在nacos官網的FAQ就有提到相應的解題思路了

nacos報rpc端口衝突_官網_04


因為我們在nginx配置的代理tcp端口為9848,這個端口可以看成是grpc的端口,因為grpc的端口 = nacos主端口 + 1000,因此我們套這個公式就可以得出,nacos的主端口為 = 9848 - 1000 = 8848,而我們微服務項目配置nacos端口,其實配置是主端口,因此實際上我們配置要寫成

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 #nginx代理tcp端口 - 1000

配置這個後,果然成功註冊。這個思路是官網帶給我們的,但作為一個有點追求的程序員應該不會僅僅滿足與此,我們可以直接根據控制枱報出的異常來獲取答案

異常排查過程就省略了,直接貼出關鍵的解題代碼

1、首先解析主端口號的核心代碼

位置在

com.alibaba.nacos.common.remote.client.RpcClient#resolveServerInfo
ServerInfo private RpcClient.ServerInfo resolveServerInfo(String serverAddress) {
        Matcher matcher = EXCLUDE_PROTOCOL_PATTERN.matcher(serverAddress);
        if (matcher.find()) {
            serverAddress = matcher.group(1);
        }

        String[] ipPortTuple = serverAddress.split(":", 2);
        int defaultPort = Integer.parseInt(System.getProperty("nacos.server.port", "8848"));
        String serverPort = (String)CollectionUtils.getOrDefault(ipPortTuple, 1, Integer.toString(defaultPort));
        return new RpcClient.ServerInfo(ipPortTuple[0], NumberUtils.toInt(serverPort, defaultPort));
    }

2、其次設置grpc端口的核心代碼

位置在:

com.alibaba.nacos.common.remote.client.grpc.GrpcClient#connectToServer

nacos報rpc端口衝突_nacos報rpc端口衝突_05


端口設置就是在截圖圈紅部分,然後從this.rpcPortOffset()我們可以發現

public int rpcPortOffset() {
        return Integer.parseInt(System.getProperty("nacos.server.grpc.port.offset", String.valueOf(Constants.SDK_GRPC_PORT_DEFAULT_OFFSET)));
    }

這個偏移量是可以通過nacos.server.grpc.port.offset進行修改,不修改默認就是1000。因此跟蹤源碼,我們可以得出另外一種解法。在微服務的nacos配置仍然填代理的nginx 的tcp地址,示例

spring.cloud.nacos.discovery.server-addr=127.0.0.1:9848 #nginx代理tcp的地址

同時啓動的時候,加上

-Dnacos.server.grpc.port.offset=0

或者在主啓動類硬編碼也 可以

System.setProperty("nacos.server.grpc.port.offset","0");

注: 這邊很重要的細節點就是:GRPC port = 主端口 + grpc端口偏移量,這個計算出來的端口值要和nginx代理tcp 端口值相等。

總結

因為朋友用的是目前最新版的nacos2,所以有些問題搜索引擎不是那麼好找答案,因此遇到這種問題,最好的解題思路就是官網和相應的github,還有就是源碼跟蹤了。

後面那個
Client not connected, current status:STARTING,其實還有一種解法,就是把客户端版本調低到1.x版本,因為這個問題本質上是連接不上grpc問題,因此我們可以不用grpc,直接用http就好了,而2.x服務端版本是同時支持http和grpc,因此客户端版本調成1.x,他就是以http的方式和服務端進行交互。不過是不建議這麼做,因為你升級了2.x,有一方面就是為高性能,如果把版本降低,那還不如直接使用1.x就好了。

還有文中的那兩種解法,我個人是建議不要改偏移量,直接通過主端口 + 1000這種方式去算就好了