openresty+websocket+redis simple chat
openresty 很早就支持websocket了,但是早期的版本cosocket是单工的,处理起来比较麻烦参见邮件列表讨论 websocket chat,后来的版本cosocket是双全工的,就可以按照这个讨论的方案来实现基于websocket的聊天,或者是push程序了,但是网络上没有找到一个具体一点的例子,于是自己写了个simple的例子。
1 思路
client的websocket连接到openresty之后,使用ngx.thread.spawn
启动两个 轻线程
,一个用来接收客户端提交的数据往redis的channel写,另一个用来订阅channel,读取redis的数据写给客户端。channel相当于一个chat room,多个client一起订阅,有人发聊天信息(pub),所有人都能得到信息(sub)。代码比较简陋,简单的思路的实现。
2 服务端代码
依赖:
- openresty
- redis
- lua-resty-redis
- lua-resty-websocket 只支持RFC 6455
nginx的配置全贴了,就是两个location,一个是页面地址,一个是websocket地址。
配置片段
location = /sredis {
content_by_lua_file conf/lua/ws_redis.lua;
}
location ~ /ws/(.*) {
alias conf/html/$1.html;
}
lua代码
-- simple chat with redis
local server = require "resty.websocket.server"
local redis = require "resty.redis"
local channel_name = "chat"
local msg_id = 0
--create connection
local wb, err = server:new{
timeout = 10000,
max_payload_len = 65535
}
--create success
if not wb then
ngx.log(ngx.ERR, "failed to new websocket: ", err)
return ngx.exit(444)
end
local push = function()
-- --create redis
local red = redis:new()
red:set_timeout(5000) -- 1 sec
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect redis: ", err)
wb:send_close()
return
end
--sub
local res, err = red:subscribe(channel_name)
if not res then
ngx.log(ngx.ERR, "failed to sub redis: ", err)
wb:send_close()
return
end
-- loop : read from redis
while true do
local res, err = red:read_reply()
if res then
local item = res[3]
local bytes, err = wb:send_text(tostring(msg_id).." "..item)
if not bytes then
-- better error handling
ngx.log(ngx.ERR, "failed to send text: ", err)
return ngx.exit(444)
end
msg_id = msg_id + 1
end
end
end
local co = ngx.thread.spawn(push)
--main loop
while true do
-- 获取数据
local data, typ, err = wb:recv_frame()
-- 如果连接损坏 退出
if wb.fatal then
ngx.log(ngx.ERR, "failed to receive frame: ", err)
return ngx.exit(444)
end
if not data then
local bytes, err = wb:send_ping()
if not bytes then
ngx.log(ngx.ERR, "failed to send ping: ", err)
return ngx.exit(444)
end
ngx.log(ngx.ERR, "send ping: ", data)
elseif typ == "close" then
break
elseif typ == "ping" then
local bytes, err = wb:send_pong()
if not bytes then
ngx.log(ngx.ERR, "failed to send pong: ", err)
return ngx.exit(444)
end
elseif typ == "pong" then
ngx.log(ngx.ERR, "client ponged")
elseif typ == "text" then
--send to redis
local red2 = redis:new()
red2:set_timeout(1000) -- 1 sec
local ok, err = red2:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "failed to connect redis: ", err)
break
end
local res, err = red2:publish(channel_name, data)
if not res then
ngx.log(ngx.ERR, "failed to publish redis: ", err)
end
end
end
wb:send_close()
ngx.thread.wait(co)
3 页面代码
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<script type="text/javascript">
var ws = null;
function WebSocketConn() {
if (ws != null && ws.readyState == 1) {
log("已经在线");
return
}
if ("WebSocket" in window) {
// Let us open a web socket
ws = new WebSocket("ws://localhost:8008/sredis");
ws.onopen = function() {
log('成功进入聊天室');
};
ws.onmessage = function(event) {
log(event.data)
};
ws.onclose = function() {
// websocket is closed.
log("已经和服务器断开");
};
ws.onerror = function(event) {
console.log("error " + event.data);
};
} else {
// The browser doesn't support WebSocket
alert("WebSocket NOT supported by your Browser!");
}
}
function SendMsg() {
if (ws != null && ws.readyState == 1) {
var msg = document.getElementById('msgtext').value;
ws.send(msg);
} else {
log('请先进入聊天室');
}
}
function WebSocketClose() {
if (ws != null && ws.readyState == 1) {
ws.close();
log("发送断开服务器请求");
} else {
log("当前没有连接服务器")
}
}
function log(text) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(text));
document.getElementById('log').appendChild(li);
return false;
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketConn()">进入聊天室</a>
<a href="javascript:WebSocketClose()">离开聊天室</a>
<br>
<br>
<input id="msgtext" type="text">
<br>
<a href="javascript:SendMsg()">发送信息</a>
<br>
<ol id="log"></ol>
</div>
</body>
</html>
4 效果
用iphone试了试,不好使,可能是websocket版本实现的问题。pc端测试可以正常使用。
Reading
openresty+websocket+redis simple chat的更多相关文章
- CentOS6.4 安装OpenResty和Redis 并在Nginx中利用lua简单读取Redis数据
1.下载OpenResty和Redis OpenResty下载地址:wget http://openresty.org/download/ngx_openresty-1.4.3.6.tar.gz Re ...
- openresty websocket 使用
openresty websocket 使用 1. 代码如下: local server =require"resty.websocket.server" local wb, ...
- Canvas + WebSocket + Redis 实现一个视频弹幕
原文出自:https://www.pandashen.com 页面布局 首先,我们需要实现页面布局,在根目录创建 index.html 布局中我们需要有一个 video 多媒体标签引入我们的本地视频, ...
- Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis
转载请标明出处: http://blog.csdn.net/forezp/article/details/78616714 本文出自方志朋的博客 Lua模块开发 在实际的开发过程中,不可能把所有的lu ...
- swoole+websocket+redis实现一对一聊天
如同web端的QQ和微信一样,这是一个web端的聊天程序. 环境:ubuntu + php + swoole扩展 + redis + mysql Redis 实现每个连接websocket的服务都唯一 ...
- openresty + lua 2、openresty 连接 redis,实现 crud
redis 的话,openresty 已经集成,ng 的话,自己引入即可. github 地址:https://github.com/openresty/lua-resty-redis github ...
- 使用 Django WebSocket Redis 搭建在线即时通讯工具
话不多说先上效果图演示 项目:http://112.74.164.107:9990/ 1.安装组建 redis: yum install redis/apt install redis 2.创建虚拟化 ...
- Openresty+Lua+Redis灰度发布
灰度发布,简单来说,就是根据各种条件,让一部分用户使用旧版本,另一部分用户使用新版本.百度百科中解释:灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式.AB test就是一种灰度发布方式,让一部分 ...
- openresty && hashids&& redis 生成短链接
1. 原理 a. 从redis 获取需要表示的短链接的id( redis incr) b. hashids 编码 id c. openresty conteent_by_lu ...
随机推荐
- JAVA对象及属性的内存堆栈管理(通过小程序简单说明)
JAVA在执行过程中会划分4个内存区域(heap.stack.data segment.code segment)代码区(codesegment):java开始执行会把代码加载到code segmen ...
- Android核心安全机制(一)
Android六种核心安全机制-加密.密钥.签名与证书 对于移动开发,程序猿很容易会忘记一些安全问题,如一个MD5的加密,大部分人都知道怎么去使用,但是其中的一些加密原理,加密方式却只有少部分会去了解 ...
- 带你深入理解STL之List容器
上一篇博客中介绍的vector和数组类似,它拥有一段连续的内存空间,并且起始地址不变,很好的支持了随机存取,但由于是连续空间,所以在中间进行插入.删除等操作时都造成了内存块的拷贝和移动,另外在内存空间 ...
- Cocos2D iOS之旅:如何写一个敲地鼠游戏(二):Cocos2D中的高清支持
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- listview下拉刷新上拉加载扩展(三)-仿最新版美团外卖
本篇是基于上篇listview下拉刷新上拉加载扩展(二)-仿美团外卖改造而来,主要调整了headview的布局,并加了两个背景动画,看似高大上,其实很简单: as源码地址:http://downloa ...
- 新手学python(1):解析XML与系统调用
最近需要做一个项目,完成一批音乐的格式转换.由于之前并未学习过python,所以想借此机会学一下.在介绍自己的学习过程之前,先把项目简要描述一下.目前在一台服务器a上有几十万首原始的MP3音乐文件,现 ...
- python中MySQLdb的使用
先举一例: 一个 Python代码实例: # -*- coding: utf-8 -*- #mysqldb import time, MySQLdb #连接 ...
- eclipse new server Cannot create a server using the selected type 网上有两种办法,其实原理一样
eclipse new server Cannot create a server using the selected type 网上有两种办法,其实原理一样 第一种说法: 还真的找到解决的方法了, ...
- Cocos2D旋转炮塔到指定角度(一)
原文地址:Rotating Turrets: How To Make A Simple iPhone Game with Cocos2D 2.X Part 2 翻译有节选和删除. 在你旋转炮塔之前,首 ...
- C++编译器何时为用户提供默认构造函数
第一种是类成员中有成员是类对象,并且该成员的类含有默认构造函数,那么C++编译器会帮你给这个类也生成一个默认构造函数,用来调用其成员对象的构造函数,完成该成员的初始化构造.需要强调的是,如果这个成员的 ...