文档章节

Go语言开发分布式聊天室

呵大官人
 呵大官人
发布于 2015/08/28 14:43
字数 1563
阅读 5475
收藏 150

声明

我是一个刚学go语言的菜鸟,还没有资格谈论什么技术分享,只是为了展示fooking的实际应用,同时把我用go写的聊天室代码贴出来供大家消遣,如果有入不了各位法眼的代码,望轻喷。该聊天室基于fooking,而业务代码是采用Go + Fastcgi。

完整的源代码在  https://github.com/scgywx/fooking/blob/master/example/chat/gateway.go,全代码200多行,去掉router部分代码,实际逻辑代码只有170来行,逻辑简单,功能强大。


详解

聊天服务器的入口main函数里有3个IP和端口配置,分别是Chat服务器、Router服务器和Redis服务器。

func main() {
	listener, _ := net.Listen("tcp", "0.0.0.0:9001")//Chat服务器配置
	srv := newChatServer("test:9010", "test:6379");//Router配置与Redis配置
	fcgi.Serve(listener, srv)
}

Chat服务器就是实现主要的聊天逻辑,Router服务器是用于转发消息,Redis用来存储用户信息。上面我讲过这个聊天室是基于fooking,所以客户端不是直接与go通信,而是透过fooking来访问的。我们使用go内置的fastcgi模块来创建一个服务,然后处理请求即可,这跟http服务器非常像,只是协议规范不同而已,看下面的代码就你知道创建一个fastcgi服务器有多简单了。

func (s *ChatServer) ServeHTTP(rep http.ResponseWriter, req *http.Request) {
	//短连接要调用一下这个,否则go不会主动断开连接
	//req.ParseForm();
	req.Form = make(url.Values);
	
	body, e := ioutil.ReadAll(req.Body)
	if e != nil {
		fmt.Printf("read request error\n")
	}else{
		sessionid := req.Header.Get("SESSIONID")
		event := req.Header.Get("EVENT")
		fmt.Printf("sid=%s, event=%s\n", sessionid, event);
		//具体的业务逻辑处理
        }
}

代码中的sessionid就是当前发送请求的客户端ID,当我们要做一些uid与客户端id映射或者是要发消息给指定用户的时候就可以使用这个ID。event就是当前请求的事件类型(0-表示请求,1-新连接,2-关闭连接),而聊天室只需要关心客户端请求和断开事件。请求可能是登陆、发消息或者是加入频道,而断开事件我们就需要把用户信息删除,并且把他的退出信息广播给所有在聊天室的人。代码如下:

switch event {
	case "1"://新连接
		//TODO
	case "2"://连接关闭
		s.logout(sessionid)
	default://消息处理				
		if len(body) > 0 {
			js, err := simplejson.NewJson(body)
			if err != nil {
				fmt.Printf("parse JSON error, data=")
				fmt.Println(body)
			}else{
				r := s.handle(sessionid, js)
				if len(r) > 0 {
					rep.Header().Add("Content-Length", strconv.Itoa(len(r)))
					rep.Write(r)
				}else{
					fmt.Println("no message response")
				}
			}
		}
}

func (s *ChatServer) handle(sid string, req *simplejson.Json) []byte{
	t, _ := req.Get("type").String()
	switch t {
		case "login": //登陆
			.....
		case "join": //加入房间
			....
		case "msg": //发送消息
			.....
		default:
			fmt.Printf("invalid type")
	}
	
	return []byte("")
}

在上面的消息处理部分,有两句rep.Header().Add和rep.Write,这表示如果需要返回数据给当前发请求的客户端,可以添加Content-Length头(表示要返回给客户端的数据长度),然后调用rep.Write发送数据(类似Http的Request对应一个Response)。

代码里面的消息广播、用户分频道都是在Router部分实现,他是fooking消息转发与用户数据维持的中间件。


为什么使用FastCGI

其实开发一个聊天室可以很简单,可以直接使用websocket协议,让客户端跟服务器直接通信,简单方便,通信代价低。并且做socket服务也不受协议限制,完全可以自定义或者是使用其它轻量级的协议,比如mqtt什么的。然而为什么要使用fastcgi呢?主要是他的协议实现简单,并且扩展性也非常强,目前世界上最强大的语言php的fpm就是使用该协议与nginx进行通信,如果你的服务使用fastcgi协议开发,那么你可以完美的使用nginx与你的服务进行通信,这样做的好处是写socket服务能像写web服务一样调试,开发完一个功能你只需要简单的执行如下命令即可(当然你可能需要添加少量的代码来判断来源是从nginx还是你自己的客户端)。

curl -d '{"type":"login","name":"xxx"}' 'http://fooking/gateway.php?SESSIONID=aaa&EVENT=0'

网关与逻辑分离另一个好处是,当业务逻辑代码需要更新,客户端毫无察觉的,真正做到无痛更新。另外fastcgi协议本身已经支持多路复用(当然这个需要服务端的支持),这个功能可是大名鼎鼎的http到2.0才支持的哟。

协议详细说明请见:http://www.fastcgi.com/drupal/node/6?q=node/22


为什么使用Fooking

文章开篇已经说了,我是一个Go语言的初学者,到这里来不是为了秀go技,而是为了展示fooking与各语言的衔接。那么为什么要使用fooking?我相信很多人都知道网关这个东西(可能在游戏领域应用的更广泛一些),他其主要的目的是用于承载客户端的连接,把消息转发与业务逻辑分开,后端开发人员只需要专心写逻辑即可。这就好比我们写web的时候,从来不需要自己去实现http server。那么fooking也是这样一个开源软件,他将socket服务变的更简单。更多的特性如下:

1 动态网关添加.
2 每个客户端唯一SessionID.
3 组播(类似redis的pub/sub).
4 服务器状态监控.
5 客户端事件通知(如:新连接、关闭连接).
6 后端无语言限制(php, python, go, nodejs, etc...).
7 自定义消息协议.
8 后端长连接维持.

fooking的详细介绍请参见: https://github.com/scgywx/fooking 或者 http://git.oschina.net/scgywx/fooking


使用方法

  • 第一步(下载和编译)
    git clone https://github.com/scgywx/fooking.git
    cd {$FOOKING_PATH}
    make

  • 第二步(启动Router)
    cd src
    ./fooking ../router.lua

  • 第三步(启动Gateway)
    ./fooking ../config.lua

  • 第四步(启动Chat服务器)

    go run gateway.go

  • 第五步(测试) 修改example/chat/index.html文件的Websocket的服务器IP和端口(查找ws://)
    然后用浏览器打开index.html即可


鸣谢

感谢@CFC4N牛腩五花肉对本次项目的大力支持。

© 著作权归作者所有

共有 人打赏支持
呵大官人

呵大官人

粉丝 120
博文 17
码字总数 15799
作品 1
普陀
加载中

评论(6)

呵大官人
呵大官人

引用来自“eecheng”的评论

why not websocket??
客户端是websocket,不过不是直接与go通信,而是透过fooking与go通信。 client -> fooking(使用websocket) fooking -> go(使用fastcgi)
呵大官人
呵大官人

引用来自“你来人间一趟”的评论

楼主没有用channel啊
嗯,channel本来是用来解决同实例不同goroutine之间的数据传输。作为分布式服务器,可能需要跨服务器传输数据,所以是走fooking的router来转发,没有使用channel.
e
eecheng
why not websocket??
lanmingle
lanmingle
好东西!刚学没多久.
__JM_Joy__
__JM_Joy__
不错不错,回头看看
你来人间一趟
你来人间一趟
楼主没有用channel啊
hijk 0.2.2 发布,自动化 JavaScript Web 开发包

hijk 是一个自动化的WebAPI开发包,全程使用JavaScript语言,只需要写一个标准JavaScript函数,复制到 js/目录下,就会自动加载为 WebAPI, 可以通过http提供网络服务,无需编译,修改后自动加...

iBoxDB
2014/07/01
1K
2
PHP聊天室框架

PHP聊天室框架 http://www.workerman.net/doc workerman-chat是一个以workerman作为服务器容器,使用PHP开发的基于Websocket协议的一个可分布式部署的聊天室框架。 workerman-chat采用gatew...

james_laughing
2016/06/27
48
0
OSChina 开源周刊 50 期,每周技术精粹

本周开源资讯 七大科技公司联手设计下一代开源视频格式 jQuery 基金会和 Dojo 基金会合并:Open Web Eclipse 寻求捐赠用于 IDE 等项目的开发 Angular 2.0 调查:43% 选择 Webstorm 编辑器 软...

OSC编辑部
2015/09/04
4.9K
3
博客阅读整理一

(部分文章是系列文章,可在原文博客查找) Java ClassLoader, JavaAgent, Aspectj Weaving一站式扫盲帖:主要总结了ClassLoader、java代理、反射相关的知识点 jdbc实现篇-源码:介绍了jdbc的源...

oO脾气不坏Oo
2015/10/17
80
0
OSChina 技术周刊第二十八期 —— 用 React 编写移动应用

每周技术抢先看,总有你想要的! 移动开发 【软件】RichEditor for Android 【软件】用 React 编写移动应用 React Native 【软件】iOS 图表控件 ios-charts 【博客】iOS 越狱开发——如何将应...

OSC编辑部
2015/04/05
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

可爱的python测试开发库(python测试开发工具库汇总)

欢迎转载,转载请注明来源: github地址 谢谢点赞 本文地址 相关书籍下载 测试开发 Web UI测试自动化 splinter - web UI测试工具,基于selnium封装。 链接 selenium - web UI自动化测试。 链...

python测试开发人工智能安全
今天
2
0
Shiro | 实现权限验证完整版

写在前面的话 提及权限,就会想到安全,是一个十分棘手的话题。这里只是作为学校Shiro的一个记录,而不是,权限就应该这样设计之类的。 Shiro框架 1、Shiro是基于Apache开源的强大灵活的开源...

冯文议
今天
1
0
linux 系统的运行级别

运行级别 运行级别 | 含义 0 关机 1 单用户模式,可以想象为windows 的安全模式,主要用于修复系统 2 不完全的命令模式,不含NFS服务 3 完全的命令行模式,就是标准的字符界面 4 系统保留 5 ...

Linux学习笔记
今天
2
0
学习设计模式——命令模式

任何模式的出现,都是为了解决一些特定的场景的耦合问题,以达到对修改封闭,对扩展开放的效果。命令模式也不例外: 命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。 解决了这...

江左煤郎
今天
3
0
字典树收集(非线程安全,后续做线程安全改进)

将500W个单词放进一个数据结构进行存储,然后进行快速比对,判断一个单词是不是这个500W单词之中的;来了一个单词前缀,给出500w个单词中有多少个单词是该前缀. 1、这个需求首先需要设计好数据结...

算法之名
昨天
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部