Go语言简单源代码剖析
来源:互联网 发布:淘宝卖家修改评价链接 编辑:程序博客网 时间:2024/06/07 10:14
https://github.com/beego/samples/tree/master/WebIM
项目在github上面的地址
是beego提供的一个应用小项目,只是对本人第一次剖析go语言的一个小记录,也算自己剖析代码的思路记录,希望对他人也能有所帮助
(1)阅读readme文档,知晓项目大致内容,项目用到的各种配置文件
(2)查看main包文件WebIM.go
import ( "github.com/astaxie/beego" "github.com/beego/i18n" "samples/WebIM/controllers")const ( APP_VER = "0.1.1.0227")func main() { beego.Info(beego.BConfig.AppName, APP_VER) // Register routers. beego.Router("/", &controllers.AppController{}) // Indicate AppController.Join method to handle POST requests. beego.Router("/join", &controllers.AppController{}, "post:Join") // Long polling. beego.Router("/lp", &controllers.LongPollingController{}, "get:Join") beego.Router("/lp/post", &controllers.LongPollingController{}) beego.Router("/lp/fetch", &controllers.LongPollingController{}, "get:Fetch") // WebSocket. beego.Router("/ws", &controllers.WebSocketController{}) beego.Router("/ws/join", &controllers.WebSocketController{}, "get:Join") // Register template functions. beego.AddFuncMap("i18n", i18n.Tr) beego.Run()}
main文件包含了三个引用包,一般main文件里的函数都被封装的很好,但是先看main文件,就可以知晓程序运行顺序,再一点一点的分析每个函数的作用
(2)
beego.Router()函数:注册路由,Router 函数的两个参数函数,第一个是路径,第二个是 Controller 的指针。路由就是告诉 beego,当用户来请求的时候,该如何去调用相应的 Controller。
PS:由于我主要是想搞明白聊天室的工作原理,所以对于beego工具的运作方法暂且不细细去看,重点是controllers
(3)
app.go
先看开发文档了解功能:供用户选择技术和用户名的欢迎页面
package controllersimport ( "strings" "github.com/astaxie/beego" "github.com/beego/i18n")var langTypes []string //支持的语言,从配置文件获取func init() { // Initialize language type list. langTypes = strings.Split(beego.AppConfig.String("lang_types"), "|") // Load locale files according to language types. for _, lang := range langTypes { beego.Trace("Loading language: " + lang) if err := i18n.SetMessage(lang, "conf/"+"locale_"+lang+".ini"); err != nil { beego.Error("Fail to set message file:", err) return } }}// baseController represents base router for all other app routers.//基本控制器为所有其他的app路由提供了基本// It implemented some methods for the same implementation;// thus, it will be embedded into other routers.type baseController struct { beego.Controller // Embed struct that has stub implementation of the interface. i18n.Locale // For i18n usage when process data and render template.}// Prepare implemented Prepare() method for baseController.// It's used for language option check and setting.func (this *baseController) Prepare() { // Reset language option. this.Lang = "" // This field is from i18n.Locale. // 1. Get language information from 'Accept-Language'. al := this.Ctx.Request.Header.Get("Accept-Language") if len(al) > 4 { al = al[:5] // Only compare first 5 letters. if i18n.IsExist(al) { this.Lang = al } } // 2. Default language is English. if len(this.Lang) == 0 { this.Lang = "en-US" } // Set template level language option. this.Data["Lang"] = this.Lang}// AppController handles the welcome screen that allows user to pick a technology and username.type AppController struct { baseController // Embed to use methods that are implemented in baseController.}// Get implemented Get() method for AppController.func (this *AppController) Get() { this.TplName = "welcome.html"}//用于解决post请求func (this *AppController) Join() { // Get form value. uname := this.GetString("uname") tech := this.GetString("tech") // Check valid. if len(uname) == 0 { this.Redirect("/", 302) return } switch tech { case "longpolling": this.Redirect("/lp?uname="+uname, 302) case "websocket": this.Redirect("/ws?uname="+uname, 302) default: this.Redirect("/", 302) } // Usually put return after redirect. return}
chatroom.go:数据管理相关的函数
package controllersimport ( "container/list" "time" "github.com/astaxie/beego" "github.com/gorilla/websocket" "samples/WebIM/models")type Subscription struct { Archive []models.Event // All the events from the archive. New <-chan models.Event // New events coming in.}func newEvent(ep models.EventType, user, msg string) models.Event { return models.Event{ep, user, int(time.Now().Unix()), msg}}func Join(user string, ws *websocket.Conn) { subscribe <- Subscriber{Name: user, Conn: ws} //上线,记录用户名和连接}func Leave(user string) { unsubscribe <- user //下线}type Subscriber struct { Name string Conn *websocket.Conn // Only for WebSocket users; otherwise nil.}var ( // Channel for new join users. subscribe = make(chan Subscriber, 10) // Channel for exit users. unsubscribe = make(chan string, 10) // Send events here to publish them. publish = make(chan models.Event, 10) // Long polling waiting list. waitingList = list.New() subscribers = list.New())// This function handles all incoming chan messages.func chatroom() { for { select { case sub := <-subscribe: if !isUserExist(subscribers, sub.Name) { subscribers.PushBack(sub) // Add user to the end of list. // Publish a JOIN event. publish <- newEvent(models.EVENT_JOIN, sub.Name, "") beego.Info("New user:", sub.Name, ";WebSocket:", sub.Conn != nil) } else { beego.Info("Old user:", sub.Name, ";WebSocket:", sub.Conn != nil) } case event := <-publish: // Notify waiting list. for ch := waitingList.Back(); ch != nil; ch = ch.Prev() { ch.Value.(chan bool) <- true waitingList.Remove(ch) } broadcastWebSocket(event) models.NewArchive(event) if event.Type == models.EVENT_MESSAGE { beego.Info("Message from", event.User, ";Content:", event.Content) } case unsub := <-unsubscribe: for sub := subscribers.Front(); sub != nil; sub = sub.Next() { if sub.Value.(Subscriber).Name == unsub { subscribers.Remove(sub) // Clone connection. ws := sub.Value.(Subscriber).Conn if ws != nil { ws.Close() beego.Error("WebSocket closed:", unsub) } publish <- newEvent(models.EVENT_LEAVE, unsub, "") // Publish a LEAVE event. break } } } }}func init() { go chatroom()}func isUserExist(subscribers *list.List, user string) bool { for sub := subscribers.Front(); sub != nil; sub = sub.Next() { if sub.Value.(Subscriber).Name == user { return true } } return false}
longpolling.go:长轮询模式的控制器和方法
package controllersimport ( "samples/WebIM/models")// LongPollingController handles long polling requests.type LongPollingController struct { baseController}// Join method handles GET requests for LongPollingController.func (this *LongPollingController) Join() { // Safe check. uname := this.GetString("uname") if len(uname) == 0 { this.Redirect("/", 302) return } // Join chat room. Join(uname, nil) this.TplName = "longpolling.html" this.Data["IsLongPolling"] = true this.Data["UserName"] = uname}// Post method handles receive messages requests for LongPollingController.func (this *LongPollingController) Post() { this.TplName = "longpolling.html" uname := this.GetString("uname") content := this.GetString("content") if len(uname) == 0 || len(content) == 0 { return } publish <- newEvent(models.EVENT_MESSAGE, uname, content)}// Fetch method handles fetch archives requests for LongPollingController.func (this *LongPollingController) Fetch() { lastReceived, err := this.GetInt("lastReceived") if err != nil { return } events := models.GetEvents(int(lastReceived)) if len(events) > 0 { this.Data["json"] = events this.ServeJSON() return } // Wait for new message(s). ch := make(chan bool) waitingList.PushBack(ch) <-ch this.Data["json"] = models.GetEvents(int(lastReceived)) this.ServeJSON()}
websocket.go:数据管理相关的函数
package controllersimport ( "encoding/json" "net/http" "github.com/astaxie/beego" "github.com/gorilla/websocket" "samples/WebIM/models")// WebSocketController handles WebSocket requests.type WebSocketController struct { baseController}// Get method handles GET requests for WebSocketController.func (this *WebSocketController) Get() { // Safe check. uname := this.GetString("uname") if len(uname) == 0 { this.Redirect("/", 302) return } this.TplName = "websocket.html" this.Data["IsWebSocket"] = true this.Data["UserName"] = uname}// Join method handles WebSocket requests for WebSocketController.func (this *WebSocketController) Join() { uname := this.GetString("uname") if len(uname) == 0 { this.Redirect("/", 302) return } // Upgrade from http request to WebSocket. ws, err := websocket.Upgrade(this.Ctx.ResponseWriter, this.Ctx.Request, nil, 1024, 1024) if _, ok := err.(websocket.HandshakeError); ok { http.Error(this.Ctx.ResponseWriter, "Not a websocket handshake", 400) return } else if err != nil { beego.Error("Cannot setup WebSocket connection:", err) return } // Join chat room. Join(uname, ws) defer Leave(uname) // Message receive loop. for { _, p, err := ws.ReadMessage() if err != nil { return } publish <- newEvent(models.EVENT_MESSAGE, uname, string(p)) }}// broadcastWebSocket broadcasts messages to WebSocket users.func broadcastWebSocket(event models.Event) { data, err := json.Marshal(event) if err != nil { beego.Error("Fail to marshal event:", err) return } for sub := subscribers.Front(); sub != nil; sub = sub.Next() { // Immediately send event to WebSocket users. ws := sub.Value.(Subscriber).Conn if ws != nil { if ws.WriteMessage(websocket.TextMessage, data) != nil { // User disconnected. unsubscribe <- sub.Value.(Subscriber).Name } } }}
archive.go:操作数据相关的函数
package modelsimport ( "container/list")type EventType intconst ( EVENT_JOIN = iota EVENT_LEAVE EVENT_MESSAGE)type Event struct { Type EventType // JOIN, LEAVE, MESSAGE User string Timestamp int // Unix timestamp (secs) Content string}const archiveSize = 20// Event archives.var archive = list.New()// NewArchive saves new event to archive list.func NewArchive(event Event) { if archive.Len() >= archiveSize { archive.Remove(archive.Front()) } archive.PushBack(event)}// GetEvents returns all events after lastReceived.func GetEvents(lastReceived int) []Event { events := make([]Event, 0, archive.Len()) for event := archive.Front(); event != nil; event = event.Next() { e := event.Value.(Event) if e.Timestamp > int(lastReceived) { events = append(events, e) } } return events}
看完代码的感觉是好的代码真的是太好了,就完全不需要我去注释了…很简单明了,这也算是熟悉一下简单的项目了
阅读全文
0 0
- Go语言简单源代码剖析
- 阅读go语言源代码1
- 阅读go语言源代码2
- 阅读go语言源代码3
- GO语言简单语法
- Go语言 Go操作mysql简单示例
- Google的Go编程语言源代码(Windows)
- Ultraedit中编辑go语言源代码
- Go语言 线程简单读写
- Go语言Mysql简单使用.
- Go语言web简单配置
- GO 语言 http 服务端(简单实例)
- go 语言 连接mysql(简单实例)
- Go语言 简单的的socket文件传输
- Go语言简单的TCP编程
- go语言编写,简单聊天室程序 1
- Go语言 简单的http服务器示例
- Go语言 简单的爬虫示例(1)
- vue入门
- 如何给webApp应用添加动态水印?并且不影响页面的交互事件
- securecrt 远程映射端口
- 高准确率声学模型基于决策树的状态聚类
- JavaScript中对Object.defineProperty 函数 的理解
- Go语言简单源代码剖析
- Android 23以下 checkSelfPermission 失败记录
- MPLAB X LAB初学使用
- Ubuntu安装PCL(Point Cloud Library)
- QT5中使用Mysql碰到的问题
- Dedecms织梦模板下载:百变配色企业通用织梦模板
- 第一篇博客
- HttpMessageConverter
- 欢迎使用CSDN-markdown编辑器