发布基础组件包-KISS(日常用作游戏服务器开发,但不限于游戏服务器开发)

weixin_38052602 2019-09-20 10:45:26
# KISS - Keep It Simple & Stupid

[![MIT licensed][1]][2]
[![Go Report Card][3]][4]

[1]: https://img.shields.io/badge/license-MIT-blue.svg
[2]: LICENSE.md
[3]: https://goreportcard.com/badge/github.com/nothollyhigh/kiss
[4]: https://goreportcard.com/report/github.com/nothollyhigh/kiss


- [KISS原则](https://zh.wikipedia.org/wiki/KISS%E5%8E%9F%E5%88%99) 是指在设计当中应当注重简约的原则

- 作者水平有限,欢迎交流和指点,qq群: 817937655

- [https://github.com/nothollyhigh/kiss](https://github.com/nothollyhigh/kiss)

## 安装
* go get github.com/nothollyhigh/kiss/...


## KISS可以用来做什么?

- 有的人喜欢"框架"这个词,KISS的定位是提供一些基础组件方便搭积木实现架构方案,组件不限于项目类型

- 作者主要从事游戏和web服务器开发,常用来构建游戏服务器,一些示例:

> 1. [单进程服务器示例](https://github.com/nothollyhigh/hellokiss)

> 2. [kissgate网关](https://github.com/nothollyhigh/kissgate),支持kiss格式的tcp/websocket连接反向代理到tcp服务,支持线路检测、负载均衡、realip等,常用来做游戏集群的网关,kiss协议格式详见 [net包](https://github.com/nothollyhigh/kiss/blob/master/net/README.md)

> 3. 集群是不同功能服务的拆分和实现,每个游戏的需求都可能不一样,请根据实际需求自行设计和实现

## KISS组件包简介

### 一、[net,网络包](https://github.com/nothollyhigh/kiss/blob/master/net/README.md)

1. Tcp
可以用做游戏服务器,支持自定义协议格式、压缩、加密等

2. Websocket
可以用做游戏服务器,支持自定义协议格式、压缩、加密等

3. Rpc
可以灵活使用任意序列化、反序列化,给用户更多自由,如protobuf、json、msgpack、gob等
支持服务端异步处理,服务端不必须在方法中处理完调用结果,可以异步处理结束后再发送结果
不像GRPC等需要生成协议、按格式写那么多额外的代码,用法上像写 net/http 包的路由一样简单

4. Http
支持优雅退出、pprof等

- 详见 [net](https://github.com/nothollyhigh/kiss/blob/master/net/README.md)

### 二、[log,日志包](https://github.com/nothollyhigh/kiss/blob/master/log/README.md)

- 自己实现 log 包之前,我简单尝试过标准库的 log 和一些三方的日志包,但是对日志文件落地不太友好,
比如日志文件按目录拆分、文件按时间和size切分、日志位置信息等

- KISS 的 log 包日志支持:

1. 日志位置信息,包括文件、行数
2. 支持文件日志,支持bufio
3. 文件日志按时间拆分目录
4. 文件日志按时间格式切分
5. 文件日志按size切分
6. 支持钩子对日志做结构化或其他自定义处理

- 详见 [log](https://github.com/nothollyhigh/kiss/blob/master/log/README.md)

### 三、[sync包](https://github.com/nothollyhigh/kiss/blob/master/sync/README.md)

- 开启debug支持死锁告警
web相关无状态服务通常不需要锁,游戏逻辑多耦合,不小心可能导致死锁,可以用这个包来排查

- WaitSession用法类似标准库的WaitGroup,但是可以指定session进行等待,支持超时

- 详见 [sync](https://github.com/nothollyhigh/kiss/blob/master/sync/README.md)

### 四、[timer,定时器](https://github.com/nothollyhigh/kiss/blob/master/timer/README.md)

- 标准库的 time.AfterFunc 触发时会创建一个协程来调用回调函数,大量定时器短时间内集中触发时开销较大

- KISS 的 timer 是小堆实现的,一个实例只需要一个协程就可以管理大量定时器,支持同步异步接口
主要用于优化标准库 time.AfterFunc 的协程开销,但要注意线头阻塞的问题
耗时较长的定时器回调建议仍使用 time.AfterFunc

- 详见 [timer](https://github.com/nothollyhigh/kiss/blob/master/timer/README.md)

### 五、[event,事件包](https://github.com/nothollyhigh/kiss/blob/master/event/README.md)

- 进程内的发布订阅组件,观察者模式,可以用于模块间解耦

- 详见 [event](https://github.com/nothollyhigh/kiss/blob/master/event/README.md)

### 六、[util,杂货铺](https://github.com/nothollyhigh/kiss/blob/master/util/README.md)

- 最常用的 Go,HandlePanic,Safe,处理panic,打印异常调用栈信息

- Workers,任务池,用于控制一定数量的协程异步处理任务
WorkersLink,也是任务池,但在Workers基础上做了一点扩展,支持异步处理的顺序组装

- Qps,方便统计、打印一些qps功能

- 详见 [util](https://github.com/nothollyhigh/kiss/blob/master/util/README.md)
...全文
186 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
weixin_38086437 2019-09-20
  • 打赏
  • 举报
回复
自己顶~
weixin_38104729 2019-09-20
  • 打赏
  • 举报
回复
再顶~
weixin_38064374 2019-09-20
  • 打赏
  • 举报
回复
### tcp echo server ```golang package main import ( "github.com/nothollyhigh/kiss/log" "github.com/nothollyhigh/kiss/net" "time" ) var () const ( addr = ":8888" CMD_ECHO = uint32(1) ) func onEcho(client *net.TcpClient, msg net.IMessage) { log.Info("tcp server onEcho from %v: %v", client.Conn.RemoteAddr().String(), string(msg.Body())) client.SendMsg(msg) } func main() { server := net.NewTcpServer("Echo") // 初始化协议号 server.Handle(CMD_ECHO, onEcho) server.Serve(addr, time.Second*5) } ``` ### tcp echo client ```golang package main import ( "fmt" "github.com/nothollyhigh/kiss/log" "github.com/nothollyhigh/kiss/net" "time" ) var ( addr = "127.0.0.1:8888" CMD_ECHO = uint32(1) ) func onConnected(c *net.TcpClient) { log.Info("TcpClient OnConnected") } func onEcho(client *net.TcpClient, msg net.IMessage) { log.Debug("tcp client onEcho from %v: %v", client.Conn.RemoteAddr().String(), string(msg.Body())) } func main() { autoReconn := true netengine := net.NewTcpEngine() // 初始化协议号 netengine.Handle(CMD_ECHO, onEcho) client, err := net.NewTcpClient(addr, netengine, nil, autoReconn, onConnected) if err != nil { log.Panic("NewTcpClient failed: %v, %v", client, err) } for i := 0; true; i++ { err = client.SendMsg(net.NewMessage(CMD_ECHO, []byte(fmt.Sprintf("hello %v", i)))) if err != nil { log.Error("tcp client echo failed: %v", err) } time.Sleep(time.Second) } } ```
weixin_38067327 2019-09-20
  • 打赏
  • 举报
回复
### websocket echo server ```golang package main import ( "github.com/nothollyhigh/kiss/log" "github.com/nothollyhigh/kiss/net" ) var ( addr = ":8888" CMD_ECHO = uint32(1) ) func onEcho(client *net.WSClient, msg net.IMessage) { log.Info("ws server onEcho from %v: %v", client.Conn.RemoteAddr().String(), string(msg.Body())) client.SendMsg(msg) } func main() { server, err := net.NewWebsocketServer("echo", addr) if err != nil { log.Panic("NewWebsocketServer failed: %v", err) } // 初始化http ws路由 server.HandleWs("/ws/echo") // 初始化协议号 server.Handle(CMD_ECHO, onEcho) server.Serve() } ``` ### websocket echo client ```golang package main import ( "fmt" "github.com/nothollyhigh/kiss/log" "github.com/nothollyhigh/kiss/net" "time" ) var ( addr = "ws://localhost:8888/ws/echo" CMD_ECHO = uint32(1) ) func onEcho(client *net.WSClient, msg net.IMessage) { log.Debug("ws client onEcho from %v: %v", client.Conn.RemoteAddr().String(), string(msg.Body())) } func main() { client, err := net.NewWebsocketClient(addr) if err != nil { log.Panic("NewWebsocketClient failed: %v, %v", err, time.Now()) } // 初始化协议号 client.Handle(CMD_ECHO, onEcho) for i := 0; true; i++ { err = client.SendMsg(net.NewMessage(CMD_ECHO, []byte(fmt.Sprintf("hello %v", i)))) if err != nil { log.Error("ws client echo failed: %v", err) break } time.Sleep(time.Second) } } ```
weixin_38081011 2019-09-20
  • 打赏
  • 举报
回复
### rpc server ```golang package main import ( "github.com/nothollyhigh/kiss/log" "github.com/nothollyhigh/kiss/net" "time" ) var ( addr = "0.0.0.0:8888" ) type HelloRequest struct { Message string } type HelloResponse struct { Message string } // Hello方法 func Hello(ctx *net.RpcContext) { req := &HelloRequest{} err := ctx.Bind(req) if err != nil { log.Error("Hello failed: %v", err) return } // 直接回包 // err = ctx.Write(&HelloResponse{Message: req.Message}) // if err != nil { // log.Error("Hello failed: %v", err) // return // } // log.Info("HelloRequest: %v", req.Message) // 支持异步回包 go func() { err = ctx.Write(&HelloResponse{Message: req.Message}) if err != nil { log.Error("Hello failed: %v", err) return } log.Info("HelloRequest: %v", req.Message) }() } func main() { server := net.NewRpcServer("Rpc") // 初始化方法,类似http初始化路由 server.HandleRpcMethod("Hello", Hello) // 启动服务 server.Serve(addr, time.Second*5) } ``` ### rpc client ```golang package main import ( "github.com/nothollyhigh/kiss/log" "github.com/nothollyhigh/kiss/net" "time" ) var ( addr = "0.0.0.0:8888" ) type HelloRequest struct { Message string } type HelloResponse struct { Message string } func onConnected(c *net.TcpClient) { log.Info("RpcClient OnConnected") } func main() { engine := net.NewTcpEngine() client, err := net.NewRpcClient(addr, engine, nil, onConnected) if err != nil { log.Panic("NewReqClient Error: %v", err) } for { req := &HelloRequest{Message: "kiss"} rsp := &HelloResponse{} // 调用Hello方法 err = client.Call("Hello", req, rsp, time.Second*3) if err != nil { log.Error("Hello failed: %v", err) } else { log.Info("HelloResponse: %v", rsp.Message) } time.Sleep(time.Second) } } ```
weixin_38081402 2019-09-20
  • 打赏
  • 举报
回复
自己顶
weixin_38082570 2019-09-20
  • 打赏
  • 举报
回复
### 日志 #### 一、日志文件切割 ```golang package main import ( "fmt" "github.com/nothollyhigh/kiss/log" "io" "os" "time" ) func main() { fileWriter := &log.FileWriter{ RootDir: "./logs/" + time.Now().Format("20060102150405/"), //日志根目录,每次启动新建目录 DirFormat: "200601021504/", //按时间格式分割日志文件子目录,""则不拆分子目录;此处测试按分钟 FileFormat: "20060102150405.log", //按时间格式切割日志文件,此处测试按秒 MaxFileSize: 1024 * 256, //按最大size切割日志文件 EnableBufio: false, //是否启用bufio,重要日志建议不开启 } out := io.MultiWriter(os.Stdout, fileWriter) log.SetOutput(out) log.SetLevel(log.LEVEL_WARN) i := 0 for { i++ log.Debug(fmt.Sprintf("log %d", i)) log.Info(fmt.Sprintf("log %d", i)) log.Warn(fmt.Sprintf("log %d", i)) log.Error(fmt.Sprintf("log %d", i)) } } ``` #### 二、在gin中使用kiss log ```golang package main import ( "fmt" "github.com/gin-gonic/gin" "github.com/nothollyhigh/kiss/log" "os" "time" ) func main() { router := gin.New() router.Use(func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path raw := c.Request.URL.RawQuery c.Next() end := time.Now() latency := end.Sub(start) clientIP := c.ClientIP() method := c.Request.Method statusCode := c.Writer.Status() comment := c.Errors.ByType(gin.ErrorTypePrivate).String() if raw != "" { path = path + "?" + raw } logLevel := log.LEVEL_INFO if latency > time.Second { logLevel = log.LEVEL_WARN } fmt.Fprintf(os.Stdout, log.LogWithFormater(logLevel, log.DefaultLogDepth-1, log.DefaultLogTimeLayout, "| %3d | %13v | %15s | %-7s %s\n%s", statusCode, latency, clientIP, method, path, comment, )) }) //gin.SetMode(gin.ReleaseMode) n := 0 router.GET("/hello", func(c *gin.Context) { n++ if n%2 == 1 { time.Sleep(time.Second) } c.String(200, "hello") }) router.Run(":8080") } ``` #### 三、自定义日志处理 ```golang package main import ( "encoding/json" "fmt" "github.com/nothollyhigh/kiss/log" "runtime" "strings" ) var ( // 按天切割日志文件,日志根目录下不设子目录,不限制单个日志文件大小 fileWriter = &log.FileWriter{ RootDir: "./logs/", //日志根目录 DirFormat: "20060102-1504/", //日志根目录下无子目录 FileFormat: "20060102.log", //日志文件命名规则,按天切割文件 MaxFileSize: 1024 * 1024, //单个日志文件最大size,0则不限制size EnableBufio: false, //是否开启bufio } ) // 实现log.ILogWriter接口 type LogWriter struct{} func (w *LogWriter) WriteLog(l *log.Log) (n int, err error) { _, file, line, ok := runtime.Caller(l.Depth + 1) if !ok { file = "???" line = -1 } else { pos := strings.LastIndex(file, "/") if pos >= 0 { file = file[pos+1:] } } l.File = file l.Line = line data, _ := json.Marshal(l) data = append(data, '\n') n, err = fileWriter.Write(data) fmt.Printf(string(data)) return n, err } func main() { // 设置为nil则默认的日志不处理,如果需要可以同时使用 log.SetOutput(nil) // 设置自定义日志处理接口 log.SetStructOutput(&LogWriter{}) log.SetLevel(log.LEVEL_WARN) for i := 0; i < 10000; i++ { log.Debug("log %d", i) log.Info("log %d", i) log.Warn("log %d", i) log.Error("log %d", i) } } ```
内容概要:本文围绕三相并网逆变器的控制策略展开研究,重点探讨了虚拟阻抗与统一有源阻尼相结合的控制方法,并实现了SVPWM(空间矢量脉宽调制)与SPWM(正弦脉宽调制)两种调制方式在Simulink平台下的仿真建模。通过引入虚拟阻抗改善系统输出阻抗特性,结合统一有源阻尼技术有效抑制LC或LCL滤波器引起的谐振问题,从而提升逆变器在弱电网条件下的并网稳定性与电能质量。研究涵盖了控制策略的设计、调制算法的实现、动态响应分析及谐波抑制效果评估,同时拓展涉及正负序分离、中点电位平衡、DPWMA调制等关键技术,构建了完整的高性能并网逆变器控制系统仿真体系。; 适合人群:适用于从事电力电子、新能源发电、智能电网及相关领域的研究生、科研人员和工程技术人员,特别是具备三相并网逆变器控制理论基础并熟悉MATLAB/Simulink仿真环境的专业人士;; 使用场景及目标:①用于高校与科研机构开展并网逆变器稳定性与控制策略的深入研究;②支撑学位论文撰写、学术期刊投稿或科研项目申报中的仿真验证工作;③为企业研发高性能、高可靠性的并网逆变器产品提供先进的控制方案与技术原型支持;; 阅读建议:建议读者结合提供的Simulink模型文件进行实际操作与仿真验证,重点关注虚拟阻抗参数设计与有源阻尼的协同作用机制,深入理解不同调制策略对系统性能的影响,并可进一步拓展学习文中提及的正负序控制、中点电位平衡等先进控制技术,全面提升对复杂电网环境下并网系统稳定运行机制的认知水平。
内容概要:本文围绕基于近端梯度算法求解LASSO分位数回归的短期风电功率预测方法展开研究,旨在提升预测模型在复杂环境下的精度与鲁棒性。文章系统构建了LASSO分位数回归模型,深入剖析其数学原理,并引入近端梯度算法进行高效优化求解,有效应对高维稀疏数据与异常值干扰等问题。通过Matlab平台完成了完整的算法实现与仿真实验,利用实际风电数据验证了该方法在不同分位点下的预测性能,结果表明其相较于传统方法具有更强的稳定性和准确性。此外,文档还整合了电力系统、机器学习、路径规划等多个领域的相关科研方向与技术应用案例,突出该方法在新能源预测与智能优化中的广泛适用性与实践价值。; 适合人群:具备扎实的数学基础(如凸优化、统计学习)与Matlab编程能力,从事新能源发电预测、电力系统调度、智能优化算法或机器学习等领域的科研人员、工程技术人员及研究生。; 使用场景及目标:①应用于短期风电功率预测,增强模型对噪声、异常值及非平稳特性的适应能力,提升电网调度的安全性与经济性;②为研究LASSO回归、分位数回归及近端梯度优化算法的学者提供可复现、可扩展的Matlab代码实例,便于算法改进与对比实验;③作为可再生能源消纳、电力市场出清、微网能量管理等工程场景下的核心数据分析与决策支持工具。; 阅读建议:此资源以算法实现为核心,建议读者在掌握LASSO与分位数回归理论基础上,结合Matlab代码逐行分析近端梯度算法的迭代流程与收敛特性,重点关注正则化项与损失函数的权衡机制。同时可借鉴文中丰富的科研案例,拓展至光伏预测、负荷预测等相似应用场景,注重理论推导、代码实现与实验验证的深度融合。
内容概要:本文围绕铰接式重型车辆的稳健路径跟踪控制问题,提出并实现了基于H∞控制器与鲁棒线性二次调节器(RLQR)的控制策略。通过建立车辆动力学模型,针对系统中存在的外部干扰与参数不确定性,设计H∞控制器以增强系统的抗干扰能力,并结合RLQR优化控制性能,在保证稳定性的同时提升路径跟踪精度。研究利用Matlab进行仿真验证,对比不同工况下的控制效果,展示了所提方法在复杂行驶环境下的优越性与鲁棒性。; 适合人群:具备自动控制理论基础、车辆工程或自动化相关背景,熟悉Matlab/Simulink仿真工具,从事智能车辆控制、路径跟踪算法研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于铰接式重型车辆(如矿用卡车、大型拖挂车)的自动驾驶路径跟踪控制系统设计;②为解决存在模型不确定性和外界扰动条件下的鲁棒控制问题提供算法参考与实现范例;③服务于高校科研项目、毕业论文或工业界智能运输系统的技术开发。; 阅读建议:此资源以Matlab代码为核心支撑,建议读者在学习过程中结合控制理论基础知识,运行并调试所提供的仿真程序,深入理解H∞与RLQR控制器的设计流程与参数整定方法,同时可通过修改模型参数或引入新的扰动场景进行拓展性实验,以增强实际应用能力。

479

社区成员

发帖
与我相关
我的任务
社区描述
其他技术讨论专区
其他 技术论坛(原bbs)
社区管理员
  • 其他技术讨论专区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧