websocket使用nginx作为反向代理

需要nginx作为websocket的反向代理,没有nginx反向代理时候没有问题,通过nginx反向代理后会报400错误,查后台调试信息:

tornado.general – DEBUG – Can “Upgrade” only to “WebSocket”.

通过分析原来是需要nginx做如下配置:

upstream wsbackend {
server 127.0.0.1:10003;
}

server {
listen 3000;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

# location / {
# root html;
# index index.html index.htm;
# }

location / {
proxy_pass http://wsbackend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
}
}

如果有多层反向代理的话需要在每一层都加上该信息!

WebSocket实验(实现跨域)

前端代码

<html>
<head>
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0">

    <script>

        var socket;

        if(typeof(WebSocket) == "undefined")
        {

            alert("您的浏览器不支持WebSocket");
        }
        //
var ws = new WebSocket('ws://ws.dev02.zhongcaiweiyou.com/soc');

        var ws = new WebSocket('ws://127.0.0.1:10004/soc');

        // var ws = new WebSocket('wss://api.lottery.com.cn/fb/blackjack/soc');

        ws.onmessage = function(event)
        {
            var table = document.getElementById('message');

            table.insertRow().insertCell().innerHTML = event.data;

        };


        //打开事件
        ws.onopen = function()
        {

            var info = "这是来自客户端的消息" + location.href + new Date();

            //var table = document.getElementById('message');

            // table.insertRow().insertCell().innerHTML = info;

            //ws.send(info);

        };
    //关闭事件

        ws.onclose = function()
        {

            var table = document.getElementById('message');

            table.insertRow().insertCell().innerHTML = "连接关闭";

        };

        //发生了错误事件

        ws.onerror = function()
        {

            var table = document.getElementById('message');

            table.insertRow().insertCell().innerHTML = "连接发生错误";

        };


        //关闭事件
        ws.onclose = function() {

            alert("Socket已关闭");
        };
        
//发生了错误事件

        ws.onerror = function() {

            alert("发生了错误");

        };


        //事件处理函数,当下拉列表选择改变时,触发该函数
        function change(id)
        {
           ws.send("这是来自客户端的消息" + location.href + new Date());

        }

    </script>

</head>
<body>
<table id='message'></table>

<button type="button" id="btnSend" onclick="change()">Click Me!</button>
</body>
</html>

后端:

# -*- coding: utf-8 -*-
__author__ = 'liwf'
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.options, tornado.httpclient
import tornado.web
import tornado.gen
import tornado.httpclient
import tornado.gen

import tornado.websocket

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [
            ('/soc', SocketHandler),
        ]
        settings = dict(
            # login_url = "/auth/login",
            debug=True,
        )
        super(Application, self).__init__(handlers, **settings)

class SocketHandler(tornado.websocket.WebSocketHandler):
    clients = set()

    #解决跨域问题
    def check_origin(self, origin):
        #parsed_origin = urllib.parse.urlparse(origin)
        #return parsed_origin.netloc.endswith(".mydomain.com")
        return True

    @staticmethod
    def send_to_all(message):
        for c in SocketHandler.clients:
            c.write_message(message)

    def open(self):
        self.write_message('Welcome to WebSocket')
        SocketHandler.send_to_all(str(id(self)) + ' has joined')
        SocketHandler.clients.add(self)

    def on_message(self, message):
        SocketHandler.send_to_all(str(id(self)) + ' has joined')
        #self.write_message(message)

    def on_close(self):
        SocketHandler.clients.remove(self)
        SocketHandler.send_to_all(str(id(self)) + ' has left')

def main():
    tornado.options.parse_command_line()
    http_server = Application()

    http_server.listen(10003, xheaders=True)  # xheaders=True为了可以获取到header信息
    print('listening on %s ...' % 10003)

    tornado.ioloop.IOLoop.current().start()


if __name__ == "__main__":
    main()

FaceBook小游戏 (instant game)尝试

今天尝试了一下facebook小游戏的开发上传流程,发现现在还不支持的$.Ajax get 方式,使用文档中提到的xmlhttprequest进行了尝试,成功跟后台通讯,happy!

下一阶段尝试websocket通讯方式。测试前端代码如下:

<html>
<head>
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="viewport" content="initial-scale=1.0,maximum-scale=1.0">


<script src="https://connect.facebook.com/en_US/fbinstant.2.0.js"></script>
    
<script>

    var playerID;
    var platform;
    FBInstant.initializeAsync().then(function()
    {

        var locale = FBInstant.getLocale();
        // 'en_US'

        platform = FBInstant.getPlatform();
        // 'iOS', 'android' or 'web'

        // var sdkVersion = FBInstant.getSDKVersion(); // '2.0'

        playerID = FBInstant.player.getID();


    });

    FBInstant.startGameAsync().then(function() {
      // The player is now guaranteed to have already tapped "play".
      //alert('start');

                var table = document.getElementById('message');
                table.insertRow().insertCell().innerHTML = platform;
                table.insertRow().insertCell().innerHTML = playerID;
    });

    //定义了XMLHttpRequest对象
var xmlrequest;
//完成XMLHttpRequest对象的初始化
function createXMLHttpRequest()
{
    if(window.XMLHttpRequest)
    {
        //DOM 2浏览器
        xmlrequest = new XMLHttpRequest();
    }
    else if (window.ActiveXObject)
    {
        // IE浏览器
        try
        {
            xmlrequest = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch (e)
        {
            try
            {
                xmlrequest = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch (e)
            {
            }
        }
    }
}
//事件处理函数,当下拉列表选择改变时,触发该函数
function change(id)
{
    //初始化XMLHttpRequest对象
    createXMLHttpRequest();
    //设置请求响应的URL
    var uri = "https://xxx/v1/usr/login?uid=123&appid=456";
    //打开与服务器资源的连接
    xmlrequest.open("GET", uri, true);
    //设置处理响应的回调函数
    xmlrequest.onreadystatechange = processResponse;
    //发送请求
    xmlrequest.send(null);
}
//定义处理响应的回调函数
function processResponse()
{
    //响应完成且响应正常
    if (xmlrequest.readyState == 4)
    {
        if (xmlrequest.status == 200)
        {
            var table = document.getElementById('message');
            table.insertRow().insertCell().innerHTML = xmlrequest.responseText;
        }
        else
        {
            var table = document.getElementById('message');
            table.insertRow().insertCell().innerHTML = "异常";
        }
    }
}
</script>


</head>
<body>
<table id='message'></table>

<button type="button" id="btnSend" onclick="change()">Click Me!</button>
</body>
</html>

Safari中无法保存登录信息的问题,Session失效

问题表现:

在Safari中(chrome中没有问题),应用(web)成功登录后再访问其他的接口时提示没有登录

问题原因:

safria隐私设置(cookie)默认是“允许来自我访问的网站”:

而整个应用架构是前端通过jsonp访问后端接口服务,前端跟后端接口服务所用域名不同导致的

问题解决:

方案1、如果仅仅是调试,可以将自己的safria设置成“始终允许”来解决

方案2、如果是正式发布则需要通过将前端和后端接口服务在同一个主域名下

方案3、将后端接口改进成不使用cookie来保存登录信息,直接使用字符串返回,前端每次调用带着授权字符串

Zookeeper安装使用(centos7.2)

Zookeeper 安装

安装java:yum install java

 

Zookeeper下载目录(选择稳定版本):http://apache.org/dist/zookeeper/

例如:http://apache.org/dist/zookeeper/stable/zookeeper-3.4.9.tar.gz

 

解压:

tar -zxvf zookeeper-3.4.9.tar.gz -C /opt/libs

 

“bin目录”中存放有运行脚本;“conf目录”中存放有配置文件;“lib目录”中存放有运行所需要第三方库

 

Zookeeper 配置

一、机模式

1.1编辑配置文件:

> cp /opt/libs/zookeeper-3.4.9/conf/zoo_sample.cfg /opt/libs/zookeeper-3.4.9/conf/zoo.cfg> vi /opt/zookeeper/conf/zoo.cfg 编辑修改下面信息: dataDir=/opt/data/zookeeper/         //数据目录. 可以是任意目录.dataLogDir=/opt/data/zookeeper/logs  //log目录, 同样可以是任意目录. 如果没有设置该参数, 将使用和#dataDir相同的设置.clientPort=2181                  //监听client连接的端口号tickTime=2000                        //zookeeper中使用的基本时间单位, 毫秒值.

 

启动与停止:

方式1:

启动zookeeper服务:

/opt/libs/zookeeper-3.4.9/bin/zkServer.sh start

停止zookeeper服务:

/opt/libs/zookeeper-3.4.9/bin/zkServer.sh stop

方式2:

/opt/libs/zookeeper-3.4.9/bin/zkServer.sh start-foreground (非后台运行ZooKeeper Server进程)

验证是否成功:telnet 127.0.0.1 2181

 

二、集群模式

Zookeeper的分布式部署安装详细文档(集群模式)

集群模式有两种形式:
1)使用多台机器,在每台机器上运行一个ZooKeeper Server进程;
2)使用一台机器,在该台机器上运行多个ZooKeeper Server进程。
在生产环境中,一般使用第一种形式,在练习环境中,一般使用第二种形式。

 

配置

  1. data目录
    用于存放进程运行数据。
  2. data目录下的myid文件
    用于存储一个数值,用来作为该ZooKeeper Server进程的标识。
  3. 监听Client端请求的端口号
  4. 监听同ZooKeeper集群内其他Server进程通信请求的端口号
  5. 监听ZooKeeper集群内“leader”选举请求的端口号
    该端口号用来监听ZooKeeper集群内“leader”选举的请求。注意这个是ZooKeeper集群内“leader”的选举,跟分布式应用程序无关。

参数配置注意事
1)同一个ZooKeeper集群内,不同ZooKeeper Server进程的标识需要不一样,即myid文件内的值需要不一样
2)采用上述第2种形式构建ZooKeeper集群,需要注意“目录,端口号”等资源的不可共享性,如果共享会导致ZooKeeper Server进程不能正常运行,比如“data目录,几个监听端口号”都不能被共享

myid Data Client Server Leader 配置文件
1 /opt/data/zookeeper/z1 2181 2222 2225 z1.cfg
2 /opt/data/zookeeper/z2 2182 3333 3335 z2.cfg
3 /opt/data/zookeeper/z3s 2183 4444 4445 z3.cfg

配置如下:

# zx.cfg

tickTime=2000

initLimit=10

syncLimit=2

dataDir=/opt/data/zookeeper/zx

clientPort=218x

# server.x中的“x”表示ZooKeeper Server进程的标识

server.1=127.0.0.1:2222:2225

server.2=127.0.0.1:3333:3335

server.3=127.0.0.1:4444:4445

注:

  • initLimit: zookeeper集群中的包含多台server, 其中一台为leader, 集群中其余的server为 initLimit参数配置初始化连接时, follower和leader之间的最长心跳时间. 此时该参数设置为5, 说明时间限制为5倍tickTime, 即5*2000=10000ms=10s.
  • syncLimit: 该参数配置leader和follower之间发送消息, 请求和应答的最大时间长度. 此时该参数设置为2, 说明时间限制为2倍tickTime, 即

 

设置myid

>echo “1” > /opt/data/zookeeper/z1/myid

>echo “2” > /opt/data/zookeeper/z2/myid

>echo “3” > /opt/data/zookeeper/z3/myid

 

启动ZooKeeper集群

分别执行

bin/zkServer.sh start conf/z1.cfg,

bin/zkServer.sh start conf/z2.cfg

bin/zkServer.sh start conf/z3.cfg

安装验证

执行命令(建立ZooKeeper Client端到ZooKeeper集群的连接会话)

bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183

 

 管理界面:zkdash: 

安装与运行:

  • 安装并运行mysql
  • 下载zkdash

·        git clone https://github.com/ireaderlab/zkdash.git   (Python2.7)

git clone  https://github.com/wolfelee/zkdash.git  (Python3.5.2,对原版进行了python3的兼容性修改)

  • 安装依赖项

·       cd zkdash·       pip install -r requirements.txt·       (如果报错,直接一项一项安装)

  • pip install peewee

·        

  • 设置配置文件

根据需要修改当前目录下./conf/conf.yml中相关配置信息,配置文件详细说明见后面

  • 同步数据库表结构 首先创建数据库zkdash,并设置数据库的用户名和密码 将配置文件的数据库的用户名和密码进行修改

·         DATABASE:·           db: ‘zkdash’·           host: ‘localhost’   # 修改为你的数据库地址·           port: 3306   # 设置端口号·           user: ‘root’   # 修改用户名·           passwd: ‘123456’  # 修改密码

设置完成后进行初始化数据库

cd zkdashpython ./bin/syncdb.py   # 注意执行路径必须为./bin/syncdb.py

说明:数据库使用mysql,创建表结构前请先配置数据库连接信息

  • 运行

·       cd zkdash·       python init.py -port=20001

说明:初次运行zkdash时需要到zookeeper管理菜单下增加监控的zookeeper集群ip信息

配置文件说明:

配置文件详细说明

数据库配置项(DATABASE)

  • db: 数据库名称
  • host: ip地址
  • port: 端口号
  • user: 用户名
  • passwd: 密码

全局配置项

  • USE_QCONF: 是否通过QConf获取zookeeper数据(使用该项可以提高树形展示配置信息的响应速度)

与QConf的搭配使用:

  • 反馈服务器地址:http://ip:port/api/v1/feedback(Agent同步状况查看依赖此反馈信息)

注意事项:

  1. 新增节点需要先指定父节点,并且只能逐级增加

当设置使用QConf获取zookeeper数据时,zookeeper管理菜单下的zookeeper集群名称需要与QConf 客户端Agent配置文件的idc名称一致

Centos7.2安装mysql

yum安装mysql总是启动不起来,后通过如下方法解决

CentOS 7的yum源中貌似没有正常安装mysql时的mysql-sever文件,需要去官网上下载

# wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
# rpm -ivh mysql-community-release-el7-5.noarch.rpm
# yum install mysql-community-server

成功安装之后重启mysql服务

# service mysqld restart

初次安装mysql是root账户是没有密码的

设置密码的方法

# mysql -uroot
mysql> set password for ‘root’@‘localhost’ = password(‘mypasswd’);
mysql> exit

搞定!