您好,登錄后才能下訂單哦!
大道至簡, 返璞歸真.
在發(fā)表這篇博文的前夕, 還有一些小伙伴在提問一些以下相關(guān)的問題:
性能怎么樣?
是否容易上手?
開發(fā)目標(biāo)在哪?
如何反饋問題?
等等, 以上問題會在本文中一一介紹.
首先來聊聊情懷這個東西! 相信每一個行業(yè)內(nèi)的從業(yè)者都或多或少有過一個夢, 這個夢叫做: "我到時候要開發(fā)一個XXX"!其實(shí)作者當(dāng)初也是一樣.
每當(dāng)半夜(凌晨)在加班、看文檔、調(diào)試的時候, 總會搜索到一些幾年前或十幾年前的框架或入門demo。例如: tinyhttp, 鏈接的源碼是一些同學(xué)fork的鏡像站。
每次看到這些內(nèi)容或多或少都會激起心中那一絲絲快熄滅的熱情, 也許這就是最后對技術(shù)的渴望?
就是在動手創(chuàng)建項目之前還反復(fù)問過自己是否要做? 能堅持下去么?也許被噴都是一種奢望?
在心里一一回答了這些問題后, 在2018年末創(chuàng)建了本項目.
說句實(shí)話! 一個網(wǎng)絡(luò)開發(fā)框架最難的不是實(shí)現(xiàn)某個功能, 而是從零開始一步一步添磚加瓦的造輪子!
作為一個網(wǎng)絡(luò)開發(fā)框架, 最重要的兩個功能肯定是需要的! 定時器庫、事件驅(qū)動庫. 如何抉擇?選項有2個: libev / libuv .
libev 成熟穩(wěn)定、輕量級、unix like支持、容易嵌入;
libuv 比libev更加優(yōu)秀,增加了許多功能(線程池、信號、同步、鎖等等),封裝更加完善, 并且增加了windows支持;
從cf框架開發(fā)之初選型來看, libuv絕對是目前最優(yōu)解. 但是作者偏偏選擇了libev. 也從此開始, 艱辛的底層開發(fā)之路就此展開.
首先, 作者不讓使用者C/C++進(jìn)行實(shí)際業(yè)務(wù)開發(fā)! 這樣做會讓使用者有較高的開發(fā)成本與學(xué)習(xí)成本, 而選擇一門較好的腳本語言就顯得尤為重要.
作者對Lua還算是稍微熟悉一點(diǎn), 所以就選了Lua作為業(yè)務(wù)腳本語言。至于Lua語言的優(yōu)勢這里就不說了, 網(wǎng)上大把文章夸它的.
現(xiàn)在既然腳本語言已經(jīng)選定, 那么就開始寫代碼吧!Let's Lua.
首先, 我們來看一段C封裝給Lua調(diào)用的API代碼:
LUAMOD_API int
luaopen_tcp(lua_State *L){
luaL_checkversion(L);
/* 添加SSL支持 */
SSL_library_init();
SSL_load_error_strings();
// CRYPTO_set_mem_functions(xmalloc, xrealloc, xfree);
// OpenSSL_add_ssl_algorithms();
/* 添加SSL支持 */
luaL_newmetatable(L, "__TCP__");
lua_pushstring (L, "__index");
lua_pushvalue(L, -2);
lua_rawset(L, -3);
lua_pushliteral(L, "__mode");
lua_pushliteral(L, "kv");
lua_rawset(L, -3);
luaL_Reg tcp_libs[] = {
{"read", tcp_read},
{"write", tcp_write},
{"ssl_read", tcp_sslread},
{"ssl_write", tcp_sslwrite},
{"stop", tcp_stop},
{"start", tcp_start},
{"close", tcp_close},
{"listen", tcp_listen},
{"connect", tcp_connect},
{"ssl_connect", tcp_sslconnect},
{"new", tcp_new},
{"new_ssl", ssl_new},
{"free_ssl", ssl_free},
{"new_server_fd", new_server_fd},
{"new_client_fd", new_client_fd},
{NULL, NULL}
};
luaL_setfuncs(L, tcp_libs, 0);
luaL_newlib(L, tcp_libs);
return 1;
}
以上是TCP實(shí)現(xiàn)的C代碼的片段, 有興趣閱讀源碼的小伙伴請點(diǎn)擊這里;
眾所周知Lua沒有原生的Socket. 那么就需要框架編寫者自己抽象底層邏輯重新實(shí)現(xiàn)一套API.
簡單的封裝Lua C庫誰都會, 而且也算不上是什么難事. 但是我們的目的是將底層同步阻塞Socket hook為非阻塞, 這時候難點(diǎn)就來了!
大家都知道libev是基于react模型的事件驅(qū)動網(wǎng)絡(luò)庫, 所有注冊事件后的業(yè)務(wù)邏輯都是以回調(diào)的形式觸發(fā). 那不就變成node-lua代碼了嗎?(笑)
這時候, 作者想了個點(diǎn)子來解決這個問題! 執(zhí)行流程如下:
每次需要做一些同步操作的時候, 就調(diào)用C API注冊回調(diào)事件.
為當(dāng)前注冊的所有事件創(chuàng)建一個Lua協(xié)程保存上下文并讓出當(dāng)前協(xié)程執(zhí)行權(quán).
- 等到注冊事件被觸發(fā)后, 調(diào)用C API恢復(fù)協(xié)程繼續(xù)執(zhí)行.
簡單來說就是將C層次的異步回調(diào)邏輯封裝為Lua層的同步非阻塞, 保證不因?yàn)镮O問題阻塞線程.
下面提供一段socket同步非阻塞的偽代碼, 經(jīng)供參考:
function TCP:recv(bytes)
local current_co = co_self()
self.read_co = read_ev(function()
-- do action
-- stop timer_ev
-- wakeup(current_co) 恢復(fù)執(zhí)行權(quán)
end)
self.timer_co = self.timer_ev(function()
-- do action
-- stop read_ev
-- wakeup(current_co) 恢復(fù)執(zhí)行權(quán)
end)
tcp_start(io, EV_READ, self.read_co)
timer_start(timer, 3秒超時, self.timer_co)
return co_yield() -- 讓出執(zhí)行權(quán)
end
一個Lua版的Socket EV_READ偽代碼大致的處理流程如上, 想看實(shí)際處理邏輯請看這里。
同理, Socket write/connect/listen等等API直接照抄就行(UDP也大同小異). (其實(shí)這里有個小插曲就是SSL SOCKET的坑, 但是由于篇幅問題就不說了.)
細(xì)心的小伙伴可能發(fā)現(xiàn)代碼同時注冊了Socket與Timer事件, Socket非阻塞操作不能解決read與connect超時的問題. 所以cf框架干脆就封裝徹底一點(diǎn).
至此, Socket算是已經(jīng)算是基本hook與封裝完成了. 接下來就可以開始寫應(yīng)用層協(xié)議了.
現(xiàn)在Socket終于能正常使用了, 那么面臨的新問題就又來了。
libev沒有自帶異步dns
dns都還需要使用者自己封裝, 這個坑真是填的無比難受! 好在網(wǎng)絡(luò)上有前輩實(shí)現(xiàn)了Lua版的異步dns, 作者稍微看明白之后就借用了過來封裝內(nèi)部使用.
這樣cf也算是有了深度定制的異步dns庫了吧!(雖然并不完善, 但是足夠使用)
一個網(wǎng)絡(luò)庫是否流行, 基本上就得看生態(tài). 那么協(xié)議層的輪子又得造起來:
其中一些協(xié)議為各位前輩那邊借過來適配后定制的, 簡單的協(xié)議則是直接花1-2小時直接手寫出來的。
為了不讓API那么封閉與提升cf的可用性, 作者決定將mysql與redis進(jìn)行初步封裝.
封裝包括大家常用的功能, 連接池、面向?qū)ο蟛僮?、無需手動管理session生命周期等等. 簡化編程思想包袱來提升開發(fā)效率.
至于內(nèi)部Socket更是讓框架來解決釋放問題確保文件描述數(shù)量限制的情況下也是可以正常使用. (其實(shí)是不喜歡依賴gc被動close fd與free內(nèi)存)
如果你耐心看完了第一部分介紹, 那么你就應(yīng)該對cf有了一個大概的了解.
cf全稱為: CoreFramework, 是一個基于libev的Lua網(wǎng)絡(luò)開發(fā)框架. 在其內(nèi)部實(shí)現(xiàn)了多種網(wǎng)絡(luò)協(xié)議與第三方庫用來幫助使用者進(jìn)行項目原型的快速開發(fā).
cf 在httpd使用上尊崇前、后端分離的解決方案, 僅實(shí)現(xiàn)了基本的view路由并且不支持rest風(fēng)格的API路由. 雖然這樣可能會引來宇多人的詬病.
cf 的httpd內(nèi)嵌websocket支持, 方便使用者在復(fù)用端口的同時也可以享受長連接編寫的樂趣.
更多的介紹, 請大家項目地址的Wiki
基于容器技術(shù)的微服務(wù)場景(swarm/kubernetes); —— 推薦
游戲服務(wù)器的前端代理層; —— 推薦
內(nèi)存/CPU資源較為緊缺的云服務(wù)器; —— 推薦
對性能要求較高的無狀態(tài)集群; —— 推薦
傳輸層: TCP/UDP
會話層: SSL Client支持
協(xié)議層: dns/webocket/http/mqtt/redis/mysql/smtp
工具庫: Timer/TASK
第三方庫: Libev、openssl/libressl、lua-5.3、jemalloc/tcmalloc(可選)
cf 目前支持絕大部分Unix like操作系統(tǒng), 作者是在Mac上進(jìn)行開發(fā), 所以Mac支持是必須的.
cf測試的Linux為Centos, 所以基本上基于Linux內(nèi)核的操作系統(tǒng)編譯后的運(yùn)行也沒什么問題(export 增加/usr/local/lib)
同時,作者還貼心的為大家做了一個簡單Dockerfile. 文件在項目根目錄下, 大家下載直接使用即可。
當(dāng)然, 如果你不想制作Dockerfile,也可以使用Docker命令直接拉去作者制作好放在docker hub的鏡像. candymi/cfweb
使用詳情與使用方法請參考Docker安裝和編譯安裝
bash#: ./cfadmin
bash#: ./cfadmin
killall cfadmin
ctrl + c
作者為大家貼心的寫了一篇詳細(xì)到不能再詳細(xì)的文檔, 以此來獲取大家的點(diǎn)贊與關(guān)注.
作者還為喜歡閱讀源碼的同學(xué)準(zhǔn)備了充足的中文注釋與英文注釋, 結(jié)合起來方便大家快速了解CF工作方式(中/英注釋結(jié)合易于理解一些專屬詞匯).
Q. 性能怎么樣?
A. 性能還不錯, 但是具體數(shù)值請自行測試.
Q. 是否容易上手?
A. 學(xué)習(xí)lua 一小時入門 -> cf 一小時入門
Q. 開這個項目的初衷是什么?
A. 其實(shí)在前面已經(jīng)回答過了.
Q. 開發(fā)目標(biāo)在哪?
A. Wiki 里有TODO項
Q. 如何反饋問題?
A. Wiki 里有Q & A項
Q. 對比行業(yè)內(nèi)的lua開源項目有何優(yōu)勢?
A. CF對比其它lua開發(fā)項目更深入改變用戶使用習(xí)慣! 簡化框架上手難度, 將框架都黑盒子透明化. 無需學(xué)習(xí)復(fù)雜的設(shè)計模式與理念.
Q. CF的開發(fā)理念是什么?
A. CF項目的目標(biāo)不是競爭, 而是明白明白簡單為美. 當(dāng)你習(xí)慣了它, 也許你就會上癮.
容器部署
Dockerfile快速構(gòu)建開發(fā)環(huán)境
cf web的初始化與使用
cf web Websocket應(yīng)用指南
也許你正在使用其它開發(fā)框架, 但是這不妨礙你對cf的督促.
也許你正在試用它, 這不妨礙你與作者溝通你的想法.
也許你正在吐槽它的缺點(diǎn),請來issue盡情吐槽.
項目文檔
項目地址
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。