core_framework —— 基於libev的輕量級lua網絡開發框架

大道至簡, 返璞歸真.前端

前言

在發表這篇博文的前夕, 還有一些小夥伴在提問一些如下相關的問題:node

  1. 性能怎麼樣?mysql

  2. 是否容易上手?react

  3. 開發目標在哪?git

  4. 如何反饋問題?github

  5. 對比行業內的lua開源項目有何優點?web

等等, 以上問題會在本文中一一介紹.redis


CF的原由

首先來聊聊情懷這個東西! 相信每個行業內的從業者都或多或少有過一個夢, 這個夢叫作: "我到時候要開發一個XXX"!其實做者當初也是同樣.sql

每當半夜(凌晨)在加班、看文檔、調試的時候, 總會搜索到一些幾年前或十幾年前的框架或入門demo。例如: tinyhttp, 連接的源碼是一些同窗fork的鏡像站。docker

每次看到這些內容或多或少都會激起心中那一絲絲快熄滅的熱情, 也許這就是最後對技術的渴望?

就是在動手建立項目以前還反覆問過本身是否要作? 能堅持下去麼?也許被噴都是一種奢望?

在內心一一回答了這些問題後, 在2018年底建立了本項目.

說句實話! 一個網絡開發框架最難的不是實現某個功能, 而是從零開始一步一步添磚加瓦的造輪子!

做爲一個網絡開發框架, 最重要的兩個功能確定是須要的! 定時器庫、事件驅動庫. 如何抉擇?選項有2個: libev / libuv .

libev 成熟穩定、輕量級、unix like支持、容易嵌入;

libuv 比libev更加優秀,增長了許多功能(線程池、信號、同步、鎖等等),封裝更加完善, 而且增長了windows支持;

從cf框架開發之初選型來看, libuv絕對是目前最優解. 可是做者恰恰選擇了libev. 也今後開始, 艱辛的底層開發之路就此展開.

首先, 做者不讓使用者C/C++進行實際業務開發! 這樣作會讓使用者有較高的開發成本與學習成本, 而選擇一門較好的腳本語言就顯得尤其重要.

做者對Lua還算是稍微熟悉一點, 因此就選了Lua做爲業務腳本語言。至於Lua語言的優點這裏就不說了, 網上大把文章誇它的.

如今既然腳本語言已經選定, 那麼就開始寫代碼吧!Let's Lua.


CF的編寫之路

1. 網絡層

首先, 咱們來看一段C封裝給Lua調用的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實現的C代碼的片斷, 有興趣閱讀源碼的小夥伴請點擊這裏;

衆所周知Lua沒有原生的Socket. 那麼就須要框架編寫者本身抽象底層邏輯從新實現一套API.

簡單的封裝Lua C庫誰都會, 並且也算不上是什麼難事. 可是咱們的目的是將底層同步阻塞Socket hook爲非阻塞, 這時候難點就來了!

你們都知道libev是基於react模型的事件驅動網絡庫, 全部註冊事件後的業務邏輯都是以回調的形式觸發. 那不就變成node-lua代碼了嗎?(笑)

這時候, 做者想了個點子來解決這個問題! 執行流程以下:

  1. 每次須要作一些同步操做的時候, 就調用C API註冊回調事件.
  1. 爲當前註冊的全部事件建立一個Lua協程保存上下文並讓出當前協程執行權.
  1. 等到註冊事件被觸發後, 調用C API恢復協程繼續執行.

簡單來講就是將C層次的異步回調邏輯封裝爲Lua層的同步非阻塞, 保證不由於IO問題阻塞線程.

下面提供一段socket同步非阻塞的僞代碼, 經供參考:

function TCP:recv(bytes)
    local current_co = co_self()
    self.read_co = read_ev(function()
    -- do action
    -- stop timer_ev
    -- wakeup(current_co) 恢復執行權
    end)
   self.timer_co =  self.timer_ev(function()
    -- do action
    -- stop read_ev
    -- wakeup(current_co) 恢復執行權
    end)
    tcp_start(io, EV_READ, self.read_co)
    timer_start(timer, 3秒超時, self.timer_co)
    return co_yield() -- 讓出執行權
end

一個Lua版的Socket EV_READ僞代碼大體的處理流程如上, 想看實際處理邏輯請看這裏

同理, Socket write/connect/listen等等API直接照抄就行(UDP也大同小異). (其實這裏有個小插曲就是SSL SOCKET的坑, 可是因爲篇幅問題就不說了.)

細心的小夥伴可能發現代碼同時註冊了Socket與Timer事件, Socket非阻塞操做不能解決read與connect超時的問題. 因此cf框架乾脆就封裝完全一點.

至此, Socket算是已經算是基本hook與封裝完成了. 接下來就能夠開始寫應用層協議了.

2. 應用層協議

如今Socket終於能正常使用了, 那麼面臨的新問題就又來了。

libev沒有自帶異步dns

dns都還須要使用者本身封裝, 這個坑真是填的無比難受! 好在網絡上有前輩實現了Lua版的異步dns, 做者稍微看明白以後就借用了過來封裝內部使用.

這樣cf也算是有了深度定製的異步dns庫了吧!(雖然並不完善, 可是足夠使用)

一個網絡庫是否流行, 基本上就得看生態. 那麼協議層的輪子又得造起來:

  1. httpdhttpc
  2. mail
  3. mysql
  4. redis
  5. mqtt
  6. websocket

其中一些協議爲各位前輩那邊借過來適配後定製的, 簡單的協議則是直接花1-2小時直接手寫出來的。

3. 封裝與易用性

爲了避免讓API那麼封閉與提高cf的可用性, 做者決定將mysql與redis進行初步封裝.

封裝包括你們經常使用的功能, 鏈接池、面向對象操做、無需手動管理session生命週期等等. 簡化編程思想包袱來提高開發效率.

至於內部Socket更是讓框架來解決釋放問題確保文件描述數量限制的狀況下也是能夠正常使用. (實際上是不喜歡依賴gc被動close fd與free內存)


CF是啥?

若是你耐心看完了第一部分介紹, 那麼你就應該對cf有了一個大概的瞭解.

cf全稱爲: CoreFramework, 是一個基於libev的Lua網絡開發框架. 在其內部實現了多種網絡協議與第三方庫用來幫助使用者進行項目原型的快速開發.

cf 在httpd使用上尊崇前、後端分離的解決方案, 僅實現了基本的view路由而且不支持rest風格的API路由. 雖然這樣可能會引來宇多人的詬病.

cf 的httpd內嵌websocket支持, 方便使用者在複用端口的同時也能夠享受長鏈接編寫的樂趣.

更多的介紹, 請你們項目地址的Wiki


CF能作什麼?

  • 基於容器技術的微服務場景(swarm/kubernetes); —— 推薦

  • 遊戲服務器的前端代理層; —— 推薦

  • 內存/CPU資源較爲緊缺的雲服務器; —— 推薦

  • 對性能要求較高的無狀態集羣; —— 推薦

  • 海量長鏈接(websocket)Agent集羣; —— 推薦


CF使用到的技術棧?

傳輸層: TCP/UDP

會話層: SSL Client支持

協議層: dns/webocket/http/mqtt/redis/mysql/smtp

工具庫: Timer/TASK

第三方庫: Libev、openssl/libressl、lua-5.三、jemalloc/tcmalloc(可選)


CF如何安裝?

cf 目前支持絕大部分Unix like操做系統, 做者是在Mac上進行開發, 因此Mac支持是必須的.

cf測試的Linux爲Centos, 因此基本上基於Linux內核的操做系統編譯後的運行也沒什麼問題(export 增長/usr/local/lib)

同時,做者還貼心的爲你們作了一個簡單Dockerfile. 文件在項目根目錄下, 你們下載直接使用便可。

固然, 若是你不想製做Dockerfile,也可使用Docker命令直接拉去做者製做好放在docker hub的鏡像. candymi/cfweb

使用詳情與使用方法請參考Docker安裝編譯安裝


CF 如何運行呢?

測試運行

bash#: ./cfadmin

後臺運行

bash#: ./cfadmin

退出

killall cfadmin

ctrl + c


文檔在哪?

做者爲你們貼心的寫了一篇詳細到不能再詳細的文檔, 以此來獲取你們的點贊與關注.

做者還爲喜歡閱讀源碼的同窗準備了充足的中文註釋與英文註釋, 結合起來方便你們快速瞭解CF工做方式(中/英註釋結合易於理解一些專屬詞彙).


回答以前的問題:

Q. 性能怎麼樣?

A. 性能還不錯, 可是具體數值請自行測試.

Q. 是否容易上手?

A. 學習lua 一小時入門 -> cf 一小時入門

Q. 開這個項目的初衷是什麼?

A. 其實在前面已經回答過了.

Q. 開發目標在哪?

A. Wiki 裏有TODO

Q. 如何反饋問題?

A. Wiki 裏有Q & A

Q. 對比行業內的lua開源項目有何優點?

A. CF對比其它lua開發項目更深刻改變用戶使用習慣! 簡化框架上手難度, 將框架都黑盒子透明化. 無需學習複雜的設計模式與理念.

Q. CF的開發理念是什麼?

A. CF項目的目標不是競爭, 而是明白明白簡單爲美. 當你習慣了它, 也許你就會上癮.


使用示例

精彩截圖

部署面板

狀態面板

pod日誌

但願

也許你正在使用其它開發框架, 可是這不妨礙你對cf的督促.

也許你正在試用它, 這不妨礙你與做者溝通你的想法.

也許你正在吐槽它的缺點,請來issue盡情吐槽.

文檔與地址

項目文檔

項目地址

相關文章
相關標籤/搜索