AirCode 是一个在线开发和部署 Node.js 应用的平台,为全栈工程师量身定制,目标是让开发变得简单。
最近我为 AirCode 封装了 Pusher API,Pusher是一家专注提供实时通信服务的 SaaS 服务商。
我将 AirCode 和 Pusher 结合起来,发现实现一个在线实时通信服务变得非常简单。
我们来看一下究竟如何简单。
创建服务
首先我们在AirCode上创建一个项目;
进入创建好的项目,我们安装 aircode-pusher:
然后改写一下hello.js:
const {Pusher} = require('aircode-pusher');
const pusher = new Pusher(Pusher.DEBUG_CONFIG);
// Create a channel
const channel = pusher.channel('my-channel');
channel.subscribe('my-event', async ({data}) => {
channel.trigger('my-event', {ack: 'received', raw: data});
});
module.exports = pusher.listen();
这样我们就通过几行代码实现了一个最简单的实时通信服务,在代码里,我们创建了一个频道my-channel,在这个频道里,我们订阅了my-event事件,在订阅事件的回调里,我们简单的把原始数据给推送(广播)回去,这样有一个客户端发送了这个事件,所有订阅该频道的客户端都会收到广播消息。
我们可以测试一下。
发布和测试
首先在AirCode的IDE中点击Deploy发布这个服务
发布后,我们就能获得服务的线上URL:
接着我们在码上掘金创建一个项目,选择添加依赖脚本,添加aircode-pusher的客户端代码。
接着我们通过线上服务的地址创建客户端对象:
const pusher = new Pusher('https://21j8c76823.us.aircode.run/hello');
然后我们建立连接,获得订阅的频道,并监听和发送事件:
(async() => {
const channels = await pusher.connect();
channels[0].bind('my-event', (event) => {
console.log(event);
});
await channels[0].send('my-event', {message: 'Hello'});
})();
点击运行代码,稍等片刻就能看到控制台上服务端返回的内容:
实现聊天程序
接下来,我们来实现聊天程序,我们在AirCode项目里再创建一个chat.js的云函数,内容如下:
const {db} = require('aircode');
const {Pusher} = require('aircode-pusher');
const pusher = new Pusher(Pusher.DEBUG_CONFIG);
const historyTable = db.table('messages');
const channel = pusher.channel('chat-channel');
channel.subscribe('chat', async ({event, data}) => {
await historyTable.save(data);
await channel.trigger(event, data);
});
channel.subscribe('get-history', async ({event, data, channel}) => {
// Get history messages
const count = data.count || 10;
const messages = await historyTable.where()
.sort({createdAt: -1})
.projection({user: true, message: true, datetime: true})
.limit(10).find();
channel.responseBody = {messages};
});
module.exports = pusher.listen();
上面的代码,我们订阅了chat事件和get-history事件,其中get-history事件我们是获取历史聊天记录,该事件只需要执行一次,并且立即返回结果,我们可以通过channel.responseBody直接返回,因为Pusher的客户端发送请求走的是HTTP,而监听数据才是WebSocket,所以我们直接可以通过responseBody拿到HTTP的响应数据,这样就省去了一次WebSocket数据发送。
对应的,我们实现客户端安代码:
<div id="chatbox">
</div>
<div class="chatbar">
<input id="chat" placeholder="just say something..." disabled></input>
<button id="postchat" disabled>post</button>
</div>
html, body {
padding: 0;
margin: 0;
}
#chatbox {
box-sizing: border-box;
width: 90vw;
height: 80vh;
margin: 10px;
padding: 6px;
border: solid 1px #ddd;
}
.chatbar {
margin-top: 10px;
padding-left: 10px;
}
.chatbar #chat {
width: 80vw;
height: 2rem;
border: solid 1px #ddd;
}
#chat {
padding-left: 4px;
}
const logger = JCode.logger(chatbox);
function log(data) {
logger.log('%c %s: %c%s %c[%s]', 'color:blue', data.user, 'color:#333', data.message, 'color:#999', data.datetime);
}
const pusher = new Pusher('https://21j8c76823.us.aircode.run/chat');
(async () => {
// 建立到服务的连接,并获取服务端订阅的所有频道
const channels = await pusher.connect();
const user = localStorage.getItem('userName') || 'user-' + Math.random().toString(16).slice(2, 8);
localStorage.setItem('userName', user);
// 通过 bind 监听数据
channels[0].bind('chat', (data) => {
console.log(data);
log(data);
});
const {messages} = await channels[0].send('get-history', '');
postchat.disabled = false;
chat.disabled = false;
logger.log('初始化完毕');
logger.log('<hr/>');
messages.reverse().forEach(log);
postchat.addEventListener('click', (event) => {
event.preventDefault();
if(chat.value) {
const now = (new Date()).toLocaleString();
const data = {
user,
message: chat.value,
datetime: now
}
channels[0].send('chat', data);
chat.value = '';
}
});
chat.addEventListener('keydown', (event) => {
if(event.key === 'Enter') {
const now = (new Date()).toLocaleString();
if(chat.value) {
const data = {
user,
message: chat.value,
datetime: now
}
channels[0].send('chat', data);
chat.value = '';
}
}
});
})();
我们的输出使用了JCode-tools,所以添加这部分依赖。
这样我们就完成了所有的功能,最终效果如下:
一个聊天室就这么简简单单完成了,你是否也想亲手试一试,可以按照上面步骤操作。有任何问题,欢迎在评论区讨论。
不过,最后提醒一下,服务端的 const pusher = new Pusher(Pusher.DEBUG_CONFIG); 用的是测试配置,如果你想上线你自己的服务,最好自己去 Pusher.com 免费注册一个账号,然后将配置信息替换成你自己的配置。
具体使用方法,详见 AirCode 的 GitHub 仓库。