Mac下安装Python虚拟环境Virtualenv

virtualenv官方文档对virtualenv的解释是:

virtualenv is a tool to create isolated Python environments.

virtualenv可以创建一个独立的 Python 环境,每个项目都可以有一个专属环境,避免了不同各种包安装冲突以及版本要求问题,可以让你更方便快捷的切换不同 Python 环境,更高效的开发。

pip是 Python 自带的包管理工具。

安装 virtualenv

$ sudo pip install virtualenv

测试virtualenv是否安装成功:

$ mkdir ~/Pyenv
$ cd ~/Pyenv
$ mkvirtualenv env1

安装 virtualenvwrapper

Virtaulenvwrapper是对virtualenv的封装,可以更方便地管理虚拟环境。

$ sudo easy_install virtualenvwrapper

第一次安装完成后需要,先设置WORKON_HOME,即环境的存储路径,并且运行source /usr/local/bin/virtualenvwrapper.sh

$ export WORKON_HOME=~/Pyenv
$ source /usr/local/bin/virtualenvwrapper.sh

把export命令和source命令加入到~/.bash_profile中,每次打开终端就无需初始化了。

$ vim ~/.bash_profile
$ export WORKON_HOME=~/Pyenv
$ source /usr/local/bin/virtualenvwrapper.sh

创建虚拟环境

$ mkvirtualenv env2

环境创建之后,会自动进入该目录,并激活该环境,当前路径前面就会有 (env2)。

列出虚拟环境:

$ lsvirtualenv -b
env1
env2

切换虚拟环境:

$ workon env1

查看环境里安装了哪些包:

$ lssitepackages

复制虚拟环境:

$ cpvirtualenv env1 env3
Copying env1 as env3...

退出虚拟环境:

$ deactivate

删除虚拟环境:

$ rmvirtualenv env2
Removing env2...

至此,Python虚拟环境Virtualenv安装流程完毕,你可以在你自己的虚拟环境下随意安装各种包,不同项目间也不会相互影响了。

使用flask来做一个小应用

提示:博主默认你已经具备了Python的基础知识,已经能够很顺畅的编写一些Python脚本,否则接下来你会比较难看懂。

旧版

这里先给出旧版本的一些使用截图,初始化的时候的样子

未分类

模糊查询

未分类

精确查询

未分类

技术

这个应用比较简单,所使用的技术也比较少,主要有以下技术要点

  • requests模拟请求
  • 正则匹配关键字
  • web.py搭建web环境
  • vue.js做数据自动绑定

是不是很简单?

Flask Web开发基于Python的Web应用开发实战pdf http://www.gooln.com/document/283768.html

在这个小应用中使用web.py的时候目录结构是这样的

未分类

其中static目录里面存放的是静态资源

未分类

结构相当简单

python代码

这里给出全部的Python代码

#!/usr/bin/env python
# coding=utf-8

import requests
import json
import web
import sys
import re

reload(sys)
sys.setdefaultencoding('utf8')

urls = (
    "/", "index",
    "/query", "Query"
)


render = web.template.render('static', cache=False)


class index:
    def GET(self):
        return render.index('static')


class Query:
    def POST(self):
        keywords = str(web.input().get('shopname'))
        url_base = "https://list.tmall.com/search_product.htm?q="+keywords
        headers = {"User-Agent": "iphone7"}

        try:
            result_base = requests.get(url=url_base, headers=headers, timeout=15).content.replace('n', '').replace(' ','')
            infostr = re.findall(r'j_shop_moreshop_more">(.+?)
', result_base) shoplist = [] for item in infostr: scorelist = re.findall(r'">(.+?)<iclass="', item) thisShopname = re.findall(r'(.+?)', item)[0] shoplist.append('{"shopname": "'+ thisShopname +'" , "dsr": "'+scorelist[0]+'", "service": "'+scorelist[1].split('">')[1]+'","ship": "'+scorelist[2].split('">')[1]+'"}') return json.dumps({"code": 0, "rows":list(set(shoplist))}) except Exception, e: print e return json.dumps({"code": -1, "msg": "没查询到相关店铺"}) if __name__ == "__main__": app = web.application(urls, globals()) app.run()

前端HTML代码

$def with (urlbase)

<html lang="zh-CN">

        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">



        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">

店铺:{{ item.shopname }}



描述相符:{{ item.dsr }}
服务态度:{{ item.service }}
物流服务:{{ item.ship }}
<script type="text/javascript" src="$urlbase/jquery.min.js"> <script type="text/javascript" src="$urlbase/vue.js"> <script type="text/javascript" src="$urlbase/index.js">

js代码

var mainVM = new Vue({
    el: 'body',
    data: {
        shopes:[
            {
                shopname:'未查询',
                dsr:'未查询',
                service:'未查询',
                ship:'未查询'
            }
        ]
    },
    methods:{
        query:function(){
            var _self = this,keyword = $('input[name="shopname"]').val();
            $.post('/query',{"shopname":keyword},function (data) {
                if(data.code == 0){
                    _self.shopes = [];

                    for(var k in data.rows){
                        var thisdata = JSON.parse(data.rows[k]);
                        _self.shopes.push({
                            shopname:thisdata.shopname,
                            dsr:thisdata.dsr,
                            service:thisdata.service,
                            ship:thisdata.ship
                        })
                    }
                }else{
                    alert('查询出错,错误信息:'+data.msg);
                }
            },"json");
        }
    }
});

可以说代码部分也是相当简单,前端HTML和js的代码就不解释了,很容易看懂,这里只对app.py做简单的解释。

观察天猫的搜索页面,发现天猫pc端跟手机端页面都可以轻松抓取,但是使用手机端页面会更加快速方便,因为结构上更加清晰,而且数据量少,抓取速度更快

如何实现只抓取手机端页面的数据呢?很简单,这里我们只需要定义以下HTTP的请求头信息就可以了,也就是headers,如下定义

headers={“User-Agent”:”iphone7″}

  
天猫的搜索链接是使用的get请求,地址为

”https://list.tmall.com/search_product.htm?q=”+keywords

参数只需要传入一个关键字就可以了,前端使用ajax把数据POST给服务端,服务端接收使用下面的这句话

keywords=str(web.input().get(‘shopname’))

是不是马上就搞定了关键的几步了?接下来发起请求拿到数据就可以了

result_base=requests.get(url=url_base,headers=headers,timeout=15).content.replace(‘n’,”).replace(”,”)

注意,这里我把返回的结果中的换行跟空格都去掉了,因为我这里所需要的数据很简单,为了匹配方便我直接给替换成可空,也就是后面的这个

.replace(‘n’,”).replace(”,”)

然后根据正则匹配的字符串进行遍历组合成结果返回给前端就好了,前端直接使用vue.js进行数据的绑定,几乎不需要DOM操作就可以完成结果列表的渲染,棒!(这里强行安利一波vue.js)

前后端通信使用json进行数据交互,友好而且方便。

重写

上面给出了所需要的技术要点和关键代码,那么现在我需要使用flask重写一遍,当然了,关键部分还是不用变动,只是处理方式上稍微有些差异,如果会用web.py,那么使用flask上手应该是很快的。

1、web.py的处理方式

在使用web.py的时候我们启动一个web服务很简单,通常执行以下命令

python app.py

这样我们就启动了一个web服务,但是这样的话会有很多问题,主要有以下几点

  • 不能关闭终端窗口,否则应用结束,一般用于调试
  • 多个应用的时候公用Python环境会引起冲突

注意:

web.py并不适合高并发的应用,但是作为一般应用还是可以轻松应对的。

以上命令执行后web.py会在8080端口绑定一个web服务,如果你想创建多个应用,那么你应该在后面加上端口号

如果你使用了多个域名指向一台机器的多个应用,那么你应该使用nginx来转发请求,而不是直接输入域名加端口号

在远程vps上运行开发完成的应用时,你可以执行以下命令把web以后台服务的形式运行

nohup python app.py 

这种方式简单粗暴,但是仅仅作为临时方案是可行的,运行上述命令后你可以安心的关掉终端,而且web服务依然在运行,但是一旦重启了服务器,那么就得重新登录vps再次执行命令,不是很方便。

2、flask的处理方式

flask和web.py类似,它自带了一个web服务器,默认绑定在5000端口,但是它本身自带的web服务器并不是很好,安全性也不高,作为开发使用还是足够的,正式生产环境中不太建议直接使用flask自带的web服务。

好了,现在可以开始了,为了解决上面提到几个问题,这里咱们来使用一个新东西,上面说了多应用环境冲突的问题,在这儿可以使用一个叫做“虚拟环境”的东西解决。

“虚拟环境”就是直接复制一个Python的全局环境,但是是独立出来的,你可以在这个环境里面安装各种模块,而且不会影响到Python的全局环境,也就是说如果你把其中的一个“虚拟环境”给玩坏了,起不来了,那么你只需要删掉坏的“虚拟环境”重新创建一个就可以了,这些操作都不会对Python全局环境有任何的影响,安全又方便,下面咱们就来创建一个“虚拟环境”。

博主使用的开发环境是Ubuntu 16.04 并没有自带这个软件,使用下面的命令安装

sudo apt-get install python-virtualenv -y

安装完之后测试下是否安装成功

~$ virtualenv --version
15.0.1

接下来咱们创建一个叫 tmall 虚拟环境用于运行我们的应用

~$ virtualenv tmall
Running virtualenv with interpreter /usr/bin/python2
New python executable in /home/kbdancer/tmall/bin/python2
Also creating executable in /home/kbdancer/tmall/bin/python
Installing setuptools, pkg_resources, pip, wheel...done.

创建的时候会给出创建的位置,如果你需要在指定的目录下面创建虚拟环境,那么你得切换到目标目录,然后执行创建命令,博主这里直接在自己的用户目录下面执行的创建命令,自然就是在用户目录下面生成的一个 tmall 文件夹,文件夹下面自动生成了Python环境

未分类

安装完之后需要将这个环境激活才能使用,执行下面的命令进行激活

~$ source tmall/bin/activate
(tmall) :~$

接着在虚拟环境中安装flask环境(博主默认你的Python全局环境中已经有了easy_install或者pip),博主这里使用pip进行安装

~$ pip install flask

好了,所需要的环境配置完成,接下来就可以开始写小应用了。

3、开始编码

编码这个环节应该是快速而且高效的,上面我们已经给出了旧代码,关键部分直接复制过来就能用,稍微改改就可以跑起来了。

flask默认使用Jinja2作为模板引擎,Jinja2在进行模板渲染的时候通常会识别{{}}中的内容进行填充,但是这里博主遇到了一个尴尬的问题,Vue.js也是使用的{{}}作为标识符进行渲染,这就导致了冲突,访问页面的时候就会出现如图所示的错误

未分类

当然,解决方法还是有的,参考这篇文章进行配置 解决Jinja2与Vue.js的模板冲突 解决思路也比较简单,就是在需要Jinja2渲染的时候添加一个空格,而vue.js渲染的时候则不需要空格,python脚本如下

from flask import Flask, render_template

app = Flask(__name__)
app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}'

前端HTML代码修改后就成了这样

<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Hello world</title>
    </head>
    <body>
        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">
            <p>店铺:{{item.shopname}}</p>
            <p>描述相符:{{item.dsr}}<br>服务态度:{{item.service}}<br>物流服务:{{item.ship}}</p>
        </div>
        <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='vue.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script>
    </body>
</html>

Jinja2默认会在templates目录下面寻找模板文件,而静态文件比如css,js之类的默认存储在static目录下面,这里我们按照Jinja2的默认设置稍微进行修改,当然,如果你想自定义模板目录或者静态文件的目录也是可以的,只需要稍微的配置下就行了,博主这里按照默认的规则来设置。

很快,我们的小应用就跑起来了

未分类

这里还是需要提到几个关键点:

flask中接收前端传递过来的参数用到的是request对象,前端使用json把数据post到后端,后端使用下面这句进行接收

request.form.get('shopname')

更多详细使用方法参考这个地址 http://www.letiantian.me/2014-06-24-flask-process-post-data/ 接着测试下小应用能不能正常运行

未分类

未分类

OK,测试通过。

4、关于部署

由于这个小应用比较简单,部署起来可以按照常规的部署方式进行,但是并不适合生产环境,所以这里暂时不写如何部署,下次有大型网站案例的时候再详细写如何部署以及优化。

5、完整代码

python部分

#!/usr/bin/env python
# coding=utf-8

from flask import Flask, render_template, request
import requests
import json
import re

app = Flask(__name__)
app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}'


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/query', methods=['POST'])
def query():
    keywords = request.form.get('shopname')
    url_base = "https://list.tmall.com/search_product.htm?q=" + keywords
    headers = {"User-Agent": "iphone7"}

    try:
        result_base = requests.get(url=url_base, headers=headers, timeout=15).content.replace('n', '').replace(' ', '')
        infostr = re.findall(r'j_shop_moreshop_more">(.+?)
', result_base) shoplist = [] for item in infostr: scorelist = re.findall(r'">(.+?)<iclass="', item) thisShopname = re.findall(r'(.+?)', item)[0] shoplist.append('{"shopname": "' + thisShopname + '" , "dsr": "' + scorelist[0] + '", "service": "' + scorelist[1].split('">')[1] + '","ship": "' + scorelist[2].split('">')[1] + '"}') return json.dumps({"code": 0, "rows": list(set(shoplist))}) except Exception, e: print e return json.dumps({"code": -1, "msg": "没查询到相关店铺"}) if __name__ == "__main__": app.run(debug=True)

HTML部分

<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="renderer" content="webkit">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Hello world</title>
    </head>
    <body>
        <input type="text" name="shopname">
        <input type="button" value="提交" @click="query">
        <div class="info" v-for="item in shopes" style="border-bottom: #ccc 1px dashed">
            <p>店铺:{{item.shopname}}</p>
            <p>描述相符:{{item.dsr}}<br>服务态度:{{item.service}}<br>物流服务:{{item.ship}}</p>
        </div>
        <script type="text/javascript" src="{{ url_for('static', filename='jquery.min.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='vue.js') }}"></script>
        <script type="text/javascript" src="{{ url_for('static', filename='index.js') }}"></script>
    </body>
</html>

JS部分

没有做任何改动,就不贴出来了

总结

写这篇文章的目的一来是复习下flask的一些知识,二来是与web.py做个对比,再者就是给入门的朋友提供一个实战的例子,方便参考。

Flask 中模块化应用的实现

Flask是一个轻量级的Web框架。虽然是轻量级的,但是对于组件一个大型的、模块化应用也是能够实现的,“蓝图”就是这样一种实现。对于模块化应用的实现,在Flask 0.2版本中进行了设计。本文暂时不对“蓝图”做详细的介绍,而是先从0.2版本中的Module类的实现讲起。其实,“蓝图”的实现和Module类的实现很相似。

为什么实现模块化应用

对于大型应用而言,随着功能的不断增加,整个应用的规模也会扩大。按照一定的规则将应用的不同部分进行模块化,不仅能够使整个应用逻辑清晰,也易于维护。例如,在Flask中,你也许想像如下构建一个简单的项目:

/myapplication
    /__init__.py
    /views
        /__init__.py
        /admin.py
        /frontend.py

以上目录结构中,我们将之前的Flask单文件修改成了一个应用包,所有的视图函数都在views下,并且按照功能分为了admin和frontend两个部分。为了实现这种模块化应用的构建,在0.2版本中Flask实现了Module类。这个类实例可以通过注册的方式,在Flask应用创建后添加进应用。

Module类实现了一系列的方法:

  • route(rule, **options)

  • add_url_rule(rule, endpoint, view_func=None, **options)

  • before_request(f)

  • before_app_request(f)

  • after_request(f)

  • after_app_request(f)

  • context_processor(f)

  • app_context_processor(f)

  • _record(func)

以上方法除了add_url_rule和_record外,都可以作为装饰器在自己的模块中使用,这些装饰器都返回一个函数。通过调用_record方法,可以将装饰器返回的函数放到_register_events中。当Flask应用创建之后,通过运行_register_events列表中的函数,可以将这个模块注册到应用中去。

Flask应用怎么注册一个Module

以下我们以一个例子来说明Flask应用怎么注册一个Module。

1. 项目结构

这个简单的例子项目结构如下:

/myapplication
    /__init__.py
    /app.py
    /views
        /__init__.py
        /admin.py
        /blog.py

admin.py和blog.py两个模块的代码如下:

# admin.py
from flask import Module

admin = Module(__name__)

@admin.route('/')
def index():
    return "This is admin page!"

@admin.route('/profile')
def profile():
    return "This is profile page."
# blog.py
from flask import Module
blog = Module(__name__)
@blog.route('/')
def index():
    return "This is my blog!"
@blog.route('/article/<int:id>')
def article(id):
    return "The article id is %d." % id

以上两个模块中,我们首先分别创建了一个Module类,然后像写一般的视图函数一样,为每个模块增加一些规则。之后,可以在创建Flask应用的时候将这些模块引入,就可以注册了。

# app.py
from flask import Flask
from views.admin import admin
from views.blog import blog
app = Flask(__name__)
@app.route('/')
def index():
    return "This is my app."
app.register_module(blog, url_prefix='/blog')
app.register_module(admin, url_prefix='/admin')
if __name__ == '__main__':
    from werkzeug.serving import run_simple
    run_simple('localhost', 5000, app)

在app.py中:

  • 我们首先引入了admin和blog两个Module对象;

  • 之后,我们创建了一个Flask应用app,并且为这个应用增加了一个视图函数;

  • 为了注册模块,我们调用了应用的register_module方法;

  • 最后,从werkzeug.serving中我们调用run_simple方法,用来创建一个本地的服务器用于测试这个Flask应用。

根据以上的步骤,我们就可以测试这个应用。分别以/blog和/admin为URL前缀,就可以访问blog和admin两个模块了。

2. 注册Module时发生了什么

根据上面的例子,只要简单的调用Flask应用的register_module方法,就可以注册一个Module了。关于register_module方法的代码如下:

def register_module(self, module, **options):
    """Registers a module with this application.  The keyword argument
    of this function are the same as the ones for the constructor of the
    :class:`Module` class and will override the values of the module if
    provided.
    """
    options.setdefault('url_prefix', module.url_prefix)
    state = _ModuleSetupState(self, **options)
    for func in module._register_events:
        func(state)

通过以上代码可以发现:

  • 可以通过增加url_prefix来区分不同的Module,这在app注册admin和blog时我们已经看到了;

  • 在注册时,我们创建了一个_ModuleSetupState的类,这个类接收Flask应用和一些参数生成一个state实例。这个实例反映了当前Flask应用的状态。

  • 前面在讲到Module类的时候,我们讲到Module未注册时会将自己模块的一些功能实现都放在_register_events列表中,这些功能实现都是函数形式。当需要将模块注册到某一应用上时,只需要传递关于这个应用信息的参数即可,即就是上面的state实例。这样,通过运行函数,可以讲一些属性绑定到当前应用上去。

以上面例子中不同模块的URL绑定来讲,通过注册,应用app现形成了如下的URL“地图”:

>>> app.url_map
Map([<Rule '/admin/profile' (HEAD, GET) -> admin.profile>,
     <Rule '/admin/' (HEAD, GET) -> admin.index>,
     <Rule '/blog/' (HEAD, GET) -> blog.index>,
     <Rule '/' (HEAD, GET) -> index>,
     <Rule '/blog/article/<id>' (HEAD, GET) -> blog.article>,
     <Rule '/static/<filename>' (HEAD, GET) -> static>]
    )
>>> app.url_map._rules_by_endpoint
{'admin.index': [<Rule '/admin/' (HEAD, GET) -> admin.index>],
'admin.profile': [<Rule '/admin/profile' (HEAD, GET) -> admin.profile>],
'blog.article': [<Rule '/blog/article/<id>' (HEAD, GET) -> blog.article>],
'blog.index': [<Rule '/blog/' (HEAD, GET) -> blog.index>],
'index': [<Rule '/' (HEAD, GET) -> index>],
'static': [<Rule '/static/<filename>' (HEAD, GET) -> static>]
}
>>> app.view_functions
{'admin.index': <function views.admin.index>,
'admin.profile': <function views.admin.profile>,
'blog.article': <function views.blog.article>,
'blog.index': <function views.blog.index>,
'index': <function __main__.index>
}

这样,就可以把不同模块的URL规则放在一起,并在endpoint和视图函数之间形成对应关系。

用supervisor+nginx部署服务的流程

以chat_service为例:

1、将项目拷贝至某一路径

2、更改supervisor配置文件:supervisor.conf(一般在/etc/目录下)

  • 在/etc/supervisor.d/目录下新建chat_service.conf配置文件,如下:
[program:chat_service]
command=/usr/local/bin/gunicorn -b 0.0.0.0:8001 -w 40 -k gevent -t 300 chat_service.wsgi:application
directory=/home/hongkeyuan/chat/bin/chat_system/chat_service
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/home/hongkeyuan/chat/log/chat_service.log
stderr_logfile=/home/hongkeyuan/chat/log/chat_service.err
  • 将配置文件包含到supervisor.conf中,在supervisor.conf中加入:
[include]
files = /etc/supervisor.d/*.conf

3、更改nginx配置文件,/etc/nginx/conf.d/目录下新建chat_service.conf配置文件,如下:

upstream chat_service {
        server localhost:8001;
}

server {
        listen 8000;
        location /static {
                alias /home/robot/chat/bin/chat_system/chat_service/static;
        }
        location / {
                proxy_pass_header Server;
                proxy_set_header Host $http_host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Scheme $scheme;
                proxy_pass http://chat_service;
        }
}

4、重新加载supervisor:supervisorctl -c /etc/supervisor.conf reload,此时配置的服务也将重启。

5、重新加载nginx:nginx -s reload。

tips:

  • 如果需要sudo权限,相关命令前需要加sudo。
  • 这里nginx主要起重定向作用,如果不需要重定向,则无需配置nginx。

Supervisor安装与配置(Linux/Unix进程管理工具)

Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。

因为Supervisor是Python开发的,安装前先检查一下系统否安装了Python2.4以上版本。下面以CentOS7,Python2.7版本环境下,介绍Supervisor的安装与配置步聚:

一、安装Python包管理工具(easy_install)

easy_install是setuptools包里带的一个命令,使用easy_install实际上是在调用setuptools来完成安装模块的工作,所以安装setuptools即可。

wget --no-check-certificate https://bootstrap.pypa.io/ez_setup.py -O - | sudo python

二、安装supervisor

easy_install supervisor

supervisor安装完成后会生成三个执行程序:supervisortd、supervisorctl、echo_supervisord_conf,分别是supervisor的守护进程服务(用于接收进程管理命令)、客户端(用于和守护进程通信,发送管理进程的指令)、生成初始配置文件程序。

三、配置

运行supervisord服务的时候,需要指定supervisor配置文件,如果没有显示指定,默认在以下目录查找:

$CWD/supervisord.conf
$CWD/etc/supervisord.conf
/etc/supervisord.conf
/etc/supervisor/supervisord.conf (since Supervisor 3.3.0)
../etc/supervisord.conf (Relative to the executable)
../supervisord.conf (Relative to the executable)

$CWD表示运行supervisord程序的目录。

可以通过运行echo_supervisord_conf程序生成supervisor的初始化配置文件,如下所示:

mkdir /etc/supervisor
echo_supervisord_conf > /etc/supervisor/supervisord.conf

四、配置文件参数说明

supervisor的配置参数较多,下面介绍一下常用的参数配置,详细的配置及说明,请参考官方文档介绍。

注:分号(;)开头的配置表示注释

[unix_http_server]
file=/tmp/supervisor.sock   ;UNIX socket 文件,supervisorctl 会使用
;chmod=0700                 ;socket文件的mode,默认是0700
;chown=nobody:nogroup       ;socket文件的owner,格式:uid:gid

;[inet_http_server]         ;HTTP服务器,提供web管理界面
;port=127.0.0.1:9001        ;Web管理后台运行的IP和端口,如果开放到公网,需要注意安全性
;username=user              ;登录管理后台的用户名
;password=123               ;登录管理后台的密码

[supervisord]
logfile=/tmp/supervisord.log ;日志文件,默认是 $CWD/supervisord.log
logfile_maxbytes=50MB        ;日志文件大小,超出会rotate,默认 50MB,如果设成0,表示不限制大小
logfile_backups=10           ;日志文件保留备份数量默认10,设为0表示不备份
loglevel=info                ;日志级别,默认info,其它: debug,warn,trace
pidfile=/tmp/supervisord.pid ;pid 文件
nodaemon=false               ;是否在前台启动,默认是false,即以 daemon 的方式启动
minfds=1024                  ;可以打开的文件描述符的最小值,默认 1024
minprocs=200                 ;可以打开的进程数的最小值,默认 200

[supervisorctl]
serverurl=unix:///tmp/supervisor.sock ;通过UNIX socket连接supervisord,路径与unix_http_server部分的file一致
;serverurl=http://127.0.0.1:9001 ; 通过HTTP的方式连接supervisord

; [program:xx]是被管理的进程配置参数,xx是进程的名称
[program:xx]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run  ; 程序启动命令
autostart=true       ; 在supervisord启动的时候也自动启动
startsecs=10         ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
autorestart=true     ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
startretries=3       ; 启动失败自动重试次数,默认是3
user=tomcat          ; 用哪个用户启动进程,默认是root
priority=999         ; 进程启动优先级,默认999,值小的优先启动
redirect_stderr=true ; 把stderr重定向到stdout,默认false
stdout_logfile_maxbytes=20MB  ; stdout 日志文件大小,默认50MB
stdout_logfile_backups = 20   ; stdout 日志文件备份数,默认是10
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
stopasgroup=false     ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=false     ;默认为false,向进程组发送kill信号,包括子进程

;包含其它配置文件
[include]
files = relative/directory/*.ini    ;可以指定一个或多个以.ini结束的配置文件

include示例:

[include]
files = /opt/absolute/filename.ini /opt/absolute/*.ini foo.conf config??.ini

五、配置管理进程

进程管理配置参数,不建议全都写在supervisord.conf文件中,应该每个进程写一个配置文件放在include指定的目录下包含进supervisord.conf文件中。

1> 创建/etc/supervisor/config.d目录,用于存放进程管理的配置文件
2> 修改/etc/supervisor/supervisord.conf中的include参数,将/etc/supervisor/conf.d目录添加到include中

[include]
files = /etc/supervisor/config.d/*.ini

未分类

supervisor配置文件目录结构

下面是配置Tomcat进程的一个例子:

[program:tomcat]
command=/opt/apache-tomcat-8.0.35/bin/catalina.sh run
stdout_logfile=/opt/apache-tomcat-8.0.35/logs/catalina.out
autostart=true
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true

六、启动Supervisor服务

supervisord -c /etc/supervisor/supervisord.conf

七、控制进程

7.1 交互终端

supervisord启动成功后,可以通过supervisorctl客户端控制进程,启动、停止、重启。运行supervisorctl命令,不加参数,会进入supervisor客户端的交互终端,并会列出当前所管理的所有进程。

未分类

上图中的tomcat就是我们在配置文件中[program:tomcat]指定的名字。
输入help可以查看可以执行的命令列表,如果想看某个命令的作用,运行help 命令名称,如:help stop

stop tomcat  // 表示停止tomcat进程
stop all     // 表示停止所有进程
// ...

7.2 bash终端

supervisorctl status
supervisorctl stop tomcat
supervisorctl start tomcat
supervisorctl restart tomcat
supervisorctl reread
supervisorctl update

7.3 Web管理界面

未分类

出于安全考虑,默认配置是没有开启web管理界面,需要修改supervisord.conf配置文件打开http访权限,将下面的配置:

;[inet_http_server]         ; inet (TCP) server disabled by default
;port=127.0.0.1:9001        ; (ip_address:port specifier, *:port for all iface)
;username=user              ; (default is no username (open server))
;password=123               ; (default is no password (open server))

修改成:

[inet_http_server]         ; inet (TCP) server disabled by default
port=0.0.0.0:9001          ; (ip_address:port specifier, *:port for all iface)
username=user              ; (default is no username (open server))
password=123               ; (default is no password (open server))
  • port:绑定访问IP和端口,这里是绑定的是本地IP和9001端口
  • username:登录管理后台的用户名
  • password:登录管理后台的密码

八、开机启动Supervisor服务

8.1 配置systemctl服务

1> 进入/lib/systemd/system目录,并创建supervisor.service文件

[Unit]
Description=supervisor
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/supervisord -c /etc/supervisor/supervisord.conf
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s

[Install]
WantedBy=multi-user.target

2> 设置开机启动

systemctl enable supervisor.service
systemctl daemon-reload

3> 修改文件权限为766

chmod 766 supervisor.service

8.2 配置service类型服务

#!/bin/bash
#
# supervisord   This scripts turns supervisord on
#
# Author:       Mike McGrath <[email protected]> (based off yumupdatesd)
#
# chkconfig:    - 95 04
#
# description:  supervisor is a process control utility.  It has a web based
#               xmlrpc interface as well as a few other nifty features.
# processname:  supervisord
# config: /etc/supervisor/supervisord.conf
# pidfile: /var/run/supervisord.pid
#

# source function library
. /etc/rc.d/init.d/functions

RETVAL=0

start() {
    echo -n $"Starting supervisord: "
    daemon "supervisord -c /etc/supervisor/supervisord.conf "
    RETVAL=$?
    echo
    [ $RETVAL -eq 0 ] && touch /var/lock/subsys/supervisord
}

stop() {
    echo -n $"Stopping supervisord: "
    killproc supervisord
    echo
    [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/supervisord
}

restart() {
    stop
    start
}

case "$1" in
  start)
    start
    ;;
  stop) 
    stop
    ;;
  restart|force-reload|reload)
    restart
    ;;
  condrestart)
    [ -f /var/lock/subsys/supervisord ] && restart
    ;;
  status)
    status supervisord
    RETVAL=$?
    ;;
  *)
    echo $"Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}"
    exit 1
esac

exit $RETVAL

将上述脚本内容保存到/etc/rc.d/init.d/supervisor文件中,修改文件权限为755,并设置开机启动

chmod 755 /etc/rc.d/init.d/supervisor
chkconfig supervisor on

注意:修改脚本中supervisor配置文件路径为你的supervisor的配置文件路径

其它Linux发行版开机启动脚本:https://github.com/Supervisor/initscripts

注意:

Supervisor只能管理非daemon的进程,也就是说Supervisor不能管理守护进程。否则提示Exited too quickly (process log may have details)异常。例子中的Tomcat默认是以守护进程启动的,所以我们改成了catalina.sh run,以前台进程的方式运行。

yum方式安装

yum install epel-release
yum install -y supervisor

supervisor没有发布在标准的CentOS源在,需要安装epel源。这种方式安装的可能不是最新版本,但比较方便,安装完成之后,配置文件会自动帮你生成。

默认配置文件:/etc/supervisord.conf
进程管理配置文件放到:/etc/supervisord.d/目录下即可

默认日志文件:/tmp/supervisord.log,可以查看进程的启动信息

laravel 守护进程Supervisor的配置

安装Supervisor

Supervisor是Linux系统中常用的进程守护程序。如果队列进程queue:work意外关闭,它会自动重启启动队列进程。在Ubuntu安装Supervisor 非常简单:

sudo apt-get install supervisor

注:如果自己配置Supervisor有困难,可以考虑使用Laravel Forge,它会为Laravel项目自动安装并配置Supervisor。

配置Supervisor

Supervisor配置文件通常存放在/etc/supervisor/conf.d目录,在该目录中,可以创建多个配置文件指示Supervisor如何监视进程,例如,让我们创建一个开启并监视queue:work进程的laravel-worker.conf文件:

[program:laravel-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3
autostart=true
autorestart=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log

在本例中,numprocs指令让Supervisor运行8个queue:work进程并监视它们,如果失败的话自动重启。配置文件创建好了之后,可以使用如下命令更新Supervisor配置并开启进程:

启动Supervisor

当你成功创建配置文件后,你需要刷新Supervisor 的配置信息:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start laravel-worker:*

你可以通过Supervisor官方文档获的更多信息: http://supervisord.org/index.html

python利用flask,gunicorn部署接口全过程

背景

无论开发 Android 还是 Web 端程序,有时候不免需要一些接口供自己调用进行测试,这里记录一下详细的过程。

环境配置

1、阿里云的 9.9 的学生特惠机, Ubuntu 14.04 64位

未分类

2、利用 putty 输入公网 ip 登录到服务器

先配置编码为 utf-8

未分类

未分类

输入账号密码

未分类

3、要利用 apt-get 包管理工具下载各种包,所以先更新下源,随手把 git 也安一下,安装中途输入 Y 即可

apt-get update
apt-get install git

未分类

Flask [flɑ:sk]

Flask是一个使用Python编写的轻量级Web应用框架。基于Werkzeug WSGI工具箱和Jinja2 模板引擎。 Flask 官方教程: http://docs.jinkan.org/docs/flask/index.html

我已经在本地写了一个简单的 Flask ,并且传到了 Coding

#coding=utf-8
from flask import Flask,jsonify,request
app = Flask(__name__) #获得 Flask 实例
 #写一个 POST 的方法,传一个 name 的参数,返回一个  json 数据
@app.route('/getJson', methods=['POST'])
def getJson():
    #bala bala,下边随便返回点东西
    name=request.form.get('name', default='windliang')
    data={
    'name':name,
    'place':'wuhan'
    }
    msg='get success'
    code=True
    return getBaseReturnValue(data,msg,code)
#返回 json 数据    
def getBaseReturnValue(data,msg,code):
  json_data = jsonify({'data':data,'msg':msg,'success':code})
  return json_data

if __name__ == '__main__':
    app.run(host='0.0.0.0') #在服务器上运行

依次执行下边的指令,‘//’后边的仅做注释不用复制

git clone https://git.coding.net/wind_liang/flasktest.git  //将 coding 上的代码克隆到本地
pip install virtualenv  //python 的一个创建隔离环境的工具
mkdir api  //创建一个新目录 api,virtualenv 直接用 clone 下的目录会出问题,原因不清楚
cp -i flasktest/api.py api  //将 coding 下载下来的 api.py 复制到新目录 api 中
cd api   // 进入 api 目录
virtualenv venv // 创建虚拟环境安包的文件夹
source venv/bin/activate //激活虚拟环境 ,这时会看到在最前边有 (venv) 标识 ,以后进入前都得先激活环境
pip install flask // 安装 flask 包
pip freeze > requirements.txt  // 将安装的包列表写到 reqirements.txt 中,
以后每次 pip 安装了新的库的时候,都需freeze 一次,
既能知道自己安装了什么库,也方便别人部署时,安装相应的库。
python api.py  // 运行程序 
ctrl+C 用于终止当前 python 程序
deactivate 用于关闭当前虚拟环境 ,先不用执行此句

virtualenv 是一个将不同项目所需求的依赖分别放在独立的地方的一个工具,它给这些工程创建虚拟的Python环境。它解决了“项目X依赖于版本1.x,而项目Y需要项目4.x”的两难问题,而且使你的全局site-packages目录保持干净和可管理。官方教程

阿里云还得设定开放 5000端口

未分类

未分类

此时利用 postman (谷歌浏览器的一个插件),或者 curl 等其他能发 post 请求的工具进行测试

未分类

未分类

ctrl+C 终止程序,进入下一步

gunicorn

Gunicorn是一个Python WSGI UNIX的HTTP服务器。这是一个预先叉工人模式,从Ruby的独角兽(Unicorn)项目移植。该Gunicorn服务器与各种Web框架兼容,我们只要简单配置执行,轻量级的资源消耗,以及相当迅速。现在我们使用 flask 自带的服务器,完成了 web 服务的启动。生产环境下,flask 自带的服务器,无法满足性能要求。我们这里采用 gunicorn 做 wsgi容器,用来部署 python

pip install gunicorn  //安装 gunicorn
pip freeze > requirements.txt //保存到当前安装的包的列表中
gunicorn -w4 -b0.0.0.0:8000 api:app
此时,我们需要用 8000 的端口进行访问,
原先的5000并没有启用。
其中 gunicorn 的部署中,
-w 表示开启多少个 worker,-b
表示 gunicorn  的访问地址 ,
api 是程序进入的文件名, 
app 是代码中获得的 flask 实例
进入阿里云记得开放8000端口

未分类

未分类

ctrl + c 终止当前程序

微信公众号服务器端 Flask 源码

python 2.7

# -*- coding: utf-8 -*-
# filename: main.py
from flask import Flask
import hashlib
import time
from flask import Flask,g,request,make_response
import xml.etree.ElementTree as ET
import requests 
import re
import os
from bs4 import BeautifulSoup
import warnings
app = Flask(__name__)
BASE_DIR = os.path.dirname(__file__) #获取当前文件夹的绝对路径
warnings.filterwarnings("ignore") #忽略警告
@app.route("/wx",methods=["GET","POST"])
def wx():
    if request.method == "GET":       # 判断请求方式是GET请求
        my_signature = request.args.get('signature')     # 获取携带的signature参数
        my_timestamp = request.args.get('timestamp')     # 获取携带的timestamp参数
        my_nonce = request.args.get('nonce')        # 获取携带的nonce参数
        my_echostr = request.args.get('echostr')         # 获取携带的echostr参数
        token = 'helloworld'     # 一定要跟微信端填写的token一致
        # 进行字典排序
        data = [token,my_timestamp ,my_nonce ]
        data.sort()
        # 拼接成字符串
        try:
            temp = ''.join(data)
        except:
            return "success"
        # 进行sha1加密
        mysignature = hashlib.sha1(temp).hexdigest()
        # 加密后的字符串可与signature对比,标识该请求来源于微信
        if my_signature == mysignature:
            return my_echostr
    else:
        rec = request.stream.read()
        xml_rec = ET.fromstring(rec)
        msgType=xml_rec.find("MsgType").text 
        if msgType != 'text': #只对文字进行回复
            return "success"
        tou = xml_rec.find('ToUserName').text
        fromu = xml_rec.find('FromUserName').text
        content = xml_rec.find('Content').text
        cc=content.encode('UTF-8') # 用户发送的文字
        t="狼吃羊"
        if(cc==t):
            content="http://windliang.oschina.io/worfeatsheep/"
        else:
            return "success"
        xml_rep = "<xml><ToUserName><![CDATA[%s]]></ToUserName><FromUserName><![CDATA[%s]]></FromUserName><CreateTime>%s</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[%s]]></Content><FuncFlag>0</FuncFlag></xml>"
        response = make_response(xml_rep % (fromu,tou,str(int(time.time())), content))
        response.content_type='application/xml'
        return response

@app.route("/",methods=["GET"])
def index():
    return "hello,world"

if __name__ == '__main__':
    app.run(host='0.0.0.0',debug='true')

Mysql select in 按id排序实现方法

表结构如下:

mysql> select * from test; 
+----+-------+ 

| id | name | 

+----+-------+ 

| 1 | test1 | 

| 2 | test2 | 

| 3 | test3 | 

| 4 | test4 | 

| 5 | test5 | 

+----+-------+ 

执行以下SQL:

mysql> select * from test where id in(3,1,5); 
+----+-------+ 

| id | name | 

+----+-------+ 

| 1 | test1 | 

| 3 | test3 | 

| 5 | test5 | 

+----+-------+ 

3 rows in set (0.00 sec) 

这个select在mysql中得结果会自动按照id升序排列,

但是我想执行”select * from test where id in(3,1,5);”的结果按照in中得条件排序,即:3,1,5,

想得到的结果如下:

id name 

3 test3 

1 test1 

5 test5 

请问在这样的SQL在Mysql中怎么写?

网上查到sqlserver中可以用order by charindex解决,但是没看到Mysql怎么解决??请高手帮忙,谢

谢!

select * from a order by substring_index('3,1,2',id,1); 

试下这个good,ls正解。

order by find_in_set(id,'3,1,5') 

谢谢,经测试order by substring_index和order by find_in_set都可以 。