Python 实现多线程下载器

前言

我为什么会想到要写一个下载器呢,实在是被百度云给逼的没招了,之前用 Axel 配合直链在百度云下载视频能达到满速,结果最近两天 Axel 忽然不能用了,于是我就想着要不干脆自己写一个吧,就开始四处查询资料,这就有了这篇博客。

我假设阅读这篇博客的你已经对以下知识有所了解:

  • Python 的文件操作
  • Python 的多线程
  • Python 的线程池
  • Python 的 requests 库
  • HTTP 报文的首部信息

下载

获取文件采用的是 requests 库,该已经封装好了许多 http 请求,我们只需要发送 get 请求,然后将请求的内容写入文件即可:

import requests

r = requests.get('http://files.smashingmagazine.com/wallpapers/july-17/summer-cannonball/cal/july-17-summer-cannonball-cal-1920x1080.png')
with open('wallpaper.png', 'wb') as f:
    f.write(r.content)

随后看看文件夹,那张名为 wallpaper.png 的图片就是我们刚刚下载的。

但是这个功能太简单了,甚至简陋,我们需要多线程并发执行下载各自的部分,然后再汇总。

拆分

为了拆分,首先得知道数据块的大小,HTTP 报文首部提供了这样的信息:

  • 用 head 方法去获取 http 首部信息,再从获取的信息提取出 Content-Length 字段(上文图片大小为 261258 bytes)
import requests

headers = {'Range': 'bytes={}-{}'.format(0, 100000)}
r = requests.get('http://files.smashingmagazine.com/wallpapers/july-17/summer-cannonball/cal/july-17-summer-cannonball-cal-1920x1080.png', headers = headers)
with open('wallpaper.png', 'wb') as f:
    f.write(r.content)

我们得到了图片的前 100001 个字节(Range 的范围是包括起始和终止的),打开 wallpaper.png 你应该能看到一幅“半残”的图。

这样我们里目标更近了一步,继续:

  • 确认线程数(比如 8 个),261258//8 = 32657,前 7 个线程都取 32657 个 bytes,第八个取剩余的
part = size // nums

for i in range(nums):
        start = part * i
        if i == num_thread - 1:   # 最后一块
            end = file_size
        else:
            end = start + part
  • 每个线程获取到的内容按顺序写入文件(file.seek() 调节文件指针)
def down(start, end):
    headers = {'Range': 'bytes={}-{}'.format(start, end)}
    # 这里最好加上 stream=True,避免下载大文件出现问题
    r = requests.get(self.url, headers=headers, stream=True)
    with open(filename, "wb+") as fp:
        fp.seek(start)
        fp.write(r.content)

嘛,线程多了起来就扔到线程池让它来帮我们调度。

封装

功能复杂了,用对象来封装整理一下:

class Downloader(): 
    def __init__(self, url, num, name):
        self.url = url
        self.num = num
        self.name = name
        r = requests.head(self.url)
        self.size = int(r.headers['Content-Length']) 

    def down(self, start, end):

        headers = {'Range': 'bytes={}-{}'.format(start, end)}
        r = requests.get(self.url, headers=headers, stream=True)

        # 写入文件对应位置
        with open(self.name, "rb+") as f:
            f.seek(start)
            f.write(r.content)


    def run(self):
        f = open(self.name, "wb")
        f.truncate(self.size)
        f.close()

        futures = []
        part = self.size // self.num 
        pool = ThreadPoolExecutor(max_workers = self.num)                                    
        for i in range(self.num):
            start = part * i
            if i == self.num - 1:   
                end = self.size
            else:
                end = start + part - 1
            # 扔进线程池
            futures.append(pool.submit(self.down, start, end))
        wait(futures)

至此,核心功能都完成了,剩下的就是实际体验的优化了。

完整的代码已托管至 GitHub,地址见这里: https://github.com/WincerChan/DAM

结语

很可惜,我写的这个下载器还是不能下载百度云直链,不过嘛,好多人都说结果不重要,都说重要的是过程,不是么?写这个下载器我也确实学到了许多,至于一开始我是出于什么样的目的?管他呢

Python并行计算简单实现

multiprocessing包是Python中的多进程管理包.
Pool(num)类提供一个进程池,然后在多个核中执行这些进程,
其中默认参数num是当前机器CPU的核数.

Pool.map(func, iterable[, chunksize=None])
2个参数, 第一个参数是函数, 第二个参数是需要可迭代的变量, 作为参数传递到func

如果func含有的参数多于一个,可以利用functools.partial 先处理.
以下是一个简单的例子.

from multiprocessing import Pool
from functools import partial 

def somefunc(str_1, str_2, iterable_iterm):
    print("%s %s %d" % (str_1, str_2, iterable_iterm))

def main():
    iterable = [1, 2, 3, 4, 5]
    pool = Pool()
    str_1 = "This"
    str_2 = "is"
    func = partial(somefunc, str_1, str_2)
    pool.map(func, iterable)
    pool.close()
    pool.join()

if __name__ == "__main__":
    main()

python、virtualenv和virtualenvwrapper

简介

搭建不同版本的python环境,并且能够随意的创建、删除和管理,在不同的python环境下切换

安装环境

centos7.4、 VMware Workstation 14 Pro

安装步骤

1、查看现有的python环境

python -V

2、安装必要的工具

yum -y install zlib zlib-devel openssl-devel gcc

3、编译安装不同的python版本

1)安装python2.7.13

cd && wget https://www.python.org/ftp/python/2.7.13/Python-2.7.13.tgz

tar -zxvf Python-2.7.13.tgz

cd Python-2.7.13

./configure --prefix=/usr/local/python2.7 && make -j 4 && make install

2)安装python3.6.3

cd && wget https://www.python.org/ftp/python/3.6.3/Python-3.6.3.tgz

tar -zxvf Python-3.6.3.tgz

cd Python-3.6.3

./configure --prefix=/usr/local/python3.6 && make -j 4 && make install

4、安装配置virtualenv

easy_install pip 

(

如果没有 easy_install可下载安装 

wget https://bitbucket.org/pypa/setuptools/downloads/ez_setup.py && python ez_setup.py

)

pip install virtualenv

配置pip

cat << EOF  > /etc/pip.conf

[list]

format=columns

EOF

pip completion --bash >> ~/.bash_profile

5、安装配置virtualenvwrapper

pip install virtualenvwrapper

echo "export WORKON_HOME=~/test/venv" >>~/.bash_profile

source ~/.bash_profile

source virtualenvwrapper.sh

使用说明

1、创建python环境

mkvirtualenv -p /usr/local/python3.6/bin/python3.6 venv301

mkvirtualenv -p /usr/local/python2.7/bin/python2.7 venv201

2、查看python环境

lsvirtualenv

3、给所有python环境安装flask

allvirtualenv pip install flask

4、切换python环境

workon venv301

workon venv201

5、直接进入python环境的目录下

cdvirtualenv

6、退出python环境

deactivate

7、删除python环境

rmvirtualenv venv201

8、其它

钩子脚本,可自行查询相关内容

使用python3和flask构建RESTful API(接口测试服务)

引言

构建RESTful API貌似是开发的工作,和测试有和关系?

其实测试开发需要构建RESTful API的场景很多。比如测试Android应用,一般的接口测试只考虑了服务器端,至于客户端在网络异常或者服务端异常时如何反应,多数天朝的测试人员是没有考虑到的。客户端在对这些异常处理不够充分的时候,会出现崩溃等各种莫名其妙的问题。

为此一些走在前沿的测试人员会自己写一些RESTful API, 把服务端的域名劫持到自己的API,故意返回各种异常,看客户端的稳定性。

另外测试开发的测试工具需要和其他系统对接等场景也经常需要API。

术语

REST: REpresentational State Transfer

目标

  • GET – /api/Category – Retrieve all categories

  • POST – /api/Category – Add a new category

  • PUT – /api/Category – Update a category

  • DELETE – /api/Category – Delete a category

  • GET – /api/Comment – Retrieve all the stored comments

  • POST – /api/Comment – Add new comment

要求

  • python3.*
  • PostgreSQL

工程目录

project/
├── app.py
├── config.py
├── migrate.py
├── Model.py
├── requirements.txt
├── resources
│   └── Hello.py
│   └── Comment.py
│   └── Category.py
└── run.py

requirements.txt的内容如下:

flask
flask_restful
flask_script
flask_migrate
marshmallow
flask_sqlalchemy
flask_marshmallow
marshmallow-sqlalchemy
psycopg2
  • flask – Python的微框架

  • flask_restful – 这是Flask的扩展,可快速构建REST API。

  • flask_script – 提供了在Flask中编写外部脚本的支持。

  • flask_migrate – 使用Alembic的Flask应用进行SQLAlchemy数据库迁移。

  • marshmallow – ORM/ODM/框架无关的库,用于复杂数据类型(如对象)和Python数据类型转换。

  • flask_sqlalchemy – Flask扩展,增加了对SQLAlchemy的支持。

  • flask_marshmallow – 这是Flask和marshmallow的中间层。

  • marshmallow-sqlalchemy – 这是sqlalchemy和marshmallow的中间层。

  • psycopg – Python的PostgreSQL API。

安装依赖

# pip3 install -r requirements.txt

安装配置PostgreSQL

这里以 Ubuntu 16.04为例:

# sudo apt-get update && sudo apt-get upgrade
# apt-get install postgresql postgresql-contrib
# su - postgres
$ createdb api
$ createuser andrew --pwprompt #创建用户
$ psql -d api -c "ALTER USER andrew WITH PASSWORD 'api';"

参考资料:

https://linode.com/docs/databases/postgresql/how-to-install-postgresql-on-ubuntu-16-04/

https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-14-04

配置

# -*- coding: utf-8 -*-
# Author:    xurongzhong#126.com wechat:pythontesting qq:37391319
# CreateDate: 2018-1-10

from flask import Blueprint
from flask_restful import Api
from resources.Hello import Hello
from resources.Category import CategoryResource
from resources.Comment import CommentResource


api_bp = Blueprint('api', __name__)
api = Api(api_bp)

# Routes
api.add_resource(Hello, '/Hello')
api.add_resource(CategoryResource, '/Category')
api.add_resource(CommentResource, '/Comment')

快速入门

app.py

from flask import Blueprint
from flask_restful import Api
from resources.Hello import Hello

api_bp = Blueprint('api', __name__)
api = Api(api_bp)

# Route
api.add_resource(Hello, '/Hello')

resource/Hello.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Author:    xurongzhong#126.com wechat:pythontesting qq:37391319
# CreateDate: 2018-1-10

from flask_restful import Resource


class Hello(Resource):
    def get(self):
        return {"message": "Hello, World!"}

    def post(self):
        return {"message": "Hello, World!"}

run.py

from flask import Flask


def create_app(config_filename):
    app = Flask(__name__)
    app.config.from_object(config_filename)

    from app import api_bp
    app.register_blueprint(api_bp, url_prefix='/api')

    return app


if __name__ == "__main__":
    app = create_app("config")
    app.run(debug=True)

启动服务

$ python3 run.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 136-695-873

用浏览器访问: http://127.0.0.1:5000/api/Hello

{
    "hello": "world"
}

接入数据库

from flask import Flask
from marshmallow import Schema, fields, pre_load, validate
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy


ma = Marshmallow()
db = SQLAlchemy()


class Comment(db.Model):
    __tablename__ = 'comments'
    id = db.Column(db.Integer, primary_key=True)
    comment = db.Column(db.String(250), nullable=False)
    creation_date = db.Column(db.TIMESTAMP, server_default=db.func.current_timestamp(), nullable=False)
    category_id = db.Column(db.Integer, db.ForeignKey('categories.id', ondelete='CASCADE'), nullable=False)
    category = db.relationship('Category', backref=db.backref('comments', lazy='dynamic' ))

    def __init__(self, comment, category_id):
        self.comment = comment
        self.category_id = category_id


class Category(db.Model):
    __tablename__ = 'categories'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(150), unique=True, nullable=False)

    def __init__(self, name):
        self.name = name


class CategorySchema(ma.Schema):
    id = fields.Integer()
    name = fields.String(required=True)


class CommentSchema(ma.Schema):
    id = fields.Integer(dump_only=True)
    category_id = fields.Integer(required=True)
    comment = fields.String(required=True, validate=validate.Length(1))
    creation_date = fields.DateTime()

migrate.py

from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from Model import db
from run import create_app

app = create_app('config')

migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)


if __name__ == '__main__':
    manager.run()

数据迁移

$ python3 migrate.py db init
$ python3 migrate.py db migrate
$ python migrate.py db upgrade

修改Category.py 和Comment.py, 完整代码

测试

可以使用curl,比如:

curl http://127.0.0.1:5000/api/Category --data '{"name":"test5","id":5}' -H "Content-Type: application/json"

也可以在chrome中使用postman:

未分类

未分类

未分类

未分类

python编程(webpy + gunicorn + nginx部署)

之前虽然也用nginx + uwsgi + webpy的方法部署过网站,但是用了gunicorn之后,发现用这种方法部署网站更为简单。下面我详细描述一下如何用这种方法进行网站部署。

1、准备server.py

和uwsgi部署的时候一样,这里仅仅需要设置一个application就可以了。

#!/usr/bin/python
import web

urls = ('/', 'Hello')

class Hello(object):
    def GET(self):
        return 'Hello world'

app = web.application(urls, globals())  
application = app.wsgifunc()

2、安装gunicorn

安装gunicorn的方法非常简单,在ubuntu下面一条命令就可以解决,

sudo apt-get install gunicorn

3、用gunicorn启动server.py文件

启动的时候注意,最后一个选项是由文件名+wsgifunc组成的。同时,我们在选项中添加了gevent的属性,gunicorn本身支持gevent机制,可以有效提高server的性能。

gnicorn -b 127.0.0.1:8080 --worker-class gevent server:application

4、用浏览器做测试

这个时候不出意外,你已经可以用127.0.0.1:8080访问我们的网站了。

5、准备nginx.conf文件

通常为了利用nginx做static文件加速,或者利用nginx做均衡负载,我们常常需要另外安装一下nginx软件。因此,此时nginx.conf必须准备好。当然,为了简单起见,我们这里只做一个代理就可以了,整个conf文件内容如下,

worker_processes 1;

events{
    worker_connections 1024;
}

http{

    sendfile on;
    keepalive_timeout 65;

    server {
        listen       80;
        server_name  localhost;

        location / {

            proxy_pass http://127.0.0.1:8080;
        } 
    }

}

6、重启启动nginx

nginx.conf准备好后,这个时候先将它copy到/etc/nginx目录下。接下来,我们需要重新启动nginx软件,一个命令就可以了,

service nginx restart

7、用浏览器测试80端口

有了nginx做代理,这个时候就可以用浏览器访问127.0.0.1了,因为一般网站默认用80做端口,所以没有意外的话,这个时候你就可以看到webpy给出的打印消息了。

Python 动态生成变量名

如果你想生成v1,v2…v100这一百个变量,使用其他静态编译语言只能在代码中手动写出这100个变量名,但是在python中可以使用循环方便地动态生成。
python中有一个函数locals(),定义是:

locals(...)
    locals() -> dictionary

    Update and return a dictionary containing the current scope's local variables.

即返回当前作用域的所有变量
所以可以用这个函数来创建变量

代码:

for i in range(4):  
    name='v'+str(i)  
    locals()['v'+str(i)]=i  

print v1,v2,v3  

输出为1 2 3

配置搭建阿里云服务器nginx+uwsgi (python)

关于使用nginx+uwsgi搭建web服务器,网上有很多教程,但是对新手来说都有些不好理解。下面我总结了一下,纯基础、好使。

首先理解一些基本概念:

WSGI是什么?

WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。

WSGI 的官方定义是,the Python Web Server Gateway Interface。从名字就可以看出来,这东西是一个Gateway,也就是网关。网关的作用就是在协议之间进行转换。

uWSGI是什么?

uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http等协议。Nginx中HttpUwsgiModule的作用是与uWSGI服务器进行交换。

nginx、uwsgi、flask之间的关系

  • 首先nginx 是对外的服务接口,外部浏览器通过url访问nginx

  • nginx 接收到浏览器发送过来的http请求,将包进行解析,分析url,如果是静态文件请求就直接访问用户给nginx配置的静态文件目录,直接返回用户请求的静态文件。如果是动态那么nginx就将请求转发给uwsgi,通过flask框架转换为某一函数或者方法进行调用,并处理后,返回给uwsgi。uwsgi转发给nginx,nginx最终将返回结果返回给浏览器。

  • 由此可见nginx并不是必须的,uwsgi完全可以完成整个的和浏览器交互的流程。但是要考虑到某些情况。

  • 安全问题程序不能直接被浏览器访问到,而是通过nginx,nginx只开放某个接口,uwsgi本身是内网接口,这样运维人员在nginx上加上安全性的限制,可以达到保护程序的作用。

  • 负载均衡问题一个uwsgi很可能不够用,即使开了多个work也是不行,毕竟一台机器的cpu和内存都是有限的,有了nginx做代理,一个nginx可以代理多台uwsgi完成uwsgi的负载均衡。

  • 静态文件问题用django或是uwsgi这种东西来负责静态文件的处理是很浪费的行为,而且他们本身对文件的处理也不如nginx好,所以整个静态文件的处理都直接由nginx完成,静态文件的访问完全不去经过uwsgi以及其后面的东西。

好的接下来直接进入搭建过程。 首先你得有一台服务器。这个是我自己的阿里云esc服务器。然后通过ssh root@云服务器ip地址,然后输入密码进行登录。

安装 Python 环境

不多说,一般系统都自带python环境以及pip

安装Nginx

pip install nginx

或者

yum install nginx

安装VirtualEnv

不同的项目可能会引用各种不同的依赖包,为了避免版本与和应用之间的冲突而造成的“依赖地狱”。VirtualEnv 可以为每个Python应用创建独立的开发环境,使他们互不影响。

pip install virtualenv

安装VirtualEnv 后只需要在项目目录内运行 virtualenv 目录名 就可以建立一个虚拟环境文件夹。 假定我的项目目录叫 /home/www/myproj进入该目录后运行

virtualenv venv

项目目录下得到新的venv目录即虚拟环境 (虚拟环境目录可以叫venv,自己随意)

输入

source venv/bin/activate 

进入虚拟环境,前面多了虚拟目录名(venv)

安装 uWSGI

pip install uwsgi

安装 Flask

pip install flask

项目文件

假设到现在为止项目里面只有一个最简单的文件 hello.py,里面内容如下:

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Hello World!"

配置uwsgi

同志们,项目的准备工作准备的差不多了。接下来就是一些配置问题(哎,以前碰到的坑巨多)。

好,在项目目录下新建一个uwsgi.ini

touch uwsgi.ini

创建好了以后,用vim在uwsgi.ini写入以下内容:

[uwsgi]
master = true
home=/home/www/myproj/venv
wsgi-file = hello.py
callable = app
socket=127.0.0.1:5000
stats=127.0.0.1:9191
processes = 4
threads = 4
buffer-size=32768

运行uwsgi试试,如果输出类似以下信息,说明成功。

uwsgi --ini uwsgi.ini
(venv)my_flask root$ uwsgi uwsgi.ini

[uWSGI] getting INI configuration from uwsgi.ini

*** Starting uWSGI 2.0.8 (64bit) on [Tur Sep 19 14:34:11 2017] 
// 此处略去那些无用的启动信息
Stats server enabled on 127.0.0.1:9191 fd: 15 ***

ok,uwsgi已经配置完成,ctrl+c 关闭程序。

配置Nginx

找到配置文件nginx.conf(我的在目录/etc/nginx/nginx.conf),修改以下部分

server {
        charset      utf-8;
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  xxxx域名地址
        root         /home/www/myproj;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
           include      uwsgi_params;
           uwsgi_pass   127.0.0.1:5000;
           uwsgi_param UWSGI_PYHOME /home/www/myproj/venv;
           uwsgi_param UWSGI_CHDIR  /home/www/myproj;
        }
    }

启动Nginx服务器、启动uwsgi服务器

这个地方要注意,我在这卡了我1天。因为有俩个服务器,所以俩个都要启动。

service nginx restart

uwsgi --ini uwsgi.ini

大功告成

nginx+uwsgi+django部署python项目

初次部署nginx+uwsgi+django真是经历了千难万险,因此在这里整理分享一下,希望在这条路上行走的你有所帮助。

我使用的系统是fedora27,已经安装了python3和pip3,这两个没有安装的各位先自行安装。

Nginx

  • 安装
dnf install nginx
  • 配置
location / {
    uwsgi_pass  127.0.0.1:9090;
    include     uwsgi_params; # the uwsgi_params file you installed
}

nginx的配置只需要改这些

Django

  • 安装
dnf install setuptools
pip3 install django
pip3 install PyMySQL
pip3 install mysqlclient
#安装mysqlclient可能会遇到以下下问题,
dnf install mysql-devel # 解决 OSError: mysql_config not found
dnf install gcc # 解决 unable to execute 'gcc': No such file or directory
dnf install redhat-rpm-config # 解决gcc: 错误:/usr/lib/rpm/redhat/redhat-hardened-cc1:No such file or directory
#python-devel for python2.x
#python3-devel for python3.x
dnf install python3-devel 解决 致命错误:Python.h:No such file or directory
  • 启动
#在你的项目目录下
[jeffery@localhost pythontest]$ python3 manage.py runserver 0.0.0.0:8000
Performing system checks...

System check identified no issues (0 silenced).
November 30, 2017 - 08:11:58
Django version 1.11.7, using settings 'pythontest.settings'
Starting development server at http://0.0.0.0:8000/
Quit the server with CONTROL-C.

这样django就启动成功了,可以在浏览器中用8000端口访问一下项目

uwsgi

  • 安装
pip3 install uwsgi
  • django_socket.ini配置
[uwsgi]
socket = 127.0.0.1:9090
chdir = /projects/pythontest
module = pythontest.wsgi
processes = 2
pidfile = uwsgi.pid
daemonize = /var/log/uwsgi/uwsgi9090.log

chdir——你的项目根目录
module——入口文件,即wsgi.py相对于项目根目录的位置,“.”相当于一层目录。如果是用django创建的项目,wsgi.py这个文件是自动生成的,自己找一下。
daemonize——作为后台进程执行,日志输出的地方,目录没有的自己创建下

  • wsgi.py
import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pythontest.settings")

application = get_wsgi_application()
  • 启动
uwsgi --ini django_socket.ini

启动完我们就可以去上面daemonize 配置的日志中看看是否启动成功
Nginx中配置的地址端口要和django_socket.ini中保持一致
接下来用浏览器访问下Nginx,有人应该已经能够成功看到自己的python项目了

不行的请继续往下看

VirtualEnv

VirtualEnv的作用:创建隔离的Python环境,解决模块或库的版本冲突或依赖。(我的系统中既有python2.7又有python3.6)

  • 安装
pip3 install virtualenv
  • 创建虚拟环境
virtualenv mytest

mytest是我这边起的名字,可以随便起
创建不了的看下自己当前用户权限够不够, 不是root用户就到 /home目录下找你现在登录的角色的目录,然后进入用户目录下再执行上面的安装命令,执行完以后用ls看下有没有mytest这个文件夹,进入mytest的bin中

[jeffery@localhost /]$ cd /home/jeffery/mytest/bin/
[jeffery@localhost bin]$ ls
activate          django-admin      pip          python         wheel
activate.csh      django-admin.py   pip3         python3
activate.fish     easy_install      pip3.6       python3.6
activate_this.py  easy_install-3.6  __pycache__  python-config
  • 激活mytest虚拟环境
[jeffery@localhost bin]$ source ./activate
(mytest) [jeffery@localhost bin]$ 

此时我们看到命令行前面加上了(mytest),这代表我们在mytest的虚拟环境中,这个时候按照上面django的安装配置重新配置一次,最后在这个虚拟环境中启动django。(我们上面启动的可以不要了,虚拟环境中启动就可以了)

  • 修改django_socket.ini
[uwsgi]
socket = 127.0.0.1:9090
chdir = /projects/pythontest
module = pythontest.wsgi
processes = 2
pidfile = uwsgi.pid
daemonize = /var/log/uwsgi/uwsgi9090.log
home = /home/jeffery/mytest

这里面我们加上了最后一行,也就是配置home——虚拟环境mytest的根目录。

重新启动uwsgi,用浏览器访问Nginx,成功访问python项目。

python virtualenv虚拟环境介绍

最近折腾tensorflow的编译安装,重新用virtualenv, 发现生疏了,就简单记录下吧

在开发Python应用程序的时候,系统安装的Python3只有一个版本:3.4。所有第三方的包都会被pip安装到Python3的site-packages目录下。

如果我们要同时开发多个应用程序,那这些应用程序都会共用一个Python,就是安装在系统的Python 3。如果应用A需要jinja 2.7,而应用B需要jinja 2.6怎么办?

这种情况下,每个应用可能需要各自拥有一套“独立”的Python运行环境。virtualenv就是用来为一个应用创建一套“隔离”的Python运行环境。

首先,我们用pip安装virtualenv:

$ pip3 install virtualenv

然后,假定我们要开发一个新的项目,需要一套独立的Python运行环境,可以这么做:

第一步,创建目录:

Mac:~ $ mkdir myproject
Mac:~ $ cd myproject/
Mac:myproject michael$

第二步,创建一个独立的Python运行环境,命名为venv:

Mac:myproject  $ virtualenv --no-site-packages venv
Using base prefix '/usr/local/.../Python.framework/Versions/3.4'
New python executable in venv/bin/python3.4
Also creating executable in venv/bin/python
Installing setuptools, pip, wheel...done.

命令virtualenv就可以创建一个独立的Python运行环境,我们还加上了参数–no-site-packages,这样,已经安装到系统Python环境中的所有第三方包都不会复制过来,这样,我们就得到了一个不带任何第三方包的“干净”的Python运行环境。

新建的Python环境被放到当前目录下的venv目录。有了venv这个Python环境,可以用source进入该环境:

Mac:myproject michael$ source venv/bin/activate
(venv)Mac:myproject michael$

注意到命令提示符变了,有个(venv)前缀,表示当前环境是一个名为venv的Python环境。

下面正常安装各种第三方包,并运行python命令:

(venv)Mac:myproject michael$ pip install jinja2
...
Successfully installed jinja2-2.7.3 markupsafe-0.23
(venv)Mac:myproject michael$ python myapp.py
...

在venv环境下,用pip安装的包都被安装到venv这个环境下,系统Python环境不受任何影响。也就是说,venv环境是专门针对myproject这个应用创建的。

退出当前的venv环境,使用deactivate命令:

(venv)Mac:myproject michael$ deactivate 
Mac:myproject michael$

此时就回到了正常的环境,现在pip或python均是在系统Python环境下执行。

完全可以针对每个应用创建独立的Python运行环境,这样就可以对每个应用的Python环境进行隔离。

virtualenv是如何创建“独立”的Python运行环境的呢?原理很简单,就是把系统Python复制一份到virtualenv的环境,用命令source venv/bin/activate进入一个virtualenv环境时,virtualenv会修改相关环境变量,让命令python和pip均指向当前的virtualenv环境。

小结

virtualenv为应用提供了隔离的Python运行环境,解决了不同应用间多版本的冲突问题。

python下的redis连接

redis的概念

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

安装redis的客户端

pip install redis

redis的插入和读取操作

import redis
r=redis.Redis(host="192.168.1.202",port=6379)
#set操作,第一个参数是key,第二个参数时value
r.set("chao","I love you")
print(r.get("chao"))
#获取所有key 个数
print(len(r.keys()))

结果:
I love you
12

连接池
redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池

import redis
redis_config = {
    "host":"192.168.1.202",
    "port":6379
}
#r=redis.Redis(**redis_config)
def get_redis_conn():
    pool = redis.ConnectionPool(**redis_config)
    r=redis.Redis(connection_pool=pool)
    return r
if __name__ == '__main__':
    r =get_redis_conn()
    r.set("one","this is frist")
    print(r.get("one"))

结果:this is frist

管道

redis-py默认在执行每次请求都会创建(连接池申请连接)和断开(归还连接池)一次连接操作,如果想要在一次请求中指定多个命令,则可以使用pipline实现一次请求指定多个命令,并且默认情况下一次pipline 是原子性操作。减少功耗

import datetime
import redis
#利用管道批量set
def withpipe(r):
    pipe = r.pipeline(transaction=True)
    for i in xrange(1, 1000):
        key = "test1" +str(i)
        value = "test1" + str(i)
        pipe.set(key, value)
    pipe.execute()

#普通依次set
def withoutpipe(r):
    # pipe = r.pipeline(transaction=True)
    for i in xrange(1, 1000):
        key = "test1" + str(i)
        value = "test1" + str(i)
        r.set(key, value)

if __name__ == "__main__":
    pool = redis.ConnectionPool(host="192.168.1.202", port=6379, db=0)
    r1 = redis.Redis(connection_pool=pool)
    r2 = redis.Redis(connection_pool=pool)
    start = datetime.datetime.now()
    # print(start)
    withpipe(r1)
    end = datetime.datetime.now()
    # print((end-start).microseconds)
    # print(end-start)
    t_time = (end - start).microseconds
    print("withpipe time is : {0}".format(t_time))

    start = datetime.datetime.now()
    withoutpipe(r2)
    end = datetime.datetime.now()
    t_time = (end - start).microseconds
    print("withoutpipe time is : {0}".format(t_time))

结果:

withpipe time is : 28000 
withoutpipe time is : 253000 

可以发现用管道要比普通插入速度快。