V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  assiadamo  ›  全部回复第 4 页 / 共 28 页
回复总数  560
1  2  3  4  5  6  7  8  9  10 ... 28  
@NessajCN
// 注册协议处理函数
type MsgProcessorFunc[T Msg] func(msg T) error

var MsgProcessor = map[int32]MsgProcessorFunc[Msg]{}

// 注册协议创建函数
var MsgCreator = map[int32]func() Msg{}

// ========= 生成的协议类 ===========
type Echo struct {
// TypeId 应该隐藏在 MsgBase 中 此处简略
TypeId int32
Msg string
}

func NewEcho() *Echo {
return &Echo{
TypeId: 1,
}
}

func (echo *Echo) Process() error {
return MsgProcessor[echo.TypeId](echo)
}

// ========= 业务层 ===========
func ProcessEcho(echo *Echo) error {
fmt.Println(echo.Msg)
return nil
}

// 调用例子
func main() {
// 想干掉的手动注册 如果不行只能用代码生成
MsgCreator[1] = func() Msg { return NewEcho() }
MsgProcessor[1] = func(msg Msg) error { return ProcessEcho(msg.(*Echo)) }

// 模拟协议发送
msg := &Echo{TypeId: 1, Msg: "test"}

// 省掉了编解码和 socket 操作

msg.Process()
}

这是可执行的代码示例
做了很多工作就是为了干掉那两句注册
@leonshaw 对的,如果有注解就不会太纠结
@NessajCN 业务逻辑写在框架外面,这里的框架是通信框架,业务逻辑调用的入口肯定是框架吧。
典型的长链接服务器处理流程:
1. 绑定端口等待链接
2. 从链接获取数据,解析成协议
3. 从协议号获取对应的业务逻辑处理函数,传入协议体
4. 若需要返回结果,也要包装成协议,编码成字节属于,通过链接写回

我认为 go 的设计哲学突出了一个简单,让基于网络层的服务器程序都非常容易实现,所以当然能一把梭全写在一起。但 java 的设计逻辑很不一样,看中抽象复用等很软工的东西,我受毒害很深。
说到软工,分层设计是很有用的思路,上述步骤中 12 应该都是通信层做的事情,协议作为通信层和业务层的桥梁,虽然位置和业务层在一起,但不应该有任何编码行为,比如 protobuf 生成的协议类,注释就有 DO NOT EDIT IT 。
问题也在这里,protobuf 不能自解释,一段数据来了不知道他是什么协议,需要再包一层加上协议号或其他数据,再结合 go 自己的一些特性,比如参数是接口的函数,不接受接口的实现类做入参,ChatGPT 说 Go 不支持协变,我都不知道有这种词,让单纯的写业务逻辑变的艰难,我见过一些框架,直接传入业务层 byte 数组,在业务层做协议编解码,我忍不了这个,所以才折腾这一出。
117 天前
回复了 importmeta 创建的主题 程序员 后端程序员更喜欢哪个前端框架?
jquery
@NessajCN 兜兜转转还是用了上面说的生成个 handles.go 的方法,目前能跑通
```
package proto
type Echo struct {
BaseMsg
Msg string
}
func (msg *Echo) Decode(src *bytes.Buffer) error {}
func (msg *Echo) Encode(dst *bytes.Buffer) error {}
func (echo *Echo) Process() error {
return MsgProcessor[echo.GetHeader().TypeId](echo)
}
```
外部代码生成个放所有业务逻辑入口的 map
```
type MsgProcessorFunc[T Msg] func(msg T) error

var MsgProcessor = map[int32]MsgProcessorFunc[Msg]{}
MsgProcessor[1] = func(msg io.Msg) error { return echo.ProcessEcho(msg.(*proto.Echo)) }
```
在 echo.ProcessEcho 中写实际业务,协议和业务分开
痛苦
@NessajCN 这样和我的需求反过来了...变成了协议的 package 里写业务逻辑,业务的 package 生成后不动了....
@povsister 因为以前用过这样的框架,觉得用起来非常爽,现在也算是体验到了框架开发者的心情
@NessajCN 我理解一下,如果有代码例子就更好了
@povsister 我预想的使用方式是开发者定义好协议,这个协议可能带 package 信息,然后 go generate ,所有的模板都生成好了,开发者只需要打开一个生成的 go 文件写业务代码就行
@povsister 我理解 register 需要开发者自己做,就兴趣缺缺
@NessajCN 不是一个
Echo 协议类生成的时候,会伴随着在业务项目中生成一个
```
func Process 协议名(协议){}
```
这样其实当协议收取的时候,框架就应该知道对应的处理函数是什么,信息完全是足够的,但需要一个方法让协议内部的 Process 方法和业务对应协议 Prosess 方法联动起来,我想这一步让框架自己处理,而不是手动的去配置联动关系
@NessajCN Java 有注解和字节码替换,可以批量的处理一系列相同定义的东西,但 Go 如果没有相关的魔法,就要手动的注册函数,比如经典的 HandleFunc(path, func(){}),这样在玩具中,或者接口比较少的项目中可以手动,但如果有几百上千种协议定义呢?
当然可以借用代码生成,比如再生成一个 handle.go ,我想省去这个文件
@to2false Go 的 interface 不能放成员变量就很可惜,比如协议内数据定义,Decode/Encode 相关的代码都不想出现在业务层,要想用 interface 就要再搞个 BaseEcho 之类的组合,还是觉得有点丑
@NessajCN 这就是想要做到的魔法
@NessajCN 框架想做到不接触业务层,直接调用协议的`msg.Process()`就能执行业务逻辑,否则还要手动将业务层的各种协议处理函数注册一下
@zjsxwc 有点天才,可以一试
@NessajCN 因为不要去 proto 包下生成的协议类中写业务逻辑,想把两边的编码隔离开
一张表存无压力,过几年再看
自己的小东西,服务器配置又小,可以试试 go ,相同的业务可以省很多内存
@aa514758835 这个比电还贵
1  2  3  4  5  6  7  8  9  10 ... 28  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1002 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 21ms · UTC 20:21 · PVG 04:21 · LAX 13:21 · JFK 16:21
Developed with CodeLauncher
♥ Do have faith in what you're doing.