工作中曾经开发了一个cobweb
的分布式服务器框架(基于golang
,c
),但是在实际开发过程中代码难以维护以及更新,主要是每次都需要跨平台进行编译,特别是cgo
往往需要指定平台的系统库,而且一些不规范的使用方式造成无法充分发挥多核的优势,可以参见 关于Go协程的思考
虽然1.16 支持抢占式,但错误的使用方式依然造成了cpu过高的问题。,后续重新设计了skynet
是一个actor
模型分布式服务框架,使用go
编写。
尽管Actor
模型和CSP
模型各有所有长,为什么不采用CSP
主要有以下方面考虑。
-
CSP
模式使用尽管很简单,但是一个致命的问题是无法控制消息的优先级,当然若只处理一个Channel
那可以规避,那么为啥还需要使用CSP
,而且像go channel 本身是基于互斥锁(1.16)实现,且无法进行优化和更加精细的控制,只能依赖于runtime
的调度。(网上所说什么时候触发调度,我认为channel不能包含其中,它本质也是加锁导致切换) -
隔离性太弱,后续一些新的
channel
引入也会造成破坏性修改。 -
select-case
模式会随着等待数量的增加性能会慢慢减弱。 -
channel
多大合适?
它是一个年轻的框架,仅仅经历了两款项目的迭代
现在版本为重启了 v2 版本v1.6.0 2023-05-28
与skynet的差异
-
增加了独占进程的概念,对于一些性能敏感的服务可以绕过公平调度的原则。 (公平调度是一个很普遍但并非最优解的调度策略,但对于需要偏占资源较多的场景就显得无力)
-
使用协程而非线程,一个好处是对于一些假死服务我们可以重新启动它,其它代价远小于线程(尽管协程的开销很低,但我们尽量保证不会被滥用)
-
一个简单的二进制文件,
skynet
修改了lua部分虚拟机源码,而且大部分实现都是基于lua
实现,而我设计的是一个将脚本语言作为可选项的插件。 -
所有库都是底层语言的实现方式,可控制力和性能更好,完全将业务和底层区分方便同时进行维护
-
无感的集群交互方式,调用其他服务(无论在不在本地)就像普通消息那样简单,不需要像
skynet
需要显示调用cluster
。 -
进程支持错误重启且消息不会丢失
(beta) -
支持后续的DSL (鸽了两年,还是没写完~)
-
在2024/03我正计划重新用C实现了一版以提供更好的性能和更底层的控制
特性
-
支持纯
Lua
开发,方便更快的开发业务。 -
mysql,redis client支持,值得注意的是这些库都是作为插件实现,跟主库关联不大,需要编译时指定
tags
-
native网络支持,对于golang的网络模型而言它不是一个高性能的解决方案,尤其是对大量长连接的情况,可参见golang的协程思考
-
内置集群组件 sktpmd,易于实现分布式
-
隐藏的数据编码,对业务不透明,提供比
protobuf-v3
更快的编码 kproto -
高性能,完全摒弃
interface{}
以及各种抽象,无任何类型断言,满足cache-line
的结构设计 -
基于
slab
算法实现的无锁内存分配器zmalloc
,比sync.Pool
更快
优势
-
skynet
是过程式以及低抽象的架构。纯函数也更贴合职责单一的原则,也方便后续运行时替换,而低抽象是因为go interface
并非零成本抽象,它有一定的性能代价。所以整个skynet
没有任何接口定义。 -
skynet
仅需要一个执行文件,大小仅仅5.78mb
,默认运行内存仅仅2.2mb
, -
65535 Lua
服务仅占用1.8GB
,也就是每个Lua服务
仅占用28.8kb
-
65535 纯go
服务仅占用120mb
,每个pure go
服务仅占用1.9kb
-
更快的
zmalloc
内存分配器 -
native网络支持,支持
windows,linux,macos
,其他平台未测试。 (需要注意的是windows
是一个残血版本,仅作为调试)