SAE上的Channel服务演示

1. Channel服务是什么

SAE 上的 Channel 服务就是一个共用的 WebSocket 服务器.

和自建的服务器的双工形式有些不同, 因为是共用的, 所以逻辑必须统一. 于是对于所有往服务器的写 操作, 全部是回调对应的应用的固定 URL . 既然这样, 我们就当它不能写好了, 写的相关逻辑, 我们自己在应用上通过 HTTP 实现.

这样, 其实 Channel 服务就是一个很单纯的 广播站 了. 通过 API 写入的数据, 会广播给当前的所有连接.

信息流上, 就是一个简单的环状:

      +-----------------+
      |   app server    |<----+
      +-----------------+     |
               |              |
               |              |
               v              |
      +-----------------+     |
      | channel server  |     |
      +-----------------+     |
               |              |
               |              |
               v              |
      +-----------------+     |
      | browser/client  |-----+
      +-----------------+

当然, 如果你要处理"连接创建", "连接关闭"这两个事件, 那你还是得去处理 Channel 的回调. 只是处理数据的话, 就别管回调了, 上面那个环就可以了.

2. Web服务器

Web服务器, 就是前面环图中的 app server , 它要做两个事:

  • 创建 ws 服务, 即获取一个 ws 服务的 URL .
  • 获取请求数据, 往 channel server 中写入数据.

这两个事对应 sae.channel 中的仅有的两个函数:

  • sae.channel.create_channel(name, duration=None)
  • sae.channel.send_message(name, message)

我们也正好把 GET 方法用于第一件事, 把 POST 方法用于第二件事, index.wsgi 文件:

# -*- coding: utf-8 -*-

CHANNEL_NAME = 'zchannel'

import sae.channel
import urllib

def application(environ, start_response):
    if environ.get('REQUEST_METHOD', 'GET') == 'GET':
        #url -> ws://channel.sinaapp.com/com/xxx
        url = sae.channel.create_channel(CHANNEL_NAME)
        start_response('200 ok', [('content-type', 'text/plain')])
        return [url]

    length = environ.get('CONTENT_LENGTH', 0)
    length = int(length)
    body = environ['wsgi.input'].read(length)
    msg = urllib.unquote(body.split('=', 1)[1])
    sae.channel.send_message(CHANNEL_NAME, msg)
    start_response('200 ok', [('content-type', 'text/html')])
    return ['ok']

3. 浏览器客户端

高级浏览器对 WebSocket 早已支持, 使用 js 操作 WebSocket 很容易:

var ws = new WebSocket(ws_url);

ws.onmessage = function(msg){
  console.log(msg)
}

当然, 我们可以加一些简单的标签来实现一个页面应用. 后面会有代码.

4. 命令行客户端

Tornado 中已经实现了一个 WebSocket 的客户端(至少 4.0.1 这个版本有), 所以我们可以做一个:

  • 从标准输入中读取内容.
  • 然后以 POST 方法写到 HTTP 服务器.
  • 同时读取 WebSocket 服务器的内容并显示到标准输出.

命令行客户端代码:

# -*- coding: utf-8 -*-

import re
import tornado.ioloop
import tornado.websocket
import tornado.gen
import tornado.iostream
import tornado.httpclient
import tornado.httputil

@tornado.gen.coroutine
def show_message():
    client = tornado.httpclient.AsyncHTTPClient()
    res = yield client.fetch('http://zchannel.sinaapp.com/')
    ws = re.findall("WebSocket\('(.*?)'\)", res.body)[0];
    conn = yield tornado.websocket.websocket_connect(ws)
    while 1:
        msg = yield conn.read_message()
        print '>', msg

@tornado.gen.coroutine
def write_message():
    stream = tornado.iostream.PipeIOStream(fd=0)
    client = tornado.httpclient.AsyncHTTPClient()
    while 1:
        s = (yield stream.read_until('\n')).rstrip()
        yield client.fetch('http://zchannel.sinaapp.com/', method='POST',
                           body=tornado.httputil.urlencode({'msg': s}))

if __name__ == '__main__':
    show_message()
    write_message()
    tornado.ioloop.IOLoop.current().start()

直接执行上面的代码, 就可以在终端中输入内容, 并看到 WebSocket 服务器吐出来的数据了.

代码中对 GET 方法访问 HTTP 服务器时获取的数据做了处理, 是因为 GET 方法我不光返回一个简单的 WebSocket 的 URL 了, 而是返回了一个完整的 HTML 页面, WebSocket 的 URL 需要从页面内容中提取. 完整代码在后面.

5. Web服务器改进

Web服务器在实现上, 它的 GET 方法只返回一个 WebSocket 的 URL 有些浪费, 我们可以直接返回一个完整的 HTML 页面, index.wsgi 文件:

# -*- coding: utf-8 -*-

s = u'''
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>zchannel</title>
</head>
<body>

  <iframe name="iframe" style="display: none;"></iframe>
  <form action="http://zchannel.sinaapp.com/" method="post" target="iframe">
    <input type="text" name="msg" /><button type="submit">发送</button>
  </form>
  <pre id="show">
  </pre>

<script type="text/javascript">

var ws = new WebSocket('%s');

ws.onmessage = function(msg){
  document.getElementById('show').innerHTML += '<br /> > ' + msg.data;
}

</script>

</body>
</html>
'''

CHANNEL_NAME = 'zchannel'

import sae.channel
import urllib

def application(environ, start_response):
    if environ.get('REQUEST_METHOD', 'GET') == 'GET':
        #url -> ws://channel.sinaapp.com/com/xxx
        url = sae.channel.create_channel(CHANNEL_NAME)
        html = (s % url).encode('utf-8')
        start_response('200 ok', [('content-type', 'text/html')])
        return [html]

    length = environ.get('CONTENT_LENGTH', 0)
    length = int(length)
    body = environ['wsgi.input'].read(length)
    msg = urllib.unquote(body.split('=', 1)[1])
    sae.channel.send_message(CHANNEL_NAME, msg)
    start_response('200 ok', [('content-type', 'text/html')])
    return ['ok']

现在可以访问 http://zchannel.sinaapp.com/ 看效果了.

时间: 2025-01-20 10:59:47

SAE上的Channel服务演示的相关文章

如何在SAE上使用Ueditor图片上传功能

SAE上是没有目录读写权限的,所以要在SAE使用Ueditor的图片上传功能需要借助SAE的Storage服务. 一.开通Storage服务 在SAE控制台开通Storage服务,并新增一个domain. 二.修改Ueditor代码 Ueditor处理上传文件的方法在DjangoUeditor/jviews.py中,上传图片的请求是由下面函数处理的 #上传附件 @csrf_exempt def UploadFile(request,uploadtype,uploadpath): ''''' 省略

SAE上微信公众账号开发参考

微信公众账号因为格式相对固定, 比做一个 Web 类应用简单多了. 此文以 SAE 环境为背景介绍开发过程, 从 0 开始, 不依赖任何 Web 框架, 以 wsgi 接口为基础. 如果你是想学习, 那么搞明白数据流和处理规则是唯一的重点, 至于 Web 框架随意. 在开发过程中, 会涉及一些辅助工具的开发, 这些东西不涉及项目最终部署, 但是却为日常开发带来方便. 因为这些东西可能会涉及其它方面的基础知识, 所以我不会详细介绍实现, 有兴趣者在理解原理之后可以自己实现适合自己的东西. 1. 申

PHP Wrapper在SAE上的应用方法_php技巧

本文讲述了PHP Wrapper在SAE上的应用方法.分享给大家供大家参考,具体如下: 一.PHP Wrapper是什么 自PHP 4.3开始,PHP开始允许用户通过stream_wrapper_register()自定义URL风格的协议.用户使用fopen(), copy()等文件系统函数对封装协议进行操作时,PHP会调用注册协议时所提供的类中相应的函数. PHP手册中给了一个例子,它将VariableStream类注册为var://协议,通过这个协议,用户可以使用文件系统函数直接读写全局变量

在SAE上搭建最新wordpress的方法_php实例

安装SAE上的wordpress,创建应用选择wordpress模板,安装后是3.4版本 新建一个版本2,下载最新wordpress安装包并解压到版本2中 初步猜想修改地方: 数据库配置:wp-config.php 复制代码 代码如下: <?php /**  * WordPress 基础配置文件.  *  * 本文件包含以下配置选项: MySQL 设置.数据库表名前缀.  * 密匙.WordPress 语言设定以及 ABSPATH.如需更多信息,请访问  * {@link http://code

为什么官网下载的wordpress直接部署到Gae和SAE上都不能使用?

SAE之流不能安装原生wordpress的直接原因是不支持写入.因为不支持写入,导致安装时的配置文件没法创建,运行时模板.数据的缓存文件没法生成.其他通用的应用程序不能运行也有类似的原因:像编辑模板这样的简单的操作也没法在SAE上做,因为模板通常以文件形式保存,编辑后模板无法写回文件导致无法保存.再以流行的smarty模板引擎为例,渲染模板时会先检查有没有模板缓存,如果没有就先创建,这也需要写入文件权限.至于常见的软件中内置的一键升级.应用商店.模板商店等功能也都因为无法创建文件而瘫痪. 如果要

Win7运行中出现“错误1079:此服务的帐户不同于运行于同一进程上的其他服务的帐户”

Windows 7在开启服务时出现"错误1079:此服务的帐户不同于运行于同一进程上的其他服务的帐户"的提示,如下图所示:   原因分析: 此故障通常在由svchost服务宿主进程所启动的服务上发生,Windows 7最多可以启动七个svchost进程实例,分别负责启动一组服务.一组服务中的每个服务必须和对应的svchost进程实例运行在同一个启动帐户下.例如Alert服务属于Local  Service组的服务,其对应的svchost进程实例运行在Local Service帐户下,如

在Windows Azure云服务上设计大型服务的最佳做法

今天的帖子来自于http://www.aliyun.com/zixun/aggregation/16689.html">Jason Roth,主编程作家.他提供了来自我们的客户咨询团队的新白皮书的概述,涉及在Windows Azure上 设计大型服务的最佳做法.我们最近发行了新的白皮书:在Windows Azure 云服务上设计大型服务的最佳做法.这份文件汇集了基于实际的客户约定的设计模式和指导方针.它结合了最好的策略和设计模式,始终如一地证明了真实世界的 Windows Azure 应用

在SAE上部署Python的Django框架的一些问题汇总

  这篇文章主要介绍了在SAE上部署Python的Django框架的一些问题汇总,SAE是新浪的一个在线APP部署平台,并且对Python应用提供相关支持,需要的朋友可以参考下 花了些工夫将碎片网部署到了SAE,中途遇到各类问题.感觉SAE看上去很美,实际上却并不是太成熟(至少python版如此). 下面记录下我遇到的一些主要问题以及解决方法. django版本问题 Django1.4都即将发布了,SAE平台自带的SAE版本依旧为1.2x.为使用django1.3版本,你需上传自己的django

Windows服务器上配置SNMP服务方法

SNMP(Simple Network Management Protocol,简单网络管理协议),用来对通信线路进行管理.在Windows服务器上配置SNMP服务时,使用手动填写信息太麻烦.下面是使用命令行执行配置文件来实现. 创建snmp.inf [NetOptionalComponents] SNMP = 1 [SNMP] Contact_Name = "ITSupport" Location = "ServerRoom" Service = Physical