Ainx的消息封装

Go 语言数据库事务详解:从 db.Begin 到 Tx 的正确用法 本文详解Go语言中database/sql事务的正确用法,从*sql.Tx的底层模型出发,阐述事务与连接池的关系,提供标准事务模板,并强调使用db.BeginTx控制隔离级别与超时。重点剖析常见陷阱:误用db而非tx执行SQL、defer顺序错误、忽略Commit错误、事务过长导致资源耗尽等。结合可运行示例,涵盖事务内查询、预处理语句及保存点模拟嵌套事务,全面覆盖工程实践中的关键细节。 阅读详情
在这里插入图片描述

📕作者简介: 过去日记,致力于Java、GoLang,Rust等多种编程语言,热爱技术,喜欢游戏的博主。
📗本文收录于Ainx系列,大家有兴趣的可以看一看
📘相关专栏Rust初阶教程、go语言基础系列、spring教程等,大家有兴趣的可以看一看
📙Java并发编程系列,设计模式系列、go web开发框架 系列正在发展中,喜欢Java,GoLang,Rust,的朋友们可以关注一下哦!


📙 本文大部分都是借鉴刘丹冰大佬的zinx框架和文章,更推荐大家去读大佬的原文,本文只是个人学习的记录

Ainx的消息封装

接下来我们再对Ainx做一个简单的升级,现在我们把服务器的全部数据都放在一个Request里,当前的Request结构如下:

type Request struct {
    conn ziface.IConnection //已经和客户端建立好的链接
    data []byte             //客户端请求的数据
}

很明显,现在是用一个[]byte来接受全部数据,又没有长度,又没有消息类型,这不科学。怎么办呢?我们现在就要自定义一种消息类型,把全部的消息都放在这种消息类型里。

创建消息封装类型

在ainx/ainterface/下创建imessage.go文件

ainx/ainterface/imessage.go

package ainterface

/*
将请求的一个消息封装到message中,定义抽象层接口
*/
type IMessage interface {
	GetDataLen() uint32 //获取消息数据段长度
	GetMsgId() uint32   //获取消息ID
	GetData() []byte    //获取消息内容

	SetMsgId(uint32)   //设计消息ID
	SetData([]byte)    //设计消息内容
	SetDataLen(uint32) //设置消息数据段长度
}

同时创建实例message类,在ainx/anet/下,创建message.go文件

ainx/anet/message.go

package anet

type Message struct {
	Id      uint32 //消息的ID
	DataLen uint32 //消息的长度
	Data    []byte //消息的内容
}

// 创建一个Message消息包
func NewMsgPackage(id uint32, data []byte) *Message {
	return &Message{
		Id:      id,
		DataLen: uint32(len(data)),
		Data:    data,
	}
}

// 获取消息数据段长度
func (msg *Message) GetDataLen() uint32 {
	return msg.DataLen
}

// 获取消息ID
func (msg *Message) GetMsgId() uint32 {
	return msg.Id
}

// 获取消息内容
func (msg *Message) GetData() []byte {
	return msg.Data
}

// 设置消息数据段长度
func (msg *Message) SetDataLen(len uint32) {
	msg.DataLen = len
}

// 设计消息ID
func (msg *Message) SetMsgId(msgId uint32) {
	msg.Id = msgId
}

// 设计消息内容
func (msg *Message) SetData(data []byte) {
	msg.Data = data
}

整理一个基本的message包,会包含消息ID,数据,数据长度三个成员,提供基本的setter和getter方法,目的是为了以后做封装优化的作用。同时也提供了一个创建一个message包的初始化方法NewMegPackage。

消息的封包与拆包

我们这里就是采用经典的TLV(Type-Len-Value)封包格式来解决TCP粘包问题吧。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
由于Ainx也是TCP流的形式传播数据,难免会出现消息1和消息2一同发送,那么zinx就需要有能力区分两个消息的边界,所以Ainx此时应该提供一个统一的拆包和封包的方法。在发包之前打包成如上图这种格式的有head和body的两部分的包,在收到数据的时候分两次进行读取,先读取固定长度的head部分,得到后续Data的长度,再根据DataLen读取之后的body。这样就能够解决粘包的问题了。

创建拆包封包抽象类

在ainx/ainterface下,创建idatapack.go文件
ainterface

ainx/ainterface/idatapack.go

package ainterface

/*
封包数据和拆包数据
直接面向TCP链接中的数据流,为传输数据添加头部信息,用于处理TCP粘包问题。
*/
type IDataPack interface {
	GetHeadLen() uint32                //获取包头长度方法
	Pack(msg IMessage) ([]byte, error) //封包方法
	Unpack([]byte) (IMessage, error)   //拆包方法
}

实现拆包封包类

在ainx/anet/下,创建datapack.go文件.

ainx/anet/datapack.go

package anet

import (
	"ainx/ainterface"
	"ainx/utils"
	"bytes"
	"encoding/binary"
	"errors"
)

// 封包拆包实例,暂时不需要成员
type DataPack struct {
}

// 封包拆包实例初始化方法
func NewDataPack() *DataPack {
	return &DataPack{}
}

// 获取包头长度方法
func (dp *DataPack) GetHeadLen() uint32 {
	//Id uint32(4字节) +  DataLen uint32(4字节)
	return 8
}

// 封包方法(压缩)
func (dp *DataPack) Pack(msg ainterface.IMessage) ([]byte, error) {
	// 创建一个存放bytes字节的缓冲
	dataBuff := bytes.NewBuffer([]byte{})
	////写dataLen
	//字节序 就是多字节数据类型 (int, float 等)在内存中的存储顺序。可分为大端序,低地址端存放高位字节;小端序与之相反,低地址端存放低位字节。
	if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetDataLen()); err != nil {
		return nil, err
	}
	//写msgID
	if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetMsgId()); err != nil {
		return nil, err
	}

	//写data数据
	if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetData()); err != nil {
		return nil, err
	}

	return dataBuff.Bytes(), nil
}

// 拆包方法(解压数据)
func (dp *DataPack) Unpack(binaryData []byte) (ainterface.IMessage, error) {
	//创建一个从输入二进制数据的ioReader
	dataBuff := bytes.NewReader(binaryData)

	//只解压head的信息,得到dataLen和msgID
	msg := &Message{}

	//读dataLen
	if err := binary.Read(dataBuff, binary.LittleEndian, &msg.DataLen); err != nil {
		return nil, err
	}

	//读msgID
	if err := binary.Read(dataBuff, binary.LittleEndian, &msg.Id); err != nil {
		return nil, err
	}

	//判断dataLen的长度是否超出我们允许的最大包长度
	if utils.GlobalSetting.MaxPacketSize > 0 && msg.DataLen > utils.GlobalSetting.MaxPacketSize {
		return nil, errors.New("Too large msg data recieved")
	}

	//这里只需要把head的数据拆包出来就可以了,然后再通过head的长度,再从conn读取一次数据
	return msg, nil
}

Ainx-V0.5代码实现

现在我们需要把封包和拆包的功能集成到Zinx中,并且测试Zinx该功能是否生效。

Request字段修改

首先我们要将我们之前的Request中的[]byte类型的data字段改成Message类型.

ainx/anet/request.go

package anet

import "ainx/ainterface"

type Request struct {
	conn ainterface.IConnection //已经和客户端建立好的链接
	msg  ainterface.IMessage    //客户端请求数据
}

// 获取请求链接信息
func (r *Request) GetConnection() ainterface.IConnection {
	return r.conn
}

// 获取请求消息的数据
func (r *Request) GetData() []byte {
	return r.msg.GetData()
}

// 获取请求的消息的ID
func (r *Request) GetMsgID() uint32 {
	return r.msg.GetMsgId()
}

集成拆包过程

接下来我们需要在Connection的StartReader()方法中,修改之前的读取客户端的这段代码:

func (c *Connection) StartReader() {
    
	//...
    
	for  {
		//读取我们最大的数据到buf中
		buf := make([]byte, utils.GlobalSetting.MaxPacketSize)
		_, err := c.Conn.Read(buf)
		if err != nil {
			fmt.Println("recv buf err ", err)
			c.ExitBuffChan <- true
			continue
		}
        
        //...
        
    }
}

改成如下:

ainx/anet/connection.go

StartReader()方法

// 处理conn读数据的Goroutine
func (c *Connection) StartReader() {
	fmt.Println("Reader Goroutine is  running")
	defer fmt.Println(c.RemoteAddr().String(), " conn reader exit!")
	defer c.Stop()

	for {
		// 创建拆包解包的对象
		dp := NewDataPack()

		//读取客户端的Msg head
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error ", err)
			c.ExitBuffChan <- true
			continue
		}

		//拆包,得到msgid 和 datalen 放在msg中
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error ", err)
			c.ExitBuffChan <- true
			continue
		}

		//根据 dataLen 读取 data,放在msg.Data中
		var data []byte
		if msg.GetDataLen() > 0 {
			data = make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data error ", err)
				c.ExitBuffChan <- true
				continue
			}
		}
		msg.SetData(data)

		//得到当前客户端请求的Request数据
		req := Request{
			conn: c,
			msg:  msg, //将之前的buf 改成 msg
		}
		//从路由Routers 中找到注册绑定Conn的对应Handle
		go func(request ainterface.IRequest) {
			//执行注册的路由方法
			c.Router.PreHandle(request)
			c.Router.Handle(request)
			c.Router.PostHandle(request)
		}(&req)
	}
}

提供封包方法

现在我们已经将拆包的功能集成到Ainx中了,但是使用Ainx的时候,如果我们希望给用户返回一个TLV格式的数据,总不能每次都经过这么繁琐的过程,所以我们应该给Ainx提供一个封包的接口,供Ainx发包使用。

ainx/ainterface/iconnection.go

新增SendMsg()方法


type IConnection interface {
	// 启动连接,让当前连接开始工作
	Start()
	// 停止链接,结束当前连接状态
	Stop()
	//从当前连接获取原始的socket TCPConn GetTCPConnection() *net.TCPConn //获取当前连接ID
	GetConnID() uint32       //获取远程客户端地址信息 RemoteAddr() net.Addr
	GetConnection() net.Conn //  (从当前连接获取原始的socket TCPConn)
	//直接将Message数据发送数据给远程的TCP客户端
	SendMsg(msgId uint32, data []byte) error
}

// 定义⼀一个统⼀一处理理链接业务的接⼝口
type HandFunc func(*net.TCPConn, []byte, int) error

ainx/anet/connection.go

SendMsg()方法实现:

// 直接将Message数据发送数据给远程的TCP客户端
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed == true {
		return errors.New("Connection closed when send msg")
	}
	//将data封包,并且发送
	dp := NewDataPack()
	msg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("Pack error msg id = ", msgId)
		return errors.New("Pack error msg ")
	}

	//写回客户端
	if _, err := c.Conn.Write(msg); err != nil {
		fmt.Println("Write msg id ", msgId, " error ")
		c.ExitBuffChan <- true
		return errors.New("conn Write error")
	}

	return nil
}

使用Ainx-V0.5完成应用程序

现在我们可以基于Ainx框架完成发送msg功能的测试用例了。

Server.go

package main

import (
	"ainx/ainterface"
	"ainx/anet"
	"fmt"
)

// ping test 自定义路由
type PingRouter struct {
	anet.BaseRouter
}

// Test Handle
func (this *PingRouter) Handle(request ainterface.IRequest) {
	fmt.Println("Call PingRouter Handle")
	//先读取客户端的数据,再回写ping...ping...ping
	fmt.Println("recv from client : msgId=", request.GetMsgID, ", data=", string(request.GetData()))

	//回写数据
	err := request.GetConnection().SendMsg(1, []byte("ping...ping...ping"))
	if err != nil {
		fmt.Println(err)
	}
}

func main() {
	//创建一个server句柄
	s := anet.NewServer("Ainx V0.5")

	//配置路由
	s.AddRouter(&PingRouter{})

	//开启服务
	s.Serve()
}

Client.go

package main

import (
	"ainx/anet"
	"fmt"
	"io"
	"net"
	"time"
)

/*
模拟客户端
*/
func main() {

	fmt.Println("Client Test ... start")
	//3秒之后发起测试请求,给服务端开启服务的机会
	time.Sleep(3 * time.Second)

	conn, err := net.Dial("tcp", "127.0.0.1:8080")
	if err != nil {
		fmt.Println("client start err, exit!")
		return
	}

	for {
		//发封包message消息
		dp := anet.NewDataPack()
		msg, _ := dp.Pack(anet.NewMsgPackage(0, []byte("Ainx V0.5 Client Test Message")))
		_, err := conn.Write(msg)
		if err != nil {
			fmt.Println("write error err ", err)
			return
		}

		//先读出流中的head部分
		headData := make([]byte, dp.GetHeadLen())
		_, err = io.ReadFull(conn, headData) //ReadFull 会把msg填充满为止
		if err != nil {
			fmt.Println("read head error")
			break
		}
		//将headData字节流 拆包到msg中
		msgHead, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("server unpack err:", err)
			return
		}

		if msgHead.GetDataLen() > 0 {
			//msg 是有data数据的,需要再次读取data数据
			msg := msgHead.(*anet.Message)
			msg.Data = make([]byte, msg.GetDataLen())

			//根据dataLen从io中读取字节流
			_, err := io.ReadFull(conn, msg.Data)
			if err != nil {
				fmt.Println("server unpack data err:", err)
				return
			}

			fmt.Println("==> Recv Msg: ID=", msg.Id, ", len=", msg.DataLen, ", data=", string(msg.Data))
		}

		time.Sleep(1 * time.Second)
	}
}

从零开始学习 Go 1.25 编程 今天是你新生活的第一天,无论年龄、背景如何,改变从这一刻开始。CodeGym帮助成千上万普通人转型为程序员,从零基础到掌握Go语言,完成真实项目,甚至入职心仪岗位。Go语言简洁高效,适合服务器端开发,是云基础设施、微服务和高负载系统的首选。2026年正是入行良机,你的经验反而是优势。别犹豫,迈出第一步,你将收获技能、自信与全新人生。🚀 阅读详情

相关推荐

易灵思Ti60F100 FPGA的HyperRAM实战:从硬件连接到Native接口时序调试

本文详细介绍了易灵思Ti60F100 FPGA内置HyperRAM控制器的实战应用。文章从硬件设计要点(如400MHz+信号完整性、等长匹配)切入,重点解析了如何通过Interface Designer配置高效的Native接口,并分享了其时序详解与调试技巧,为开发者提供了一份从硬件连接到软件调试的深度指南。

weixin_29168153的博客 459

Ainx-V0.2-简单的连接封装与业务绑定

📕作者简介:过去日记,致力于Java、GoLang,Rust等多种编程语言,热爱技术,喜欢游戏的博主。📗本文收录于系列,大家有兴趣的可以看一看📘相关专栏系列、spring教程等,大家有兴趣的可以看一看📙系列,系列、系列正在发展中,喜欢Java,GoLang,Rust,的朋友们可以关注一下哦!📙 本文大部分都是借鉴刘丹冰大佬的zinx框架和文章,更推荐大家去读大佬的,本文只是个人学习的记录。

个人博客,记录了本人学习的各种有趣的技术。 1360

微机原理-基于8086电压报警2.5V仿真系统设计

*单片机设计介绍,微机原理-基于8086电压报警2.5V仿真系统设计。

2201_75940997的博客 1083

【FDE知识库】(1):AI 编程时代开发语言选型分析,云使用go,企业用java,算法python,底层c

AI 编程时代,开发语言选型需综合考量AI适配度、编译速度、跨平台与学习成本。Python以海量语料和即时执行成首选,适用于AI/ML与原型开发;TypeScript凭借类型系统与秒级构建,成为前端及应用层主流;Go以极速编译与统一风格,契合AI代理高效迭代需求。Rust/C++虽性能强,但编译慢、学习成本高,不适于快速试错。综合推荐:优先选Python、TypeScript、Go,兼顾效率与生态,实现最优AI协作体验。

freewebsys的专栏 624

逆向实战:Soul聊天协议加密与Frida绕过检测技术解析

本文详细解析了Soul聊天协议的逆向工程实战,重点介绍了使用Frida绕过检测技术的具体方法。通过IDA和JADX进行静态分析,结合Frida动态调试,揭示了数据加密与解密的核心逻辑,并提供了完整的逆向工作流和实用技巧,帮助开发者深入理解协议逆向过程。

weixin_29260291的博客 927

YOLOv11的QAT(训练感知量化)实战

本文总结了YOLO模型量化感知训练(QAT)的实践要点:QAT通过模拟量化噪声训练模型,比训练后量化(PTQ)更适合密集预测任务。关键实现包括:1)配置对称/非对称量化器;2)排除检测头量化;3)调整学习率策略并添加量化正则;4)选择困难样本校准;5)转换前稳定BN统计量。调试时需检查量化范围、数值分布和逐层突变,并注意SPP等敏感层。建议小模型使用QAT,大模型可能适得其反,且TensorRT的FP16模式可能优于INT8-QAT。量化应以功能正确为前提,避免过度优化。

as1225120794的博客 510

每天10分钟学Go:第5课 switch分支判断

本课程讲解 Go 语言 switch 分支判断核心用法,涵盖单值匹配、多 case 合并、无表达式条件分支、fallthrough 穿透与类型 switch 断言,搭配可运行代码示例,带你掌握 Go 分支结构语法与实战写法。

2501_94567985的博客 259

【无人机路径规划】基于强化学习实现多无人机路径规划

【无人机路径规划】基于强化学习实现多无人机路径规划

k8291121的博客 1659

TinyGo 驱动 ESP32 端侧 AI 推理实战:纯 Go 实现轻量机器学习模型部署全流程

基于乐鑫官方 TinyGo 教程,从零实现纯 Go 语言开发 ESP32 端侧 AI 推理应用,覆盖环境搭建、模型转换到全流程部署。

嵌入式扫地僧 517

Arduino 系列:Arduino Due 系列 (基于 ATSAM3X8E)_(5).电源管理与电压调节

电源管理与电压调节是确保嵌入式系统稳定性和可靠性的关键环节。通过合理选择电源输入方式、使用高效的电压调节器、设计适当的滤波电路和实施动态电源管理,可以有效提高系统的性能和寿命。同时,使用电源调试工具和编写调试代码,可以帮助开发者及时发现和解决问题。希望本文档能为使用 Arduino Due 的开发者提供有用的信息和指导。

2401_87715305的博客 1310

智能办公助手——OpenClaw办公自动化集成(2026办公版)

OpenClaw智能办公助手是一款集成任务管理、文档处理等功能的办公自动化系统。该系统采用Python实现,核心功能包括:1)任务管理系统,支持任务增删改查、状态跟踪和统计报表生成;2)文档智能助手,提供文档处理能力。任务管理模块具备优先级设置、状态变更、时间记录等功能,并能生成包含完成率等指标的任务报告。该系统旨在通过自动化办公流程提升工作效率,减少重复性工作。开发者可利用这些模块构建完整的智能办公解决方案。

一键难忘的博客 351

每天10分钟学Go:第9课 Range循环

在 Go 里,`range` 常用来遍历切片、数组、字符串、映射和通道。它会在每次循环时,把当前元素的信息交给循环变量。前面《》我们已经会写普通的for循环,可以把range理解成一种更方便的遍历方式。

2501_94567985的博客 129

SXU-软件项目管理-朱少民-第九章 项目监控与控制 第十章 项目收尾 附相关习题

软件项目管理-朱少民-第九章 项目监控与控制 附相关习题

weixin_63597914的博客 1489

2026-11-11-Go分布式锁实战

线上偶发重复扣库存,查了半天是业务执行超过了锁的过期时间,锁提前释放,另一个请求拿到锁又执行了一遍。单机锁在多实例部署下失效,Redis SET NX PX 加锁、Lua 脚本原子释放、看门狗续期,这篇讲透分布式锁三大坑,附实现对照表和实战清单。

利威尔的博客,主要分享一些go后端,agent相关知识,都是最新最前沿而且是免费的,在我的专栏里有各种方向的详细分类和内容整理,详细内容请阅读主页置顶文章,希望对你有帮助。 296

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

每天10分钟学Go:第1课 环境搭建与第一个Go程序

本号专注分享实战干货、避坑指南、性能调优、面试重难点, 每一篇都是亲手落地测试,帮你少走弯路、快速提升核心竞争力。5、将go程序构建成可执行的二进制文件:go build hello-world.go。3、编辑go文件写入程序:vim hello-world.go。4、运行go程序:go run hello-world.go。2、创建go文件:touch hello-world.go。1、创建项目目录:mkdir go-test261111。2、配置go环境变量。

2501_94567985的博客 119

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Go 泛型入门:类型参数、约束与 Map/Filter/Stack

掌握类型参数、约束、~ 与 union,写出通用 Map/Filter/Stack,并知道什么时候不该用泛型。

JudithHuang的博客 216

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Go 测试实战:表驱动、Benchmark 与 httptest

从表驱动、子测试到 Benchmark/Fuzz/httptest,建立“测行为不测实现”的测试习惯。

JudithHuang的博客 200

day01-认识Go语言 + golang环境的安装

Go语言由Google开发,2009年发布,兼具编译型性能与脚本语言易用性。其优势包括运行快、语法简洁、跨平台构建、丰富标准库及原生并发支持(goroutine与channel),适用于微服务、区块链等场景。安装时需配置GOROOT(安装路径)、GOPATH(工作空间,含pkg和bin目录)及GOBIN,并将%GOROOT%\bin、%GOBIN%、%GOPATH%加入系统Path。通过命令行验证环境后,可使用GoLand等IDE进行开发,快速上手高效编程。

2301_77190282的博客 230

Go CPU 剖析的“地基“:信号、CAS、写屏障、无锁环形缓冲

本文通过“卡点自查表”梳理Go CPU剖析(cpuprof.go)中反直觉设计的底层逻辑,聚焦信号处理、CAS自旋锁、写屏障与无锁缓冲四大核心机制。指出所有设计皆源于信号处理器“不可阻塞、不碰堆、不持锁”的铁律,进而推导出:CompareAndSwap一次即走、环形缓冲无锁写入、预分配内存等策略的本质是向“在任意指令间被中断”这一事实妥协。结合多核并发与操作系统调度原理,揭示了高性能采样器如何在极端约束下实现低延迟、高吞吐。

不愿言弃-念何的博客 636

Go1.25 升级后 GoLand 调试提示【变量不可用】解决方案

顶部菜单Help→在弹出的配置文件中添加下面一行,路径替换为你本机 dlv.exe 真实路径Windows 路径使用正斜杠,不要用反斜杠保存配置文件,完全重启 GoLand(不是只重启调试会话)Go 大版本升级后,优先检查 Delve 兼容性,这是 Go1.24+、Go1.25 最常见的调试踩坑点。升级dlv + 指定dlv路径 + 关闭编译优化参数,90% 的“变量不可用”问题都可以解决。

关于代码的那些事 235

Go FX 依赖注入框架:从入门到实战

FX 的核心思想就是“声明式依赖管理”—— 你只需要告诉它"我有这些组件,它们需要什么",剩下的创建、排序、生命周期都由框架自动处理。特性传统方式FX 方式依赖顺序手动维护自动解析生命周期手写 start/stopfx.Hook 自动调度测试难度高(需真实依赖)低(接口易 mock)代码可读性分散在各处集中在 module 中错误提示运行时 panic启动时清晰报错对于中大型 Go 项目,FX 能显著降低代码耦合度,提升可测试性和可维护性。

ggaofengg的专栏 173

Go 定时任务实战:工业边缘场景下的 robfig/cron

工业边缘网关里有大量周期性工作:设备状态轮询、数据同步、定时上报、日志清理。,本文从角度梳理其用法、任务封装、并发控制与常见的坑。

ZenovaEdgeOS的博客 456

纯 Go 写的 JS 引擎,跑分超过了 C 语言的 QuickJS

moejs 是一个纯 Go 编写的高性能 JavaScript 运行时,专为 Go 程序中的插件沙箱场景设计。发布于 2026 年 11 月,由 new-api 维护者 Calcium-Ion 开发,相较 C 版 QuickJS 快 35%,内存占用仅 81 KiB,单次调用仅 6.9 微秒。支持快速复用运行时、零拷贝解析、安全沙箱与 TypeScript 直接编译,通过 test262 80971 个用例零失败。适合高并发插件系统,如 AI 网关钩子处理,已在实际场景中实现请求吞吐量提升 8.6 倍。仓库

前端之虎陈随易 253

Tailscale 上手:把几台机器拼成一个局域网

一台每天背来背去的笔记本,一台家里的 Windows 台式,一台常年开着的 Linux 小主机,外加一台云上的 VPS。于是每次想在笔记本上连一次小主机,流程都是:回忆 IP → 发现变了 → 去路由器后台查 → 发现不在同一个网 → 打开某个穿透工具 → 折腾十分钟 → 干正事只要两分钟。Tailscale 解决的就是这一串前置动作。装完之后,上面这些机器在我眼里变成了四个稳定的名字,不管它们实际待在世界哪个网络里。

weixin_42357472的博客 222

运行go web服务,检测mysql服务和端口

如果你的窗口里显示的是别的报错文字(比如 ‘xxx’ 不是内部或外部命令、系统找不到指定的路径、或 Go 编译报错),把那几行原文发我,我再精确修——不过现在这版 bat 已经把最常见的三个坑(数据库、端口、闪退看不到错误)都堵上了。启动 MySQL:用干净的 my.ini 直接起 mysqld.exe(绕开 phpstudy 面板配置),验证连通:oa_admin_rule 554 行、oa_admin 2 行,数据完好。服务本身还能起来,但登录、菜单全依赖数据库,所以你看到的是红色警告 + 登录失效。

xinhuanjieyi的博客 202

每天10分钟学Go:第8课 映射(Map)

本文搭配可运行的完整示例代码,系统讲解 Go 语言 Map 的创建、增删改查、遍历等核心语法,整理了 maps 标准库用法与 8 个高频踩坑点,帮你快速掌握 Go 哈希表的正确使用方式。

2501_94567985的博客 219
上一篇: MongoDB Java实战
下一篇: Ainx的多路由模式
过去日记
过去日记 新星创作者: 后端开发技术领域 新星创作者: 后端开发技术领域
博客等级 码龄3年 5343粉丝 225原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过去日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值