来源:自学PHP网 时间:2019-08-07 16:47 作者:小飞侠 阅读:次
[导读] 剖析Go编写的Socket服务器模块解耦及基础模块的设计...
Server的解耦―通过Router+Controller实现逻辑分发 在实际的系统项目工程中中,我们在写代码的时候要尽量避免不必要的耦合,否则你以后在更新和维护代码的时候会发现如同深陷泥潭,随便改点东西整个系统都要变动的酸爽会让你深切后悔自己当初为什么非要把东西都写到一块去(我不会说我刚实习的时候就是这么干的。。。) 这是一个典型的MVC框架,可以看到,当用户发送请求到beego后,Beego内部在通过路由进行参数的过滤,然后路由根据用户发来的参数判断调用哪个Controller执行相关的逻辑,并在controller里调用相关的模块实现功能。通过这种方式,Beego成功的将所有模块都独立出来,也就是astaxie所说的“乐高积木化”。
复制代码 代码如下:
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{} ,0 , 20) Route(func(entry Msg)bool{ if entry.Meta["msgtype"]=="mirror"{ return true} return false },&mirror) } 日志模块的设计与定时任务模块模块 作为一个Server,日志(Log)功能是必不可少的,一个设计良好的日志模块,不论是开发Server时的调试,还是运行时候的维护,都是非常有帮助的。 因为这里写的是一个比较简化的Server框架,因此我选择对Golang本身的log库进行扩充,从而实现一个简单的Log模块。 在这里,我将日志的等级大致分为Debug,Operating,Error 3个等级,Debug主要用于存放调试阶段的日志信息,Operateing用于保存Server日常运行时产生的信息,Error则是保存报错信息。 模块代码如下:
复制代码 代码如下:
func LogErr(v ...interface{}) { logfile := os.Stdout log.Println(v...) logger := log.New(logfile,"\r\n",log.Llongfile|log.Ldate|log.Ltime); logger.SetPrefix("[Error]") logger.Println(v...) defer logfile.Close(); } func Log(v ...interface{}) { logfile := os.Stdout log.Println(v...) logger := log.New(logfile,"\r\n",log.Ldate|log.Ltime); logger.SetPrefix("[Info]") logger.Println(v...) defer logfile.Close(); } func LogDebug(v ...interface{}) { logfile := os.Stdout log.Println(v...) logger := log.New(logfile,"\r\n",log.Ldate|log.Ltime); logger.SetPrefix("[Debug]") logger.Println(v...) defer logfile.Close(); } func CheckError(err error) { if err != nil { LogErr(os.Stderr, "Fatal error: %s", err.Error()) } } 注意这里log的输出我使用的是stdout,因为这样在Server运行的时候可以直接将log重定向到指定的位置,方便整个Server的部署。不过在日常开发的时候,为了方便调试代码,我推荐将log输出到指定文件位置下,这样在调试的时候会方便很多(主要是因为golang的调试实在太麻烦,很多时候都要依靠打log的时候进行步进。便于调试的Log模块代码示意:
复制代码 代码如下:
func Log(v ...interface{}) { logfile := os.OpenFile("server.log",os.O_RDWR|os.O_APPEND|os.O_CREATE,0); if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) return } log.Println(v...) logger := log.New(logfile,"\r\n",log.Ldate|log.Ltime); logger.SetPrefix("[Info]") logger.Println(v...) defer logfile.Close(); } 然后就是计时循环模块啦,日常运行中,Server经常要执行一些定时任务,比如隔一定时间刷新后台,隔一段时间自动刷新爬虫等等,在这里我设计了一个Task接口,通过类似于TaskList的的方式将所有定时任务注册后统一执行,代码如下:
复制代码 代码如下:
type DoTask interface { Excute() } var tasklist []interface{} func AddTask(controller DoTask) { var arr interface{} arr = controller tasklist = append(tasklist,arr) fmt.Println(tasklist) } 在这里以一个定时报时任务作为例子:
复制代码 代码如下:
type Task1 struct {} func (this * Task1)Excute() { timer := time.NewTicker(2 * time.Second) for { select { case <-timer.C: go func() { Log(time.Now()) }() } } } func init() { var task1 Task1 tasklist = make([]interface{} ,0 , 20) AddTask(&task1) for _, v := range tasklist { v.(DoTask).Excute() } } 注意这里的定时任务要做成非阻塞的,否则整个Server都会卡在tasklist的第一个task的。。。
|
自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习
京ICP备14009008号-1@版权所有www.zixuephp.com
网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com