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尽情吐槽.

文档与地址

项目文档

项目地址

相关文章
相关标签/搜索