這個版本基本上達到了我最早想要的效果: 簡潔, 直觀, 無鎖, 並行, 高效. 高效不一定是運行時的效率, 更多的是開發效率. 也就是最少的bug
產生可能性, 最快的代碼實現.
代碼實際上在2月份就基本完工了, 等到經歷了一個html5的遊戲後, 感覺應該差不多了. 不太可能再有大的改動. 另外, 也添加了一些功能.
這些功能也導致我重新修改了通信協議. 一起彙總如下:
1. Actor的調度/通信的基本原理未變, 依然是基於epoll, pipe. 只是管道上的通信僅僅作爲喚醒目標工作線程的目的, 不承載業務屬性. 相信要
高效得多. 還是不支持windows平臺.
2. 應用層的協議使用了此博客中提到的stmp協議. 主要是考慮到編解碼的自動化和可擴展性. 業務消息依然使用protobuf(2.6.1).
3. 提供了websocket的實現, 且支持在同一端口上同時使用stmp和websocket作爲承載, 但websocket的上層還是必需使用stmp協議.
4. 提供了tcp短連接支持.
5. 網絡消息依然採用註冊加回調的形式, libgsc提供了幾個宏, 用於支持不同的業務消息註冊. 如:
REG_N2H_NOGUSR(GAS_REQ_GC_AUTH, GcAuthWithGasReq, GcAuthWithGasRsp, GcMsgAuth::gas_req_gc_auth)
表示:
a). 註冊一個GAS_REG_GC_AUTH的命令字(short),
b). 請求消息是GcAuthWithGasReq(pb生成的c++類)
c). 響應消息是GcAuthWithGasRsp.
d). 消息到來時的處理函數是: GcMsgAuth::gas_req_gc_auth
6. 網絡事務處理透明化, 假想一個玩家(Game Client)登錄的請求處理如下:
void GcMsgAuth::gas_req_gc_auth(N2H* n2h, Gn2htrans* gt, GcAuthWithGasReq* req)
{
GcAuthWithGasRsp* rsp = new GcAuthWithGasRsp();
if(req->usr() == "usr" && req->pwd() == "pwd")
rsp->set_rsp("login ok");
else
rsp->set_rsp("login fail");
gt->end(rsp);
}
Gn2htrans表示了一個由TCP客戶端發起的消息事務. 在填充好rsp後, 通過調用gt->end(rsp); 響應結果就會被髮送到客戶端. 有點類似
在java-sevlet中加入了一個事務的概念. 區別在於, 這個事務不一定要立即結束. 下面第8點詳述.
7. Actor的所有通信都基於lambda, 面向future(調用Actor的future函數)的編程模式. 例如:
Db* db = this->getDb();
db->future([db]
{
db.load();
});
8. 提供了一個非常有用的可阻塞Actor(ActorBlocking). 用於IO阻塞操作. 如上面的代碼Db* db, 就是繼承自ActorBlocking, 當db->load()執行時,
調用db->future的線程可能早就返回了. 這在有網絡請求需要訪問數據庫後才返回的場景下會使代碼量變得非常小, 且十分直觀. 貌似libcaf和akka
都沒有提供類似的功能. 如下面的代碼:
void GcMsgAuth::gas_req_gc_auth(N2H* n2h, Gn2htrans* gt, GcAuthWithGasReq* req)
{
Db* db = this->getDb(); //thread-0
db->future([db, gt, req]
{
GcAuthWithGasRsp* rsp = new GcAuthWithGasRsp(); //thread-1
if(db.auth(req->usr(), req->pwd()))
rsp->set_rsp("login ok");
else
rsp->set_rsp("login fail");
gt->end(rsp); //事務結束.
});
}
在收到客戶端的登錄請求後, 取出一個db-actor, 並通過同步調用db.auth前往數據庫驗證用戶名/密碼的正確性後, 再將事務響應.
業務消息的開發人員不需要關心請求/響應與事務的內存釋放. 減少bug的發生機率.另外, 事務在結束時, 可以將req和rsp吐出到日誌輸出功能, 這在調試
和運營日誌輸出時非常有用.
GsAuthWithGasReq* req = new GsAuthWithGasReq();
h2nActor->future(0x0001, req, [this](ushort ret, Message* r)
{
if(ret != RET_SUCCESS) /** 失敗. */
return;
GsAuthWithGasRsp* rsp = (GsAuthWithGasRsp*)r;
LOG_INFO("auth with GAS successfully: %s\n", rsp->ShortDebugString().c_str())
},
/** timeout 回調. */
[this, req]
{
LOG_WARN("timeout, req: %s\n", req->ShortDebugString().c_str())
this->close();
});
h2nActor應該繼承自H2N基類. h2n的future函數提供了兩個lambda表達式, 一個是響應到來時回調, 另一個是超時到來時回調.
這兩個lambda都在h2nActor所處的線程中被調用. 非常安全.
10. 提供了消息加/解密接口.
11. 零配置或零配置文件.
12. 代碼已上傳至github, 一併提供了java版本的實現. 由於libgsc是使用的c++驗證想法, 且經歷了一個實際的項目, 因此暫時以c++的版本爲主, java版暫
作爲功能移植版本, 但基本上保持一致. 讓時間來完善它們: https://github.com/xzwdev/libgsc