Go 即时通讯体系:日志模块重构,并从main函数开始

news/2025/10/5 9:40:02/文章来源:https://www.cnblogs.com/slgkaifa/p/19126276

Go 即时通讯体系:日志模块重构,并从main函数开始

重构logger

上次写的logger.go过于繁琐,有很多没用到的功能;重构后只提供了简洁的日志接口,支持日志轮转、多级别日志记录等功能,并采用单例模式确保全局只有一个日志实例

全局变量

var (
once sync.Once // 用于实现单例模式的同步控制
Logger *zap.Logger // 全局日志实例
String = zap.String // 导出常用的 zap.Field 构造方法
Any = zap.Any
Err = zap.Error
Int = zap.Int
Float32 = zap.Float32
)

默认配置常量

const (
defaultLogPath = "./logs" // 默认日志存储目录
defaultLogLevel = "debug" // 默认日志级别
defaultMaxSize = 100 // 单个日志文件最大大小(MB)
defaultMaxBackups = 30 // 保留的旧日志文件数量
defaultMaxAge = 7 // 日志保留天数
defaultCompress = true // 是否压缩旧日志
)

初始化日志记录器

// Init 初始化日志记录器(单例模式)
func Init(
) *zap.Logger {
once.Do(
func(
) {
// 确保日志目录存在
if err := os.MkdirAll(defaultLogPath, 0755
)
; err != nil {
panic(err)
}
// 设置日志级别
level := getLogLevel(defaultLogLevel)
// 日志文件路径
logFile := getDatedLogFilename(defaultLogPath)
// 设置日志轮转
writer := zapcore.AddSync(&lumberjack.Logger{
Filename: logFile,
MaxSize: defaultMaxSize, // MB
MaxBackups: defaultMaxBackups, // 保留的旧日志文件数量
MaxAge: defaultMaxAge, // 保留天数
Compress: defaultCompress, // 是否压缩
}
)
// 编码器配置
encoderConfig := zap.NewProductionEncoderConfig(
)
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder // 不带颜色
// 核心配置
core := zapcore.NewCore(
// zapcore.NewJSONEncoder(encoderConfig), // json格式
zapcore.NewConsoleEncoder(encoderConfig)
, // 使用 Console 编码器
writer,
level,
)
// 创建Logger
Logger = zap.New(core)
}
)
return Logger
}

获取日志实例

func GetLogger(
) *zap.Logger {
if Logger == nil {
Init(
) // 自动初始化
}
return Logger
}

直接可用的日志方法

func Debug(msg string
, fields ...zap.Field) {
GetLogger(
).Debug(msg, fields...
)
}
func Info(msg string
, fields ...zap.Field) {
GetLogger(
).Info(msg, fields...
)
}
func Warn(msg string
, fields ...zap.Field) {
GetLogger(
).Warn(msg, fields...
)
}
func Error(msg string
, fields ...zap.Field) {
GetLogger(
).Error(msg, fields...
)
}
func Fatal(msg string
, fields ...zap.Field) {
GetLogger(
).Fatal(msg, fields...
)
}
func Sync(
) error {
return GetLogger(
).Sync(
)
}

代码地址:logger.go

从main函数开始

func main(
) {
defer log.Sync(
) // 记录日志
log.Info("start chat server..."
)
newRouter := router.NewRouter(
)
go chat.MyServer.Start(
)
s := &http.Server{
Addr: ":8080"
,
Handler: newRouter,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 <<
20
,
}
err := s.ListenAndServe(
)
if err != nil {
log.Error("server start error"
, log.Err(err)
)
}
}
  1. 初始化路由
    • 创建 HTTP 请求路由器。
    • 会在这里定义所有的 API 端点(endpoints)和对应的处理函数。
    • 返回一个实现了 http.Handler 接口的路由器对象。
  2. 启动后台服务
    • 使用 go 关键字启动一个 goroutine,异步运行 chat.MyServer.Start() 方法。
    • 这通常用于启动需要长期运行的 WebSocket 服务。
  3. 配置 HTTP 服务器
  4. 启动 HTTP 服务器:开始监听指定端口(8080)的 HTTP 请求。

自定义Gin路由

NewRouter 函数解析

func NewRouter(
) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
server := gin.Default(
)
server.Use(Cors(
)
)
server.Use(Recovery)
socket := RunSocket
group := server.Group(""
)
{
// 用户管理功能
group.GET("/user"
, api.GetUserList)
group.GET("/user/:uuid"
, api.GetUserDetails)
group.GET("/user/name"
, api.GetUserOrGroupByName)
group.POST("/user/register"
, api.Register)
group.POST("/user/login"
, api.Login)
group.PUT("/user"
, api.ModifyUserInfo)
group.POST("/friend"
, api.AddFriend)
group.GET("/message"
, api.GetMessage)
group.GET("/file/:fileName"
, api.GetFile)
group.POST("/file"
, api.SaveFile)
group.GET("/group/:uuid"
, api.GetGroup)
group.POST("/group/:uuid"
, api.SaveGroup)
group.POST("/group/join/:userUuid/:groupUuid"
, api.JoinGroup)
group.GET("/group/user/:uuid"
, api.GetGroupUsers)
group.GET("/socket.io"
, socket)
}
return server
}
  1. Gin 模式设置gin.SetMode(gin.ReleaseMode) 将 Gin 设置为发布模式,减少调试信息输出。
  2. 中间件使用Cors() 中间件处理跨域请求,Recovery 中间件捕获并处理 panic。
  3. 路由分组:所有路由都定义在根分组 ("") 下,清晰的 RESTful 风格路由设计。
  4. 路由类型:用户管理:GET/POST/PUT 操作;文件管理:文件上传/下载;WebSocket:实时通信端点。

跨域处理中间件 (Cors)

func Cors(
) gin.HandlerFunc {
return
func(c *gin.Context) {
method := c.Request.Method
origin := c.Request.Header.Get("Origin"
)
if origin != "" {
// 设置跨域响应头
c.Header("Access-Control-Allow-Origin"
, "*"
)
c.Header("Access-Control-Allow-Methods"
, "POST, GET, OPTIONS, PUT, DELETE, UPDATE"
)
c.Header("Access-Control-Allow-Headers"
, "Origin, X-Requested-With, Content-Type, Accept, Authorization"
)
c.Header("Access-Control-Expose-Headers"
, "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type"
)
c.Header("Access-Control-Allow-Credentials"
, "true"
)
}
// 处理 OPTIONS 预检请求
if method == "OPTIONS" {
c.JSON(http.StatusOK, "ok!"
)
return
}
// 异常捕获
defer
func(
) {
if err := recover(
)
; err != nil {
log.Error("HttpError"
, log.Any("HttpError"
, err)
)
}
}(
)
c.Next(
) // 处理请求
}
}
  1. 跨域头设置
    • 允许所有来源 (*),生产环境应替换为具体域名
    • 允许的 HTTP 方法
    • 允许的请求头
    • 允许客户端访问的响应头
    • 允许携带凭证
  2. OPTIONS 请求处理:直接返回 200 状态码,满足浏览器的预检请求。
  3. 异常捕获:使用 defer+recover 捕获处理过程中的 panic,记录错误日志。

恢复中间件 (Recovery)

func Recovery(c *gin.Context) {
defer
func(
) {
if r := recover(
)
; r != nil {
log.Error("gin catch error"
, log.Any("error"
, r)
)
c.JSON(http.StatusOK, response.FailMsg("系统内部错误"
)
)
}
}(
)
c.Next(
)
}

panic 恢复:捕获路由处理函数中可能发生的 panic,记录错误日志

WebSocket 实现 (RunSocket)

var upGrader = websocket.Upgrader{
CheckOrigin:
func(r *http.Request) bool {
return true // 允许所有跨域 WebSocket 连接
}
,
}
func RunSocket(c *gin.Context) {
user := c.Query("user"
) // 获取用户标识
if user == "" {
return // 无用户标识则拒绝连接
}
log.Info("newUser"
, log.String("newUser"
, user)
)
// 升级 HTTP 连接为 WebSocket 连接
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil
)
if err != nil {
return
}
// 创建客户端对象
client := &server.Client{
Name: user,
Conn: ws,
Send: make(
chan []byte
)
,
}
// 注册客户端到服务器
server.MyServer.Register <- client
// 启动读写协程
go client.Read(
)
go client.Write(
)
}
  1. WebSocket 升级
    • 使用 gorilla/websocket 包的 Upgrader 将 HTTP 连接升级为 WebSocket 连接
    • CheckOrigin 返回 true 允许所有跨域连接(生产环境应做限制)
  2. 客户端管理
    • 通过 user 查询参数识别用户,创建客户端对象,包含 WebSocket 连接和消息通道
    • 通过注册通道将客户端注册到中央服务器。
  3. 并发处理:为每个客户端启动独立的读 (client.Read()) 和写 (client.Write()) 协程,实现全双工通信。

代码地址:IM-Go

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/928082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

作为一个高中生开发者,我的所思所想

作为一个高中生开发者,我的所思所想各位若觉得我年幼无知、缺乏阅历、不知天高地厚,尽可直言 —— 这些评价,真没毛病。 要是觉得有些ai味,没错,写完用ai润色了一下 自我介绍 我是上海一所高中的高二学生,就读于…

[Godot] 如何导出安卓 APK 并在手机上调试 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

代码随想录算法训练营 Day61 图论ⅩⅠ Floyd A※ 最短路径算法 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

旅游网站系统设计wordpress图文标题一体布局

解析 倍增真香 关键性质&#xff1a;树上距离一个点最远的点必定是直径两端点其一。 本题限制好&#xff0c;要求少动态维护倍增数组暴力维护直径即可。 如果每次合并的是两棵树&#xff0c;而不是一棵树加一个点&#xff0c;可以先离线下来&#xff0c;照样能做。 如果每次强…

视频付费网站建设网站怎样做反向链接

目录 lua_shared_dict lua-resty-lrucache 新建 设置 获取 删除 综合使用案例 计数 全部刷新 lua_shared_dict 语法&#xff1a; lua_shared_dict <名称> <大小> 默认值&#xff1a; 否 上下文&#xff1a; http 阶段&#xff1a; 取决于使用情况 声明一…

Elastic Stack 9.1.4 发布:重要安全更新与功能优化

Elastic Stack 9.1.4 版本正式发布,建议用户升级至该最新版本。本文详细介绍了该版本的修复问题和各产品变更列表,包含重要的安全更新和功能改进。Elastic Stack 9.1.4 发布 作者:Stamatis Kourkoutas 发布日期:20…

2025钛白粉源头厂家最新推荐排行榜:覆盖广东珠三角东莞华南深圳长三角地区的优质供应商解析

当前钛白粉市场需求持续升级,下游涂料、塑料、造纸等行业对产品白度、遮盖力、相容性等指标要求愈发严苛,同时环保政策趋严倒逼行业技术革新。然而,市场上部分厂家存在技术储备不足、质量管理体系不完善等问题,导致…

详细介绍:Ubuntu开机自动运行Docker容器中的Qt UI程序

详细介绍:Ubuntu开机自动运行Docker容器中的Qt UI程序2025-10-05 09:26 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; d…

免费网站建设公司联系方式wordpress会员登陆

弗洛伊德算法大致有点像dp的推导 dp[i][j] min(dp[i][k] dp[k][j], dp[i][j]), 其中 i 是起始点&#xff0c;j 是终止点。k是它们经过的中途点。 通过这个公式不断地更新dp[i][j],得到最短路径长。 我们先定义两个矩阵&#xff0c;minpath[i][j],表示的是从 i 到 j 当前得到的…

完整教程:图论回溯

完整教程:图论&回溯pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &q…

自已创建网站要怎么做网站策划就业前景

一个整数由个位、十位、百味...组成&#xff0c;我们知道整数可以用int型表示&#xff0c;那么一个整数到底是几位数呢&#xff1f;&#xff1f; 下面这个代码就是来计算位数的&#xff1a; #include<stdio.h>// 获得num的位数 int getbit(int num) {if(num 0)return 0…

用 Whisper 打破沉默:AI 语音技术如何重塑无障碍沟通方式? - 指南

用 Whisper 打破沉默:AI 语音技术如何重塑无障碍沟通方式? - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &quo…

什么事三合一网站百度免费推广

光电耦合器作为一种关键的电子连接器&#xff0c;在航天航空领域扮演着重要角色。本文将深入探讨光电耦合器在航天航空领域的应用及其技术特点。 光电耦合器在航天航空领域的应用 光电耦合器作为一种高可靠性、高速传输、抗干扰能力强的连接器&#xff0c;在航天航空领域有着广…

实用指南:【论文阅读 | PR 2024 |ICAFusion:迭代交叉注意力引导的多光谱目标检测特征融合】

实用指南:【论文阅读 | PR 2024 |ICAFusion:迭代交叉注意力引导的多光谱目标检测特征融合】pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impo…

软件设计师难考吗网站seo规划

AutoUpdater.NET 是一款用于WPF、Winform软件版本更新的框架&#xff0c;类似框架还有Squirrel、WinSparkle、NetSparkle、Google Omaha。 一、安装AutoUpdater.NET 首先&#xff0c;您需要在项目中安装AutoUpdater.NET库。您可以通过NuGet包管理器来安装它。在Visual Studio中…

做网站一般要了解哪些重庆妇科医院排名最好的医院

android提高UI的流畅度Android中所有的界面绘制工作都是在UI线程中进行的&#xff0c;提高UI流畅度的最核心根本在于释放UI线程。即:不在主线程中做耗时的操作。很多人都知道&#xff0c;耗时的操作要放到子线程中去做&#xff0c;比如访问网络&#xff0c;比如读写sd卡。像这类…

生成式AI改进极端多标签分类技术

本文介绍利用生成式AI改进极端多标签分类的新方法,通过层次化标签聚类解决长尾分布问题,提出XLGen-BCL和XLGen-MCG两种架构,在多个数据集上验证了聚类引导模型在整体性能和罕见标签分类上的优势。会议信息 EACL 202…

2025.10.5——1绿

普及+/提高 P2216 [HAOI2007] 理想的正方形 单调队列优化的类似悬线法的题。

NIO----JAVA - 教程

NIO----JAVA - 教程2025-10-05 09:08 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-fa…