go语言从零学起(三) -- chat实现的思考
要通过go实现一个应用场景:
1 建立一个websocket服务
2 维护在线用户的链接
3 推送消息和接受用户的操作
列出需求,很显然的想到了chat模型。于是研究了revel框架提供的samples/chat代码,以及基于gorilla/websocket实现的chat。
他们实现的思路比较类似,大概代码如下:
package main import (
"github.com/go-martini/martini"
"github.com/gorilla/websocket"
"net/http"
"time"
) const (
readBufferSize =
writeBufferSize =
) type Client struct {
conn *websocket.Conn
} // 常住监听一个链接的读
func (c *Client) readPump() { for { mstype, message, err := c.conn.ReadMessage() // ....... 该处处理接受用户发来的数据
}
}
//监听一个链接的写
func (c *Client) writePump() { for {
c.conn.SetWriteDeadline(time.Now().Add( * time.Second)) // ....... 该处处理推送给用户的数据
}
} // 基于martini实现的websocket服务
func main() {
m := martini.Classic()
m.Get("/ws", func(res http.ResponseWriter, req *http.Request) {
conn, err := websocket.Upgrade(res, req, nil, readBufferSize, writeBufferSize)
if err != nil {
log.Println(err)
return
}
client := &Client{conn: conn} go client.writePump() //建立一个goroutine client.readPump() })
m.Run()
}
上述代码只是用来简述运用go语言实现chat的大概思路,在最后会贴出原代码的出处。
通过看上面代码会发现,每个链接进来就需要2个goroutine,去维护它的读跟写。如果要是1万个链接,相应的就要有2万个常驻而不会释放的goroutine去监听。这样的话,到最后会不会只是维护链接就会耗尽服务器的cpu跟内存。因为只是接触go的时间不长,看完代码我有些质疑这样实现的效率问题。为了佐证我的想法,我查了下c基于epoll对链接读写的维护,代码如下:
struct epoll_event ev, *events;
for(;;) {
nfds = epoll_wait(kdpfd, events, maxevents, -1); //等待I/O事件
for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) { //如果是主socket的事件,则表示有新连接进入,需要进行新连接的处理。
client = accept(listener, (struct sockaddr *) &local, &addrlen);
if(client < 0){
perror("accept error");
continue;
}
setnonblocking(client); // 将新连接置于非阻塞模式
ev.events = EPOLLIN | EPOLLET;
//注意这里的参数EPOLLIN | EPOLLET并没有设置对写socket的监听,
//如果有写操作的话,这个时候epoll是不会返回事件的,
//如果要对写操作也监听的话,应该是EPOLLIN | EPOLLOUT | EPOLLET。
ev.data.fd = client; // 并且将新连接也加入EPOLL的监听队列
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) { // 设置好event之后,将这个新的event通过epoll_ctl
//加入到epoll的监听队列里,这里用EPOLL_CTL_ADD
//来加一个新的 epoll事件。可以通过EPOLL_CTL_DEL来减少
//一个epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。
fprintf(stderr, "epoll set insertion error: fd=%d"0, client);
return -1;
}
} else // 如果不是主socket的事件的话,则代表这是一个用户的socket的事件,
// 则用来处理这个用户的socket的事情是,比如说read(fd,xxx)之类,或者一些其他的处理。
do_use_fd(events[n].data.fd);
}
}
从代码层面对比来看,同是1万个链接,c基于epoll的实现只有一个常驻的线程去监听I/O事件,而go需要2万个goroutine去监听。这2种语言实现方式间会不会存在比较明显的性能差距,这是我开始学习的时候,一直存在的疑问。
不过看了这篇文章时,解除了我很多疑惑,也让我对go的goroutine有了深入一点的认识。
https://tiancaiamao.gitbooks.io/go-internals/content/zh/05.2.html (goroutine的生老病死)
1 goroutine切换时的上下文信息是保存在结构体的sched域中的。goroutine是轻量级的“线程”或者称为协程,切换时变不必陷入到操作系统内核中,所以保存过程很轻量
2 goroutine创建完成完后,会放入相应的队列
3 go会建多个worker进程,worker做的事情就是不停的去任务队列中取一个任务出来执行
4 goroutine准备好的时候,会获取cpu去执行,执行完了会让出cpu然后挂起
模式大概是这样的:
func M() {
for {
sched.lock.Lock() //互斥地从就绪G队列中取一个g出来运行
if sched.allg > 0 {
g := sched.allg[0]
sched.allg = sched.allg[1:]
sched.lock.Unlock()
g.Run() //运行它
} else {
sched.lock.Unlock()
}
}
}
for i:=0; i<GOMAXPROCS; i++ {
go M()
}
上面这些是刚开始学习的一些疑惑和感悟,希望之后深入学习后,对go有更全面的认识。
参考资料:
1 https://tiancaiamao.gitbooks.io/go-internals/content/zh/05.2.html
2 https://tiancaiamao.gitbooks.io/go-internals/content/zh/05.3.html
3 http://leejia.blog.51cto.com/4356849/1021066
revel的chat代码
https://github.com/revel/samples
gorilla/websocket的chat代码
https://github.com/gorilla/websocket/tree/master/examples
go语言从零学起(三) -- chat实现的思考的更多相关文章
- go语言从零学起(一) -- 文档教程篇
先记录一下自己学go语言的出发点 作为一个phper,精通一门底层语言一直是努力的目标. 相对于c,c++,go语言不需要过多的关注指针,内存释放,一两行代码就能跑起一个server服务,简直不要太简 ...
- go语言从零学起(四) -- 基于martini和gorilla实现的websocket聊天实例
如果只是想了解chat的实现方式,在gorilla和revel框架里面都有完整的chat实例可以提供参考.本篇讲解的是,如何基于martini实现websocket的聊天. 配置步骤: 1 已经安装了 ...
- go语言从零学起(二)--list循环删除元素(转载)
本篇系转载 在使用go的container/list的package时,你可能会无意间踩一个小坑,那就是list的循环删除元素. list删除元素,直观写下来的代码如下: package main i ...
- 从零学脚手架(三)---webpack属性详解
如果此篇对您有所帮助,在此求一个star.项目地址: OrcasTeam/my-cli 在上一篇中,介绍了webpack的entry.output.plugins属性. 在这一篇,接着介绍其它配置属性 ...
- 带你从零学ReactNative开发跨平台App开发(三)
ReactNative跨平台开发系列教程: 带你从零学ReactNative开发跨平台App开发(一) 带你从零学ReactNative开发跨平台App开发(二) 带你从零学ReactNative开发 ...
- 零基础,三个月内,找到??? java后端开发工作
一.分析你的问题 出于尊重,先分析一下你的原问题吧,从您的问题,我提取到关键信息:"零基础"."三个月内"."找到工作",最后一个关键词&q ...
- 从头开始学JavaScript (三)——数据类型
原文:从头开始学JavaScript (三)--数据类型 一.分类 基本数据类型:undefined.null.string.Boolean.number 复杂数据类型:object object的属 ...
- C语言老司机学Python (五)
今天看的是标准库概览. 操作系统接口: 用os模块实现. 针对文件和目录管理,还有个shutil模块可以用. 例句: import os os.getcwd() # 返回当前的工作目录 os.chdi ...
- 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手
.引言 网络编程中TCP协议的三次握手和四次挥手的问题,在面试中是最为常见的知识点之一.很多读者都知道“三次”和“四次”,但是如果问深入一点,他们往往都无法作出准确回答. 本篇文章尝试使用动画图片的方 ...
随机推荐
- Beta发布 _thunder_文案+美工展示
作业要求:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/1366 团队介绍:thunder 组成员及各位博客地址: 1.王航:htt ...
- bing词典vs有道词典对比测试报告——功能篇之细节与用户体验
之所以将细节与用户体验放在一起讨论,是因为两者是那么的密不可分.所谓“细节决定成败”,在细节上让用户感受方便.舒适.不费心而且温馨,多一些人文理念,多一些情怀,做出来的产品自然比其他呆板的产品更受欢迎 ...
- java实验2实验报告(20135232王玥)
实验二 Java面向对象程序设计 一.实验内容 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 4. 熟悉S.O.L.I.D原则 5. 了解 ...
- 第二阶段Sprint1
昨天:进行第二阶段第一次站立会议,讨论冲刺阶段,目标,任务认领 今天:实现视频录制,共享平台的视频下载和上传 遇到的问题:调手机摄像头没问题,共享平台怎么办
- 25_IO_第25天(Properties、序列化流、打印流、CommonsIO)_讲义
今日内容介绍 1.Properties集合 2.序列化流与反序列化流 3.打印流 4.commons-IO 01Properties集合的特点 * A: Properties集合的特点 * a: Pr ...
- 蜗牛慢慢爬 LeetCode 11. Container With Most Water [Difficulty: Medium]
题目 Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai ...
- beta阶段成果展示博客
跟着我们一一点一点揭开蒙娜丽莎的微笑 - 本次beta阶段之前,我们团队,对其他组在事后诸葛亮期间对我们的评价进行深刻的审视,特别是缺点方面,开了好几次的站立会议,专门讨论beta的主要方向和任务.最 ...
- 1使用 vue-cli 搭建项目(cp)
http://www.cnblogs.com/wisewrong/p/6255817.html(copy:web) https://zhuanlan.zhihu.com/p/26183652(也很好) ...
- jdbc -- 001 -- 一般方式创建数据库连接(oracle/mysql)
连接数据库步骤: 1. 注册驱动(只做一次) 2. 建立连接(Connection) 3. 创建执行SQL的语句(Statement) 4. 执行语句 5. 处理执行结果(ResultSet) 6. ...
- Cron表达式简单的介绍
1.Cron是什么,用来做什么的 根据百度百科的解释:计划任务,是任务在约定的时间执行已经计划好的工作,这是表面的意思.在Linux中,我们经常用到 cron 服务器来完成这项工作.cron服务器可以 ...