在实际的系统项目工程中中,我们在写代码的时候要尽量避免不必要的耦合,否则你以后在更新和维护代码的时候会发现如同深陷泥潭,随便改点东西整个系统都要变动的酸爽会让你深切后悔自己当初为什么非要把东西都写到一块去(我不会说我刚实习的时候就是这么干的。。。)
所以这一篇主要说说如何设计Sever的内部逻辑,将Server处理Client发送信息的这部分逻辑与Sevrer处理Socket连接的逻辑进行解耦~
这一块的实现灵感主要是在读一个HTTP开源框架:Beego 的源代码的时候产生的,Beego的整个架构就是高度解耦的,这里引用一下作者的介绍:
beego 是基于八大独立的模块构建的,是一个高度解耦的框架。当初设计 beego 的时候就是考虑功能模块化,用户即使不使用 beego 的 HTTP 逻辑,也依旧可以使用这些独立模块,例如:你可以使用 cache 模块来做你的缓存逻辑;使用日志模块来记录你的操作信息;使用 config 模块来解析你各种格式的文件。所以 beego 不仅可以用于 HTTP 类的应用开发,在你的 socket 游戏开发中也是很有用的模块,这也是 beego 为什么受欢迎的一个原因。大家如果玩过乐高的话,应该知道很多高级的东西都是一块一块的积木搭建出来的,而设计 beego 的时候,这些模块就是积木,高级机器人就是 beego。
这是一个典型的MVC框架,可以看到,当用户发送请求到beego后,Beego内部在通过路由进行参数的过滤,然后路由根据用户发来的参数判断调用哪个Controller执行相关的逻辑,并在controller里调用相关的模块实现功能。通过这种方式,Beego成功的将所有模块都独立出来,也就是astaxie所说的“乐高积木化”。
在这里,我们可以仿照Beego的架构,在Server内部加入一层Router,通过Router对通过Socket发来的信息进通过我们设定的规则行的判断后,调用相关的Controller进行任务的分发处理。在这个过程中不仅Controller彼此独立,匹配规则和Controller之间也是相互独立的。
下面给出Router的实现代码,其中Msg的结构对应的是Json字符串,当然考虑到实习公司现在也在用这个,修改了一部分,不过核心思路是一样的哦:
import ( "utils" "fmt" "encoding/json" ) type Msg struct { Conditions map[string]interface{} `json:"Meta"` Content interface{} `json:"content"` } type Controller interface { Excute(message Msg) []byte } var routers [][2]interface{} func Route(judge interface{},controller Controller) { switch judge.(type) { case func(entry Msg)bool:{ var arr [2]interface{} arr[0] = judge arr[1] = controller routers = append(routers,arr) } case map[string]interface{}:{ defaultJudge:= func(entry Msg)bool{ for keyjudge,valjudge := range judge.(map[string]interface{}){ val,ok := entry.Meta[keyjudge] if !ok { return false } if val != valjudge { return false } } return true } var arr [2]interface{} arr[0] = defaultjudge arr[1] = controller routers = append(routers,arr) fmt.Println(routers) } default: fmt.Println("Something is wrong in Router") } }
通过自定义接口Router,我们将匹配规则judge和对应的controller封装了进去,然后在Server端负责接收socket发送信息的函数handleConnection那里再实现Router内部的遍历即可:
for _,v := range routers{ pred := v[0] act := v[1] var message Msg err := json.Unmarshal(postdata,&message) if err != nil { Log(err) } if pred.(func(entry Msg)bool)(message) { result := act.(Controller).Excute(message) conn.Write(result) return } }
这样Client每次发来信息,我们就可以让Router自动跟现有的规则进行匹配,最后调用对应的Controller进行逻辑的实现啦,下面给出一个controller的编写实例,这个Controll的作用是发来的json类型是mirror的时候,将Client发来的信息原样返回:
type MirrorController struct { } func (this *MirrorController) Excute(message Msg)[]byte { mirrormsg,err :=json.Marshal(message) CheckError(err) return mirrormsg } func init() { var mirror routers = make([][2]interface{},20) Route(func(entry Msg)bool{ if entry.Meta["msgtype"]=="mirror"{ return true} return false },&mirror) }