為啥不能自定義client_id,個人感覺例如一些涉及會員的應(yīng)用的,如果自己定義client_id,會少很多麻煩
實際上GatewayWorker 2.1.4之前的版本是支持自定義client_id的,但是出現(xiàn)了很多問題,后來改成不可自定義,并且全局自增。
client_id 用來全局標(biāo)記一個socket連接,是一個全局自增的數(shù)值,由此可以精確控制向哪個socket連接發(fā)送數(shù)據(jù),由于client_id自增,即使向過期的client_id發(fā)送數(shù)據(jù)不會造成影響。
client_id交給開發(fā)者自定義,難免會導(dǎo)致client_id重復(fù),而client_id重復(fù),會導(dǎo)致部分socket失效或者斷開,也無法確定向哪個socket連接發(fā)送數(shù)據(jù),導(dǎo)致應(yīng)用異常,而這樣的異常很難排查。
一般開發(fā)者會想把client_id定義為uid,這樣會導(dǎo)致業(yè)務(wù)處理困難或者業(yè)務(wù)異常。舉幾個例子
1、聊天客戶端與wm(GatewayWorker)建立連接,這時客戶端突然斷網(wǎng)然后重新發(fā)起一個連接,而在wm這邊由于客戶端異常斷網(wǎng),沒立即收到連接斷開的fin包,wm這邊就有兩個uid的連接,當(dāng)wm終于檢測到第一個連接斷開時,會觸發(fā)onClose($uid),這時可能會認(rèn)為uid下線了,做一些下線處理操作,但是實際上這個uid還有一個連接,并沒有下線,導(dǎo)致業(yè)務(wù)異常。
2、如果把client_id定義為uid,假設(shè)用戶打開了兩個socket連接,那么會導(dǎo)致其中的一個連接失效,無法收到數(shù)據(jù),典型的例子是兩個聊天室的連接id都是uid,則只有一個聊天室能收到消息,另外一個收不到消息,或者可能在這個聊天室給uid發(fā)送消息,但是另外一個聊天室也收到了。同樣如果這個用戶退出了其中的一個聊天室,onClose($uid)只能知道這個uid退出了,但是不知道退出的是哪個聊天室,導(dǎo)致業(yè)務(wù)編程困難。
3、如果把client_id定義為uid,無法實現(xiàn)多客戶端通訊,像PC QQ 和手機QQ同時在線是需要兩條socket連接的,必須需要分開標(biāo)記的,如果都統(tǒng)一用uid,則無法針對某個客戶端推送消息,也無法方便的判斷消息是從哪個平臺的客戶端發(fā)來的。
即使不用uid標(biāo)記client_id
如果client_id自定義不當(dāng),會引發(fā)邏輯錯誤,比如用戶A上線后client_id為1,這時要向client_id為1的的連接發(fā)送數(shù)據(jù),恰巧A用戶還沒收到數(shù)據(jù)就下線了,B用戶上線,也被分配client_id=1,則會導(dǎo)致發(fā)給A用戶的數(shù)據(jù)卻發(fā)給了B,導(dǎo)致業(yè)務(wù)異常
結(jié)論
client_id為自增并且短時間內(nèi)不重復(fù)是非常必要的,而達到這點的client_id的值對于開發(fā)者來說也沒有什么意義,反而交給開發(fā)者定義會出現(xiàn)很多不可預(yù)知的問題,并且很難排查。
關(guān)于和現(xiàn)有賬號綁定問題,GatewayWorker后面會考慮增加一組接口,用來綁定uid與client_id的關(guān)系,在client_id下線時自動解綁,綁定關(guān)系存儲在Gateway的內(nèi)存中,不用讀存儲,效率很高。接口類似如下:
Gateway::bindUid($client_id, $uid); // 將$client_id綁定到uid下,uid與client_id是一對多關(guān)系
Gateway:;sendToUid($msg, $uid); // 給這個uid下的所有鏈接發(fā)送數(shù)據(jù)
GatewayWorker 增加了bindUid 和 sendToUid方法,可以直接使用了,參見手冊
http://workerman.net/gatewaydoc/gateway-worker-development/bind-uid.html
http://workerman.net/gatewaydoc/gateway-worker-development/send-to-uid.html