Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Decoupling of Socket Server Module written by Go and Design example Analysis of basic Module

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

Today, I will talk to you about the decoupling of the Socket server module written by Go and the design example analysis of the basic module. Many people may not know much about it. In order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.

Decoupling of Server-logical Distribution through Router+Controller

In the actual system project project, we should try our best to avoid unnecessary coupling when we write the code, otherwise you will find yourself in a quagmire when you update and maintain the code in the future. the acidity of changing something casually and changing the whole system will make you deeply regret why you had to write everything together in the first place (I wouldn't say that's what I did when I started my internship. No, no, no. )

So this article mainly talks about how to design the internal logic of Sever, decoupling this part of the logic of Server dealing with Client sending information from the logic of Sevrer dealing with Socket connection.

The inspiration for the implementation of this piece is mainly generated when reading the source code of a HTTP open source framework: Beego. The whole architecture of Beego is highly decoupled. Here is the author's introduction:

Beego is built on eight independent modules and is a highly decoupled framework. The original design of beego is to consider functional modularization, even if users do not use beego HTTP logic, you can still use these independent modules, for example: you can use cache module to do your cache logic; use log module to record your operation information; use config module to parse your files in various formats. So beego can not only be used for HTTP application development, but also a useful module in your socket game development, which is one of the reasons why beego is so popular. If you have ever played with Lego, you should know that many advanced things are built out of building blocks, and when designing beego, these modules are building blocks, and advanced robots are beego.

Here is a diagram of the architecture of Beego:

This is a typical MVC framework. We can see that when the user sends the request to the beego, the Beego is filtering the parameters through routing, and then the route determines which Controller is called to execute the relevant logic according to the parameters sent by the user, and calls the relevant modules in the controller to achieve the function. In this way, Beego successfully separated all the modules, what astaxie called "Lego building".

Here, we can imitate the structure of Beego, add a layer of Router inside the Server, through Router to judge the information sent through Socket through the rule line we set, and then call the relevant Controller to distribute the task. In this process, not only Controller is independent of each other, but also matching rules and Controller are independent of each other.

The following is the implementation code of Router. The structure of Msg corresponds to the Json string. Of course, considering that the internship company is also using this, some of it has been modified, but the core idea is the same:

The copy code is as follows:

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")

}

}

Through the custom interface Router, we encapsulate the matching rule judge and the corresponding controller, and then traverse the Router in the function handleConnection that is responsible for receiving the information sent by the Router on the server side:

The copy code is as follows:

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

}

}

In this way, every time a message is sent from Client, we can let Router automatically match the existing rules, and finally call the corresponding Controller for logical implementation. Here is a writing example of controller. The function of this Controll is to return the information sent by Client as is when the sent json type is mirror:

The copy code is as follows:

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)

}

Design of log module and timing task module

As a Server, Log function is essential, a well-designed logging module, whether debugging when developing Server, or runtime maintenance, is very helpful.

Because what is written here is a relatively simplified Server framework, I chose to extend the log library of Golang itself to implement a simple Log module.

Here, I roughly divide the log level into three levels: Debug,Operating,Error. Debug is mainly used to store log information during debugging, Operateing is used to save the information generated during the daily operation of Server, and Error is used to save error information.

The module code is as follows:

The copy code is as follows:

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 ())

}

}

Note that I use stdout for the output of log here, because this redirects the log directly to the specified location when Server is running, which facilitates the deployment of the entire Server. However, in daily development, in order to debug the code, I recommend that you output log to the specified file location, which will be much more convenient when debugging (mainly because golang debugging is too troublesome, and you often have to step in when you hit log. The code of the Log module for debugging is illustrated as follows:

The copy code is as follows:

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 ()

}

Then there is the timing cycle module. In daily operation, Server often has to perform some scheduled tasks, such as refreshing the background at regular intervals, automatically refreshing the crawler at regular intervals, and so on. Here, I design a Task interface, which registers and executes all scheduled tasks in a way similar to TaskList. The code is as follows:

The copy code is as follows:

Type DoTask interface {

Excute ()

}

Var tasklist [] interface {}

Func AddTask (controller DoTask) {

Var arr interface {}

Arr = controller

Tasklist = append (tasklist,arr)

Fmt.Println (tasklist)

}

Here, take a timing task as an example:

The copy code is as follows:

Type Task1 struct {}

Func (this * Task1) Excute () {

Timer: = time.NewTicker (2 * time.Second)

For {

Select {

Case

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report