Centos安装Oracle客户端

从这里”http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html”下载以下三个文件

instantclient-basic-Linux.x64-11.2.0.4.0.zip

instantclient-sdk-linux.x64-11.2.0.4.0.zip

instantclient-sqlplus-linux.x64-11.2.0.4.0.zip

新建并复制到Oracle文件夹下,自然解压后,会生成“instantclient_11_2”目录,basicsqlplus会在其根下,而sdk会在其下生成“sdk”目录;在其下建立目录“/network/admin”,创建文件:“tnsnames.ora”,并添加:

ORCL =

 (DESCRIPTION =

   (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.232.1)(PORT = 1521))

   (CONNECT_DATA =

     (SERVER = DEDICATED)

     (SERVICE_NAME = orcl)

   )

 )

配置:”sudo gedit /etc/profile

export ORACLE_HOME=/root/instantclient_11_2

export PATH=$ORACLE_HOME:$PATH

export TNS_ADMIN=$ORACLE_HOME/network/admin

export LD_LIBRARY_PATH=$ORACLE_HOME:$LD_LIBRARY_PATH

export NLS_LANG=’SIMPLIFIED CHINESE_CHINA.AL32UTF8′

 运行:“source /etc/profile”使之生效

执行:sqlplus /nolog

结果:

u@ubuntu:~$ sqlplus /nolog

SQL*Plus: Release 11.2.0.4.0 Production on星期二 3 31 23:35:222015

Copyright (c) 1982, 2013, Oracle. All rights reserved.

SQL>

    creating build/lib.linux-x86_64-3.5-11g

    gcc -pthread -shared build/temp.linux-x86_64-3.5-11g/cx_Oracle.o -L/root/instantclient_11_2 -lclntsh -o build/lib.linux-x86_64-3.5-11g/cx_Oracle.cpython-35m-x86_64-linux-gnu.so

    /usr/bin/ld: cannot find -lclntsh

    collect2: error: ld returned 1 exit status

    error: command ‘gcc’ failed with exit status 1

解决方法:ln -s /root/instantclient_11_2/libclntsh.so.11.1 /root/instantclient_11_2/libclntsh.so

Celery,Tornado,Supervisor构建和谐的分布式系统【转】

Celery 分布式的任务队列

与rabbitmq消息队列的区别与联系:

  • rabbitmq 调度的是消息,而Celery调度的是任务.
  • Celery调度任务时,需要传递参数信息,传输载体可以选择rabbitmq.
  • 利用rabbitmq的持久化和ack特性,Celery可以保证任务的可靠性.

优点:

  • 轻松构建分布式的Service Provider,提供服务。
  • 高可扩展性,增加worker也就是增加了队列的consumer。
  • 可靠性,利用消息队列的durable和ack,可以尽可能降低消息丢失的概率,当worker崩溃后,未处理的消息会重新进入消费队列。
  • 用户友好,利用flower提供的管理工具可以轻松的管理worker。

    flower
  • 使用tornado-celery,结合tornado异步非阻塞结构,可以提高吞吐量,轻松创建分布式服务框架。
  • 学习成本低,可快速入门

快速入门
定义一个celery实例main.py:

from celery import Celery
app = Celery('route_check', include=['check_worker_path'], 
        broker='amqp://user:password@rabbitmq_host:port//')
app.config_from_object('celeryconfig')

include指的是需要celery扫描是否有任务定义的模块路径。例如add_task 就是扫描add_task.py中的任务

celery的配置文件可以从文件、模块中读取,这里是从模块中读取,celeryconfig.py为:

from multiprocessing import cpu_count

from celery import platforms
from kombu import Exchange, Queue

CELERYD_POOL_RESTARTS = False
CELERY_RESULT_BACKEND = 'redis://:password@redis_host:port/db'
CELERY_QUEUES = (
    Queue('default', Exchange('default'), routing_key='default'),
    Queue('common_check', Exchange('route_check'), routing_key='common_check'),
    Queue('route_check', Exchange('route_check'), routing_key='route_check', delivery_mode=2),
    Queue('route_check_ignore_result', Exchange('route_check'), routing_key='route_check_ignore_result',
          delivery_mode=2)
)
CELERY_ROUTES = {
    'route_check_task.check_worker.common_check': {'queue': 'common_check'},
    'route_check_task.check_worker.check': {'queue': 'route_check'},
    'route_check_task.check_worker.check_ignore_result': {'queue': 'route_check_ignore_result'}
}
CELERY_DEFAULT_QUEUE = 'default'
CELERY_DEFAULT_EXCHANGE = 'default'
CELERY_DEFAULT_EXCHANGE_TYPE = 'direct'
CELERY_DEFAULT_ROUTING_KEY = 'default'
# CELERY_MESSAGE_COMPRESSION = 'gzip'
CELERY_ACKS_LATE = True
CELERYD_PREFETCH_MULTIPLIER = 1
CELERY_DISABLE_RATE_LIMITS = True
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True
CELERYD_CONCURRENCY = cpu_count() / 2
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_PUBLISH_RETRY = True
CELERY_TASK_PUBLISH_RETRY_POLICY = {
    'max_retries': 3,
    'interval_start': 10,
    'interval_step': 5,
    'interval_max': 20
}
platforms.C_FORCE_ROOT = True

这里面是一些celery的配置参数

在上面include的add_task.py定义如下:

#encoding:utf8

from main import app

@app.task
def add(x,y):
    return x+y

启动celery
celery -A main worker -l info -Ofair

  • -A 后面是包含celery定义的模块,我们在main.py中定义了app = Celery...
    测试celery:
  • -l 日志打印的级别,这里是info
  • Ofair 这个参数可以让Celery更好的调度任务
# encoding:utf8
__author__ = 'brianyang'

import add_task

result = add_task.add.apply_async((1,2))
print type(result)
print result.ready()
print result.get()
print result.ready()

输出是

<class 'celery.result.AsyncResult'>
False
3
True

当调用result.get()时,如果还没有返回结果,将会阻塞直到结果返回。这里需要注意的是,如果需要返回worker执行的结果,必须在之前的config中配置CELERY_RESULT_BACKEND这个参数,一般推荐使用Redis来保存执行结果,如果不关心worker执行结果,设置CELERY_IGNORE_RESULT=True就可以了,关闭缓存结果可以提高程序的执行速度。
在上面的测试程序中,如果修改为:

# encoding:utf8
__author__ = 'brianyang'

import add_task

result = add_task.add.(1,2)
print type(result)
print result

输出结果为:

<type 'int'>
3

相当于直接本地调用了add方法,并没有走Celery的调度。
通过flower的dashbord可以方便的监控任务的执行情况:

task list

task detail

还可以对worker进行重启,关闭之类的操作

taks_op

使用Celery将一个集中式的系统拆分为分布式的系统大概步骤就是:

  • 根据功能将耗时的模块拆分出来,通过注解的形式让Celery管理
  • 为拆分的模块设置独立的消息队列
  • 调用者导入需要的模块或方法,使用apply_async进行异步的调用并根据需求关注结果。
  • 根据性能需要可以添加机器或增加worker数量,方便弹性管理。

需要注意的是:

  • 尽量为不同的task分配不同的queue,避免多个功能的请求堆积在同一个queue中。
  • celery -A main worker -l info -Ofair -Q add_queue启动Celery时,可以通过参数Q加queue_name来指定该worker只接受指定queue中的tasks.这样可以使不同的worker各司其职。
  • CELERY_ACKS_LATE可以让你的Celery更加可靠,只有当worker执行完任务后,才会告诉MQ,消息被消费。
  • CELERY_DISABLE_RATE_LIMITS Celery可以对任务消费的速率进行限制,如果你没有这个需求,就关闭掉它吧,有益于会加速你的程序。

tornado-celery

tornado应该是python中最有名的异步非阻塞模型的web框架,它使用的是单进程轮询的方式处理用户请求,通过epoll来关注文件状态的改变,只扫描文件状态符发生变化的FD(文件描述符)。
由于tornado是单进程轮询模型,那么就不适合在接口请求后进行长时间的耗时操作,而是应该接收到请求后,将请求交给背后的worker去干,干完活儿后在通过修改FD告诉tornado我干完了,结果拿走吧。很明显,Celery与tornado很般配,而tornado-celery是celery官方推荐的结合两者的一个模块。
整合两者很容易,首先需要安装:

  • tornado-celery
  • tornado-redis
    tornado代码如下:
# encoding:utf8
__author__ = 'brianyang'

import tcelery
import tornado.gen
import tornado.web

from main import app
import add_task

tcelery.setup_nonblocking_producer(celery_app=app)


class CheckHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        x = int(self.get_argument('x', '0'))
        y = int(self.get_argument('y', '0'))
        response = yield tornado.gen.Task(add_task.add.apply_async, args=[x, y])
        self.write({'results': response.result})
        self.finish


application = tornado.web.Application([
    (r"/add", CheckHandler),
])

if __name__ == "__main__":
    application.listen(8889)
    tornado.ioloop.IOLoop.instance().start()

在浏览器输入:http://127.0.0.1:8889/add?x=1&y=2
结果为:

通过tornado+Celery可以显著的提高系统的吞吐量。

Benchmark

使用Jmeter进行压测,60个进程不间断地的访问服务器:
接口单独访问响应时间一般在200~400ms

  • uwsgi + Flask方案:
    uwsgi关键配置:

    processes       = 10
    threads         = 3

    Flask负责接受并处理请求,压测结果:
    qps是46,吞吐量大概是2700/min

    uwsgi+Flask
  • tornado+Celery方案:
    Celery配置:
    CELERYD_CONCURRENCY = 10也就是10个worker(进程),压测结果:
    qps是139,吞吐量大概是8300/min

    tornado+Celery

    从吞吐量和接口相应时间各方面来看,使用tornado+Celery都能带来更好的性能。

Supervisor

  • 什么是supervisor
    supervisor俗称Linux后台进程管理器
  • 适合场景
    — 需要长期运行程序,除了nohup,我们有更好的supervisor
    — 程序意外挂掉,需要重启,让supervisor来帮忙
    — 远程管理程序,不想登陆服务器,来来来,supervisor提供了高大上的web操作界面.
    之前启动Celery命令是celery -A main worker -l info -Ofair -Q common_check,当你有10台机器的时候,每次更新代码后,都需要登陆服务器,然后更新代码,最后再杀掉Celery进程重启,恶不恶心,简直恶心死了。
    让supervisor来,首先需要安装:
    pip install supervisor
    配置文件示例:
[unix_http_server]
file=/tmp/supervisor.sock   ; path to your socket file
chmod=0777
username=admin
password=admin

[inet_http_server]
port=0.0.0.0:2345
username=admin
password=admin

[supervisord]
logfile=/var/log/supervisord.log ; supervisord log file
logfile_maxbytes=50MB       ; maximum size of logfile before rotation
logfile_backups=10          ; number of backed up logfiles
loglevel=info               ; info, debug, warn, trace
pidfile=/var/run/supervisord.pid ; pidfile location
nodaemon=false              ; run supervisord as a daemon
minfds=1024                 ; number of startup file descriptors
minprocs=200                ; number of process descriptors
user=root                   ; default user
childlogdir=/var/log/            ; where child log files will live

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ; use unix:// schem for a unix sockets.
username=admin
password=admin
[program:celery]
command=celery -A main worker -l info -Ofair

directory=/home/q/celeryTest
user=root
numprocs=1
stdout_logfile=/var/log/worker.log
stderr_logfile=/var/log/worker.log
autostart=true
autorestart=true
startsecs=10

; Need to wait for currently executing tasks to finish at shutdown.
; Increase this if you have very long running tasks.
stopwaitsecs = 10

; When resorting to send SIGKILL to the program to terminate it
; send SIGKILL to its whole process group instead,
; taking care of its children as well.
killasgroup=true

; Set Celery priority higher than default (999)
; so, if rabbitmq is supervised, it will start first.
priority=1000

示例文件很长,不要怕,只需要复制下来,改改就可以
比较关键的几个地方是:

[inet_http_server]
port=0.0.0.0:2345
username=admin
password=admin

这个可以让你通过访问http://yourhost:2345 ,验证输入admin/admin的方式远程管理supervisor,效果如下:

remote supervisor

[program:flower]这里就是你要托管给supervisor的程序的一些配置,其中autorestart=true可以在程序崩溃时自动重启进程,不信你用kill试试看。
剩下的部分就是一些日志位置的设置,当前工作目录设置等,so esay~

supervisor优点:

  • 管理进程简单,再也不用nohup & kill了。
  • 再也不用担心程序挂掉了
  • web管理很方便

缺点:

  • web管理虽然方便,但是每个页面只能管理本机的supervisor,如果我有一百台机器,那就需要打开100个管理页面,太麻烦了.

怎么办~

supervisor-easy闪亮登场

通过rpc调用获取配置中的每一个supervisor程序的状态并进行管理,可以分组,分机器进行批量/单个的管理。方便的不要不要的。来两张截图:

  • 分组管理:

    group
  • 分机器管理:

    server

    通过简单的配置,可以方便的进行管理。

Mac下使用c++玩opencv

利用pkg-config 获取相应的库, 安装命令:
brew install pkg-config
重新安装opencv
brew reinstall opencv
编译程序
>g++ $(pkg-config --cflags --libs opencv) /Users/wgg/Downloads/TestOpenCV.cpp -o testOpencv
>testOpencv 
可以查看具体引用库
pkg-config --cflags --libs opencv

-I/usr/local/include/opencv -I/usr/local/include -L/usr/local/lib -lopencv_shape -lopencv_stitching -lopencv_objdetect -lopencv_superres -lopencv_videostab -lopencv_calib3d -lopencv_features2d -lopencv_highgui -lopencv_videoio -lopencv_imgcodecs -lopencv_video -lopencv_photo -lopencv_ml -lopencv_imgproc -lopencv_flann -lopencv_core -lopencv_hal

Python装饰器的一个例子

测试一下例子基本上就明白了

 

# -*- coding: utf-8 -*-
__author__ = 'liwf'
import logging

from time import time

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
is_debug = True

def count_time(is_debug):
    def  handle_func(func):
        def  handle_args(*args, **kwargs):
            if is_debug:
                begin = time()
                func(*args, **kwargs)
                logging.debug( "[" + func.__name__ + "] -> " + str(time() - begin) )
            else:
                func(*args, **kwargs)
        return handle_args
    return handle_func

def pr():
    for i in range(1,1000000):
        i = i * 2
    print("hello world")

def test():
    pr()

@count_time(is_debug)
def test2():
    pr()

@count_time(False)
def test3():
    pr()

if __name__ == "__main__":
    #test()
    test2()
    test2()
    test2()
    test2()
    test2()

Python-decorator装饰器小结[转]

基本概念

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。

很有名的例子,就是咖啡,加糖的咖啡,加牛奶的咖啡。本质上,还是咖啡,只是在原有的东西上,做了“装饰”,使之附加一些功能或特性。

例如记录日志,需要对某些函数进行记录

笨的办法,每个函数加入代码,如果代码变了,就悲催了

装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰,搞定

优点

抽离出大量函数中与函数功能本身无关的雷同代码并继续重用

即,可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限和身份验证从业务中独立出来

概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能

Python中的装饰器

Python中,装饰器实现是十分方便的

原因是:函数可以被扔来扔去。

函数作为一个对象:

A.可以被赋值给其他变量,可以作为返回值

B.可以被定义在另外一个函数内

def:

装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问(申明的函数被换成一个被装饰器装饰过后的函数)

当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。

由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现

分类:

装饰器分为无参数decorator,有参数decorator

* 无参数decorator

生成一个新的装饰器函数

* 有参decorator

有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰

装饰器有参/无参,函数有参/无参,组合共4种

具体定义:

decorator方法

A.把要装饰的方法作为输入参数,

B.在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景),

C.只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)

无参数装饰器 – 包装无参数函数

不需要针对参数进行处理和优化

def decorator(func):
    print "hello"
    return func

@decorator
def foo():
    pass

foo()

foo()等价于:

foo = decorator(foo)
foo()

无参数装饰器 – 包装带参数函数

def decorator_func_args(func):
    def handle_args(*args, **kwargs): #处理传入函数的参数
        print "begin"
        func(*args, **kwargs)   #函数调用
        print "end"
    return handle_args


@decorator_func_args
def foo2(a, b=2):
    print a, b

foo2(1)

foo2(1)等价于

foo2 = decorator_func_args(foo2)
foo2(1)

带参数装饰器 – 包装无参数函数

def decorator_with_params(arg_of_decorator):#这里是装饰器的参数
    print arg_of_decorator
    #最终被返回的函数
    def newDecorator(func): 
        print func
        return func
    return newDecorator


@decorator_with_params("deco_args")
def foo3():
    pass
foo3()

与前面的不同在于:比上一层多了一层封装,先传递参数,再传递函数名

第一个函数decomaker是装饰函数,它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象,所以在内部必须至少创建一个接受被装饰函数的函数,然后返回这个对象(实际上此时foo3= decorator_with_params(arg_of_decorator)(foo3))

带参数装饰器– 包装带参数函数

def decorator_whith_params_and_func_args(arg_of_decorator):
    def handle_func(func):
        def handle_args(*args, **kwargs):
            print "begin"
            func(*args, **kwargs)
            print "end"
            print arg_of_decorator, func, args,kwargs
        return handle_args
    return handle_func


@decorator_whith_params_and_func_args("123")
def foo4(a, b=2):
    print "Content"

foo4(1, b=3)

内置装饰器

内置的装饰器有三个:staticmethod,classmethod, property

class A():
    @staticmethod
    def test_static():
        print "static"
    def test_normal(self):
        print "normal"
    @classmethod
    def test_class(cls):
        print "class", cls

a = A()
A.test_static()
a.test_static()
a.test_normal()
a.test_class()

结果:

static
static
normal
class __main__.A

A.test_static

staticmethod 类中定义的实例方法变成静态方法

基本上和一个全局函数差不多(不需要传入self,只有一般的参数),只不过可以通过类或类的实例对象来调用,不会隐式地传入任何参数。

类似于静态语言中的静态方法

B.test_normal

普通对象方法:普通对象方法至少需要一个self参数,代表类对象实例

C.test_class

类中定义的实例方法变成类方法

classmethod需要传入类对象,可以通过实例和类对象进行调用。

是和一个class相关的方法,可以通过类或类实例调用,并将该class对象(不是class的实例对象)隐式地当作第一个参数传入。

就这种方法可能会 比较奇怪一点,不过只要你搞清楚了python里class也是个真实地存在于内存中的对象,而不是静态语言中只存在于编译期间的类型,就好办了。正常的方法就是和一个类的实例对象相关的方法,通过类实例对象进行调用,并将该实例对象隐式地作为第一个参数传入,这个也和其它语言比较像。

D.区别

staticmethod,classmethod相当于全局方法,一般用在抽象类或父类中。一般与具体的类无关。

类方法需要额外的类变量cls,当有子类继承时,调用类方法传入的类变量cls是子类,而不是父类。

类方法和静态方法都可以通过类对象和类的实例对象访问

定义方式,传入的参数,调用方式都不相同。

E.property

对类属性的操作,类似于Java中定义getter/setter

class B():
    def __init__(self):
        self.__prop = 1
    @property
    def prop(self):
        print "call get"
        return self.__prop
    @prop.setter
    def prop(self, value):
        print "call set"
        self.__prop = value
    @prop.deleter
    def prop(self):
        print "call del"
        del self.__prop

其他

A.装饰器的顺序很重要,需要注意

@A
@B
@C
def f ():

等价于

f = A(B(C(f)))

B.decorator的作用对象可以是模块级的方法或者类方法

C.functools模块提供了两个装饰器。这个模块是Python 2.5后新增的。

functools.wraps(func)total_ordering(cls)这个具体自己去看吧,后续用到了再补充

一个简单例子

通过一个变量,控制调用函数时是否统计时间

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#@author: wklken@yeah.net
#@version: a test of decorator
#@date: 20121027
#@desc: just a test


import logging

from time import time

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
is_debug = True

def count_time(is_debug):
    def  handle_func(func):
        def  handle_args(*args, **kwargs):
            if is_debug:
                begin = time()
                func(*args, **kwargs)
                logging.debug( "[" + func.__name__ + "] -> " + str(time() - begin) )
            else:
                func(*args, **kwargs)
        return handle_args
    return handle_func

def pr():
    for i in range(1,1000000):
        i = i * 2
    print "hello world"

def test():
    pr()

@count_time(is_debug)
def test2():
    pr()

@count_time(False)
def test3():
    pr()

if __name__ == "__main__":
    test()
    test2()
    test3()

结果:

hello world
hello world
DEBUG:root:[test2] -> 0.0748538970947
hello world

The end!

centos7.2 python2.7.5升级到python3.5.2

安装必要的程序
yum install deltarpm
yum install openssl-devel
yum install sqlite-devel (python 中使用sqlite)

安装
0. yum install gcc
1. yum install wget
2. wget https://www.python.org/ftp/python/3.5.2/Python-3.5.2.tgz
3. tar -xf Python-3.5.2.tgz
4. cd Python-3.5.2
5. ./configure
6. make
7. make install

配置
1.mv /usr/bin/python /usr/bin/python2.7.5
2.解决升级后yum不能使用问题
vi /usr/bin/yum
把文件头部的#!/usr/bin/python改成#!/usr/bin/python2.7.5保存退出即可

vi /usr/libexec/urlgrabber-ext-down
把文件头部的#!/usr/bin/python改成#!/usr/bin/python2.7.5保存退出即可

3.将默认python改成3.5.2
ln -s /usr/local/bin/python3.5 /usr/bin/python

4.更新pip版本
rm /usr/bin/pip
ln -s /usr/local/bin/pip3 /usr/bin/pip
5.检测
python -V

常见错误:

make install时:

报错

/usr/bin/install -c -m 644 ./Tools/gdb/libpython.py python-gdb.py
gcc -pthread -c -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -std=c99 -Wextra -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -I. -I./Include -DPy_BUILD_CORE -o Programs/_testembed.o ./Programs/_testembed.c
gcc -pthread -Xlinker -export-dynamic -o Programs/_testembed Programs/_testembed.o libpython3.6m.a -lpthread -ldl -lutil -lm
# Substitution happens here, as the completely-expanded BINDIR
# is not available in configure
sed -e "s,@EXENAME@,/usr/local/bin/python3.6m," < ./Misc/python-config.in >python-config.py
# Replace makefile compat. variable references with shell script compat. ones; ->
LC_ALL=C sed -e 's,\$(\([A-Za-z0-9_]*\)),\$\{\1\},g' < Misc/python-config.sh >python-config
# On Darwin, always use the python version of the script, the shell
# version doesn't use the compiler customizations that are provided
# in python (_osx_support.py).
if test `uname -s` = Darwin; then \
cp python-config.py python-config; \
fi

解决方法:

按提示执行:

>sed -e “s,@EXENAME@,/usr/local/bin/python3.6m,” < ./Misc/python-config.in >python-config.py
>LC_ALL=C sed -e ‘s,\$(\([A-Za-z0-9_]*\)),\$\{\1\},g’ < Misc/python-config.sh >python-config

Tornado服务与前端部署在不同域名下时的跨域cookie问题

场景:服务与前端分开开发,在不同的机器上,为了调试方便,需要解决跨域cookie问题

前端服务:(域名 http://127.0.0.1:10001)

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="content-type" content="application/xhtml+xml; charset=UTF-8" />
    <title></title>
  <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
  </head>
  <body>

    <button id="shuangbei">登录</button>
    <button id="napai">获取信息</button>

    <script type="text/javascript">

     $.ajaxSetup({
      xhrFields: {
         withCredentials: true
      },
      crossDomain: true
    });

       function funOK(msg) {
          alert(msg.uid);
      }
    $(document).ready(function(){

      $("#shuangbei").click(function(){
          $.getJSON('http://127.0.0.1:10002/login', {
                      'uid': 99999999,
                      'type':'login',
                  }).always(function (data) {
                     alert(data.uid);
                  });
      });

      $("#napai").click(function(){
          $.getJSON('http://127.0.0.1:10002/info', {
                  }).always(function (data) {
                     alert(data.uid);
                  });
      });

    });
    </script>

  </body>
</html>

服务端:(域名http://127.0.0.1:10002)

配置:

def set_default_headers(self):
    self.set_header('Access-Control-Allow-Origin', 'http://127.0.0.1:10001')          #这个必须有域名,不能使用通配符
    self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')
    self.set_header('Access-Control-Max-Age', 1000)
    self.set_header('Access-Control-Allow-Headers', '*')
    self.set_header('Content-type', 'application/json')