Analyze the decoupling of the socket server module and the design of the basic module of Go written by _golang

Source: Internet
Author: User
Tags json

Server decoupling-logical distribution via Router+controller implementation

In the actual system project engineering, we should try to avoid unnecessary coupling when we write code, otherwise you will find that you are in deep mire when you update and maintain the code. Anything a pity dorado the whole system will have to change the acid will make you deeply regret the original why you have to write things to go (I will not say that I just did when the internship is so dry ...) )
So this article is mainly about how to design the internal logic of sever, the server processing client to send the information of this part of the logic and sevrer processing socket connection logic to decouple ~
This piece of implementation inspiration is mainly in reading an HTTP open source framework: Beego source code generation, Beego's entire architecture is highly decoupled, here reference to the author's introduction:
Beego is a highly decoupled framework built on eight independent modules. The original design Beego is to consider the function of modularity, even if the user does not use Beego HTTP logic, you can still use these stand-alone modules, such as: You can use the cache module to do your caching logic; Use the log module to record your operation information; use Config Module to parse your files in various formats. So Beego not only can be used for HTTP class application development, in your socket game development is also a useful module, which is why beego popular reason. If you play Lego, you should know that a lot of advanced things are a piece of building blocks, and the design Beego, these modules are building blocks, advanced robot is beego.
Here's an Beego diagram:

This is a typical MVC framework, as you can see, when the user sends the request to Beego, the Beego is filtered through the routing parameters, and then the route is based on the parameters the user sends to determine which controller to execute the relevant logic, and the relevant module implementation function is called in the controller. In this way, Beego succeeded in all the modules are independent, that is what Astaxie said "Lego Lego."
Here, we can follow the architecture of Beego, add a layer of router inside the server, and call the relevant controller to distribute the task by router the information sent through the socket into the judgment of the rule line we set. In this process not only controller each other, the matching rules and controller are independent of each other.
Here is the implementation code for the router, where the structure of MSG corresponds to the JSON string, which, of course, takes into account that the internship company is now using this to modify a part, but the core idea is the same oh:

Copy Code code 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")
}
}

By customizing the interface router, we encapsulate the matching rules judge and corresponding controller, and then implement the router internal traversal in the function handleconnection where the server is responsible for receiving the socket sending information:
Copy Code code 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
}
}

So every time the client sent information, we can let router automatically match the existing rules, and finally call the corresponding controller to implement the logic, the following gives a controller of the written example, The effect of this controll is that when the JSON type is mirror, the client-sent message is returned as it is:
Copy Code code as follows:

Type Mirrorcontroller struct {

}

Func (this *mirrorcontroller) Excute (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 module of Timing task module
As a server, log function is essential, a well-designed log module, whether it is the development of server debugging, or run-time maintenance, are very helpful.
Because this is a simpler server framework, I chose to extend the log library of Golang itself to implement a simple log module.
Here, I will be the level of the log roughly divided into Debug,operating,error 3 levels, debug is mainly used to store the debug phase of the log information, operateing used to save the server during the day-to-day operation of the information generated, error is to save the error information.
The module code is as follows:
Copy Code code 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 the log output here I am using stdout, because it will be able to redirect the log directly to the specified location when the server is running, facilitating the deployment of the entire server. However, in the daily development, in order to facilitate debugging code, I recommend the log output to the specified file location, so the debugging time will be more convenient (mainly because Golang debugging is too troublesome, many times to rely on the log when the step forward.) Easy to debug Log module code schematic:
Copy Code code 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 is the timer cycle module, the day-to-day operation, the server often to perform a number of scheduled tasks, such as a certain time to refresh the background, a period of time to automatically refresh the crawler, and so on, where I designed a task interface, through similar to the tasklist of all scheduled tasks registered after the unified implementation, The code is as follows:
Copy Code code 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 is an example of a timed timekeeping task:
Copy Code code as follows:

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 (&AMP;TASK1)
For _, V: = Range Tasklist {
V. (DOTASK). Excute ()
}

}

Note that the timing task here is to be non-blocking, otherwise the entire server will be stuck in the first task of tasklist ...

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.