import os
import json
from flask import Flask
def create_app():
app = Flask('test')
# 这里在虚拟环境中设置环境变量。 export RMON_CONFIG=xxx.json
file = os.environ.get('RMON_CONFIG')
content = ''
if file:
rest = {}
with open(file) as f:
for line in f:
# if line.strip().startswith('#'):
if "#" in line:
continue
content += line
if content:
config = json.loads(content)
for k in config:
app.config[k.upper()] = config[k]
return app
if __name__ == '__main__':
create_app()
月份:2017年11月
Python Flask框架连接Mysql 学习笔记
认识Flask框架
Flask 是一个 Python 语言的微型网络开发框架。微框架中的 “微” 意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎则很容易替换。
Flask 基于 WerkzeugWSGI 工具箱和 Jinja2 模板引擎。实验中你将会知道 Jinja2 给予我们极大的方便,比如可以传递变量参数等。让我们的表示层动态的展示你想展示的信息,更详细的说明可参考Python Flask Web框架。
认识 MySQL 和简单的 SQL 语句
MySQL 作为一种关系型数据库,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站的开发都选择 MySQL 作为网站数据库。并且实验楼已经安装好 MySQL,故实验时只需使用即可。
我们要用到的 SQL 语句包括 select 语句,insert into 语句,create 语句,order by 子句,
Limit限制语法,natural join 语法。
创建myproject文件夹并激活virtualenv:
mkdir myprojectcdmyprojectvirtualenv venv. venv/bin/activate
在 virtualenv 中安装 Flask:
pipinstall -i http://mirrors.aliyuncs.com/pypi/simple flask
测试连接数据库代码:
插入数据:
importMySQLdbdb=MySQLdb.connect("localhost","root","","recommend")cursor=db.cursor()sql="create table user_anime(user int,anime int)"cursor.execute(sql)db.close()
简单说明一下上面的代码:
第一行导入连接 MySQL 的库
第二行通过指定参数(ip,用户名,密码,数据库)连接到某一个数据库
第三行使用 cursor() 方法获取操作游标
第四行为要执行的 SQL 语句,这句是创建一个名为 user_anime 表
第五行为执行 SQL 语句
查询Mysql 并取出数据
love=[]
DB=MySQLdb.connect("localhost","root","","recommend")
#获得数据库游标
c=DB.cursor()
#下面代码为实现从数据库中得到用户user所喜欢的番剧编号,以便判断重复
love=[]
#sql语句
sql="select anime_id from user_anime where user_id=%s"%user
c.execute(sql)
#得到结果集
results=c.fetchall()
for line in results:
love.append(line[0])
一个Flask应用运行过程剖析
相信很多初学Flask的同学(包括我自己),在阅读官方文档或者Flask的学习资料时,对于它的认识是从以下的一段代码开始的:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World!"
if __name__ == '__main__':
app.run()
运行如上代码,在浏览器中访问http://localhost:5000/,便可以看到Hello World!出现了。这是一个很简单的Flask的应用。
然而,这段代码怎么运行起来的呢?一个Flask应用运转的背后又有哪些逻辑呢?如果你只关心Web应用,那对这些问题不关注也可以,但从整个Web编程的角度来看,这些问题非常有意义。本文就主要针对一个Flask应用的运行过程进行简要分析,后续文章还会对Flask框架的一些具体问题进行分析。
为了分析方便,本文采用 Flask 0.1版本 的源码进行相关问题的探索。
一些准备知识
在正式分析Flask之前,有一些准备知识需要先了解一下:
- 使用Flask框架开发的属于Web应用。由于Python使用WSGI网关,所以这个应用也可以叫WSGI应用;
-
服务器、Web应用的设计应该遵循网关接口的一些规范。对于WSGI网关,要求Web应用实现一个函数或者一个可调用对象webapp(environ, start_response)。服务器或网关中要定义start_response函数并且调用Web应用。关于这部分的内容可以参考: http://fanchunke.me/Python/wsgiref%E5%8C%85%E2%80%94%E2%80%94%E7%AC%A6%E5%90%88WSGI%E6%A0%87%E5%87%86%E7%9A%84Web%E6%9C%8D%E5%8A%A1%E5%AE%9E%E7%8E%B0%EF%BC%88%E4%B8%80%EF%BC%89/
-
Flask依赖于底层库werkzeug。相关内容可以参考: http://fanchunke.me/Flask/Werkzeug%E5%BA%93%E7%AE%80%E4%BB%8B/
本文暂时不对服务器或网关的具体内容进行介绍,只需对服务器、网关、Web应用之间有怎样的关系,以及它们之间如何调用有一个了解即可。
一个Flask应用运行的过程
1. 实例化一个Flask应用
使用app = Flask(name),可以实例化一个Flask应用。实例化的Flask应用有一些要点或特性需要注意一下:
- 对于请求和响应的处理,Flask使用werkzeug库中的Request类和Response类。对于这两个类的相关内容可以参考: http://fanchunke.me/Flask/Werkzeug%E5%BA%93%E2%80%94%E2%80%94wrappers%E6%A8%A1%E5%9D%97/
-
对于URL模式的处理,Flask应用使用werkzeug库中的Map类和Rule类,每一个URL模式对应一个Rule实例,这些Rule实例最终会作为参数传递给Map类构造包含所有URL模式的一个“地图”。这个地图可以用来匹配请求中的URL信息,关于Map类和Rule类的相关知识可以参考: http://fanchunke.me/Flask/Werkzeug%E5%BA%93%E2%80%94%E2%80%94routing%E6%A8%A1%E5%9D%97/
-
当实例化一个Flask应用app(这个应用的名字可以随便定义)之后,对于如何添加URL模式,Flask采取了一种更加优雅的模式,对于这点可以和Django的做法进行比较。Flask采取装饰器的方法,将URL规则和视图函数结合在一起写,其中主要的函数是route。在上面例子中:
@app.route('/')
def index():
pass
这样写视图函数,会将’/’这条URL规则和视图函数index()联系起来,并且会形成一个Rule实例,再添加进Map实例中去。当访问’/’时,会执行index()。关于Flask匹配URL的内容,可以参考后续文章。
- 实例化Flask应用时,会创造一个Jinja环境,这是Flask自带的一种模板引擎。可以查看Jinja文档 (http://docs.jinkan.org/docs/jinja2/) ,这里先暂时不做相关介绍。
-
实例化的Flask应用是一个可调用对象。在前面讲到,Web应用要遵循WSGI规范,就要实现一个函数或者一个可调用对象webapp(environ, start_response),以方便服务器或网关调用。Flask应用通过call(environ, start_response)方法可以让它被服务器或网关调用。
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`"""
return self.wsgi_app(environ, start_response)
注意到调用该方法会执行wsgi_app(environ, start_response)方法,之所以这样设计是为了在应用正式处理请求之前,可以加载一些“中间件”,以此改变Flask应用的相关特性。对于这一点后续会详细分析。
- Flask应用还有一些其他的属性或方法,用于整个请求和响应过程。
2. 调用Flask应用时会发生什么
上面部分分析了实例化的Flask应用长什么样子。当一个完整的Flask应用实例化后,可以通过调用app.run()方法运行这个应用。
Flask应用的run()方法会调用werkzeug.serving模块中的run_simple方法。这个方法会创建一个本地的测试服务器,并且在这个服务器中运行Flask应用。关于服务器的创建这里不做说明,可以查看werkzeug.serving模块的有关文档。
当服务器开始调用Flask应用后,便会触发Flask应用的call(environ, start_response)方法。其中environ由服务器产生,start_response在服务器中定义。
上面我们分析到当Flask应用被调用时会执行wsgi_app(environ, start_response)方法。可以看出,wsgi_app是真正被调用的WSGI应用,之所以这样设计,就是为了在应用正式处理请求之前,wsgi_app可以被一些“中间件”装饰,以便先行处理一些操作。为了便于理解,这里先举两个例子进行说明。
例子一: 中间件SharedDataMiddleware
中间件SharedDataMiddleware是werkzeug.wsgi模块中的一个类。该类可以为Web应用提供静态内容的支持。例如:
import os
from werkzeug.wsgi import SharedDataMiddleware
app = SharedDataMiddleware(app, {
'/shared': os.path.join(os.path.dirname(__file__), 'shared')
})
Flask应用通过以上的代码,app便会成为一个SharedDataMiddleware实例,之后便可以在http://example.com/shared/中访问shared文件夹下的内容。
对于中间件SharedDataMiddleware,Flask应用在初始实例化的时候便有所应用。其中有这样一段代码:
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
self.static_path: target
})
这段代码显然会将wsgi_app变成一个SharedDataMiddleware对象,这个对象为Flask应用提供一个静态文件夹/static。这样,当整个Flask应用被调用时,self.wsgi_app(environ, start_response)会执行。由于此时self.wsgi_app是一个SharedDataMiddleware对象,所以会先触发SharedDataMiddleware对象的call(environ, start_response)方法。如果此时的请示是要访问/static这个文件夹,SharedDataMiddleware对象会直接返回响应;如果不是,则才会调用Flask应用的wsgi_app(environ.start_response)方法继续处理请求。
例子二: 中间件DispatcherMiddleware
中间件DispatcherMiddleware也是werkzeug.wsgi模块中的一个类。这个类可以讲不同的应用“合并”起来。以下是一个使用中间件DispatcherMiddleware的例子。
from flask import Flask
from werkzeug import DispatcherMiddleware
app1 = Flask(__name__)
app2 = Flask(__name__)
app = Flask(__name__)
@app1.route('/')
def index():
return "This is app1!"
@app2.route('/')
def index():
return "This is app2!"
@app.route('/')
def index():
return "This is app!"
app = DispatcherMiddleware(app, {
'/app1': app1,
'/app2': app2
})
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 5000, app)
在上面的例子中,我们首先创建了三个不同的Flask应用,并为每个应用创建了一个视图函数。但是,我们使用了DispatcherMiddleware,将app1、app2和app合并起来。这样,此时的app便成为一个DispatcherMiddleware对象。
当在服务器中调用app时,由于它是一个DispatcherMiddleware对象,所以首先会触发它的call(environ, start_response)方法。然后根据请求URL中的信息来确定要调用哪个应用。例如:
- 如果访问/,则会触发app(environ, start_response)(注意: 此时app是一个Flask对象),进而处理要访问app的请求;
-
如果访问/app1,则会触发app1(environ, start_response),进而处理要访问app1的请求。访问/app2同理。
3. 和请求处理相关的上下文对象
当Flask应用真正处理请求时,wsgi_app(environ, start_response)被调用。这个函数是按照下面的方式运行的:
def wsgi_app(environ, start_response):
with self.request_context(environ):
...
请求上下文
可以看到,当Flask应用处理一个请求时,会构造一个上下文对象。所有的请求处理过程,都会在这个上下文对象中进行。这个上下文对象是_RequestContext类的实例。
# Flask v0.1
class _RequestContext(object):
"""The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the
`_request_ctx_stack` and removed at the end of it. It will create the
URL adapter and request object for the WSGI environment provided.
"""
def __init__(self, app, environ):
self.app = app
self.url_adapter = app.url_map.bind_to_environ(environ)
self.request = app.request_class(environ)
self.session = app.open_session(self.request)
self.g = _RequestGlobals()
self.flashes = None
def __enter__(self):
_request_ctx_stack.push(self)
def __exit__(self, exc_type, exc_value, tb):
# do not pop the request stack if we are in debug mode and an
# exception happened. This will allow the debugger to still
# access the request object in the interactive shell.
if tb is None or not self.app.debug:
_request_ctx_stack.pop()
根据_RequestContext上下文对象的定义,可以发现,在构造这个对象的时候添加了和Flask应用相关的一些属性:
- app ——上下文对象的app属性是当前的Flask应用;
-
url_adapter ——上下文对象的url_adapter属性是通过Flask应用中的Map实例构造成一个MapAdapter实例,主要功能是将请求中的URL和Map实例中的URL规则进行匹配;
-
request ——上下文对象的request属性是通过Request类构造的实例,反映请求的信息;
-
session ——上下文对象的session属性存储请求的会话信息;
-
g ——上下文对象的g属性可以存储全局的一些变量。
-
flashes ——消息闪现的信息。
LocalStack和一些“全局变量”
注意: 当进入这个上下文对象时,会触发_request_ctx_stack.push(self)。在这里需要注意Flask中使用了werkzeug库中定义的一种数据结构LocalStack。
_request_ctx_stack = LocalStack()
关于LocalStack,可以参考: (http://fanchunke.me/Flask/Werkzeug%E5%BA%93%E2%80%94%E2%80%94local%E6%A8%A1%E5%9D%97/) 。LocalStack是一种栈结构,每当处理一个请求时,请求上下文对象_RequestContext会被放入这个栈结构中。数据在栈中存储的形式表现成如下:
{880: {'stack': [<flask._RequestContext object>]}, 13232: {'stack': [<flask._RequestContext object>]}}
这是一个字典形式的结构,键代表当前线程/协程的标识数值,值代表当前线程/协程存储的变量。werkzeug.local模块构造的这种结构,很容易实现线程/协程的分离。也正是这种特性,使得可以在Flask中访问以下的“全局变量”:
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)
其中_request_ctx_stack.top始终指向当前线程/协程中存储的“请求上下文”,这样像app、request、session、g等都可以以“全局”的形式存在。这里“全局”是指在当前线程或协程当中。
由此可以看出,当处理请求时:
- 首先,会生成一个请求上下文对象,这个上下文对象包含请求相关的信息。并且在进入上下文环境时,LocalStack会将这个上下文对象推入栈结构中以存储这个对象;
-
在这个上下文环境中可以进行请求处理过程,这个稍后再介绍。不过可以以一种“全局”的方式访问上下文对象中的变量,例如app、request、session、g等;
-
当请求结束,退出上下文环境时,LocalStack会清理当前线程/协程产生的数据(请求上下文对象);
-
Flask 0.1版本只有“请求上下文”的概念,在Flask 0.9版本中又增加了“应用上下文”的概念。关于“应用上下文”,以后再加以分析。
4. 在上下文环境中处理请求
处理请求的过程定义在wsgi_app方法中,具体如下:
def wsgi_app(environ, start_response):
with self.request_context(environ):
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
response = self.make_response(rv)
response = self.process_response(response)
return response(environ, start_response)
从代码可以看出,在上下文对象中处理请求的过程分为以下几个步骤:
-
在请求正式被处理之前的一些操作,调用preprocess_request()方法,例如打开一个数据库连接等操作;
-
正式处理请求。这个过程调用dispatch_request()方法,这个方法会根据URL匹配的情况调用相关的视图函数;
-
将从视图函数返回的值转变为一个Response对象;
-
在响应被发送到WSGI服务器之前,调用process_response(response)做一些后续处理过程;
-
调用response(environ, start_response)方法将响应发送回WSGI服务器。关于此方法的使用,可以参考: http://fanchunke.me/Flask/Werkzeug%E5%BA%93%E2%80%94%E2%80%94wrappers%E6%A8%A1%E5%9D%97/
-
退出上下文环境时,LocalStack会清理当前线程/协程产生的数据(请求上下文对象)。
基于Python的Flask的开发实战(第二节程序的基本结构)
1. 初始化
所有的flask程序都必须创建一个程序实例
web服务器使用wsgi接口协议,把接收客户端的请求都转发给这个程序实例来进行处理。这个程序实例就是flask对象
from flask import Flask
app = Flask(__name__)
#__name__决定程序的根目录,以便以后能找到相对于程序根目录的资源文件位置
2. 路由和视图函数
程序实例需要知道接收请求后,需要知道url请求应该运行哪些代码。所以保存了一个url和python函数的映射关系;这个映射关系就叫做路由
flask程序中路由的写法:
2.1 使用app.route装饰器,把修饰的函数注册为路由。例如
@app.route('/')
def index():
return "<h1>Hello World</h1>"
#函数的名字不是必须写index的,只是和装饰器关联的时候写的函数名而已
#把index函数注册为程序根路径的处理程序。函数的返回值称为响应,是客户端接收的内容。
像index这样的函数称为试图函数,试图函数返回的响应可以是包含html的简单字符串,也可以是复杂的东西
2.2 可变url部分映射,使用特定的装饰器语法就可以
@app.route('/user/<name>')
def user(name):
return "<h1>hello %s</h1>"%(name)
装饰器中的指定可变内容为name,name对user(name)函数中的传递参数,这2个部分内容必须一致
调用试图函数时候,flask会自动的将动态部分作为参数传入参数,这个函数中,参数用于生成个人的欢迎信息
#备注:路由中的动态部分默认使用字符串类型,可以使用int,float,path来定义;例如;path类型也是字符串,但不把斜线视作分隔符,而将其当做动态片段的一部分
3. 启动服务器
调用程序实例app的run方法启动flask集成开发的web服务器
if __name__ == "__main__":
app.run(debug=True)
debug=True代表的是调试模式,这个flask自带的run方法开启的服务器不适合在生产中使用,此处只用来测试
4. 一个完整的Flask程序
啥也不说,先上例子hello.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>HelloWorld</h1>'
@app.route('/user/<name>')
def user(name):
return "<h1>hello %s</h1>"%name
if __name__ == "__main__":
app.run(debug=True)
默认会开启服务器本机5000端口;127.0.0.1:5000
执行脚本python hello.py
浏览器测试http://127.0.0.1:5000/
http://127.0.0.1:5000/user/xiaobai
5. 请求上下文
Flask使用请求上下文,临时把某些对象变为全局可用;例如
from flask import request
@app.route('/')
def index():
user_agent = request.headers.get('User-Agent')
return '<h1>your browser is %s</h1>'%(user_agent)
在这个视图函数中,我们把request当做全局变量使用,flask使用请求上下文让特定的变量在一个线程中全局可访问。于此同时却不会干扰其他线程
session:请求上下文;用户会话,用于存储请求之间需要“记住”的值的词典
激活请求上下文的后就可以使用request和session变量了
6. 程序上下文
current_app:程序上下文;当前激活程序的程序实例
g:程序上下文;处理请求时用作临时存储的对象
7. 请求映射关系表
接收请求,处理请求,,,之间有个映射表,要不然不知道该去执行什么代码。URL映射
from hello import app
print app.url_map
Map([<Rule '/' (HEAD, OPTIONS, GET) -> index>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/user/<name>' (HEAD, OPTIONS, GET) -> user>])
8. 请求钩子
有的时候在处理请求之前和之后,执行某些特定的代码是很有用的,这就用到了请求钩子
例如在请求之前创建数据库连接或者redis连接;或者是系统里面用户请求处理之前先验证用户的身份,是否激活,激活执行什么操作,没激活用户一直绑到固定页面去直到激活
为了避免每个试图函数中都使用重复的代码,flask提供了注册通用函数的功能;
也就是说只要写一个请求钩子-函数,整个程序实例全局都被应用了。
例如:在所有请求之前先验证下用户的认证状态
@before_app_request
def before_request():
if current_user.is_authenticated:
current_user.ping()
if not current_user.confirmed and request.endpoint[:5] != 'auth.' and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))
常见的4种钩子:
- before_first_request:注册一个函数,在处理第一个请求之前运行
-
before_request:注册一个函数,每次请求之前运行
-
after_request:注册一个函数,没有未处理的异常抛出,每次请求之后运行
-
teardown_request:注册一个函数,有未处理的异常抛出,每次请求之后运行
在请求钩子和视图函数之间共享数据一般使用程序上下文g;
例如before_request处理程序可以从数据库中加载已登录用户,将其保存到g.user中,随后调用试图函数,试图函数再从g.user中获取用户
9. 基于Flask的http响应
flask调用试图函数处理请求,并把返回值作为响应的内容.大多情况下是一个简单的字符串或者json字符串;返回字符串常用于给对方提供接口的时候使用
http响应中很重要的一个内容是状态码,flask默认设置为200,这个代码表明请求已经被成功处理了
如果试图函数返回的响应需要不同的状态码,可以把状态码加到后面返回
例如
@app.route('/')
def index():
return '<h1>Bad Request</h1>',400
试图函数返回的响应还可以接受第三个参数,第三个参数是一个字典类型的首部,可以添加到http响应中去,一般不用添加
如果不想返回这种好多个元素的元祖,可以使用Response对象来标准化下返回。
例如:创建一个响应对象,然后设置cookie
from flask import make_response
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer',42)
return response
还有一种特殊的响应类型,flask提供了一种基于302的跳转响应,这个响应由redirect函数来提供。指向的地址由Location首部提供,重定向的响应可以使用3个值形式的返回值生成。也可以再Response对象中设定
例如:
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
还有一种特殊的响应类型,flask提供了一种错误响应。这个由abort函数来提供。abort抛出404异常,抛出异常后把控制权移交给web服务器
例如:
from flask import abort
@app.route('/user/<id>')
def get_user(id):
user = load_user(id)
if not user:
abort(404)
return '<h1>Hello,%s</h1>'%(user.name)
10. flask的扩展flask-script
这个例子主要是讲如何把flask扩展添加到程序中,并使用
例如下面你的例子是添加flask-script扩展,使用命令行参数增强程序的功能
使用命令行方式启动web服务器,而不是修改文件,给run方法传递参数
安装扩展
pip install flask-script
使用flask-script扩展,并把hello.py文件改为命令行参数启动的形式#添加的扩展默认会安装到flask.ext命名空间中
from flask import Flask
from flask.ext.script import Manager
app = Flask(__name__)
manager = Manager(app)
@app.route('/')
def index():
return '<h1>HelloWorld</h1>'
@app.route('/user/<name>')
def user(name):
return "<h1>hello %s</h1>"%name
if __name__ == "__main__":
manager.run()
flask-script扩展中添加了一个Manager的类,以上例子中,这个扩展初始化的方法是,把程序实例作为参数传递给构造函数来初始化主类的实例。后续其他flask扩展也基本是这个套路
这样修改之后,程序就可以使用一组基本的命令行选项来启动和调试了
python hello.py shell#在flask应用上下文环境中运行python shell,方便测试和调试web环境
python hello.py runserver#运行flask开发服务器,app.run()
python hello.py -h#显示帮助信息
python hello.py runserver --help
usage: hello.py runserver [-h] [-t HOST] [-p PORT] [--threaded]
[--processes PROCESSES] [--passthrough-errors] [-d]
[-r]
python hello.py runserver -h 0.0.0.0 -p 80#这样就开启了本机的80端口,别的机器可以远程访问了
Python Flask 框架 终极部署教程,超详细。Uwsgi+Nginx+mysql+Supervisor+Virtualenv, 基于阿里云默认Linux
我发现网上还没完整详细版本的Flask 部署教程,而我在部署中遇到很多坑,所以在这里写下来,完整的教程以下是部署流程:
处理本地的代码
假设你已经完成了项目的开发,本地已经安装了git,那么首先将你的代码提交到git;
#进项目根目录
pip freeze > requirements.txt #导flask 全部包,方便新环境下一次性安装。
git init # 之前如果没有做,那么需要做
git add --all #提交所有修改后的文件
git remote add origin http:xxxx.git #这一步如果之前没做,那么你需要做。
git commmit -w 'first commit'
安装Mysql
现在进入服务器正式开始搭建环境,首先安装mysql.
新鲜出炉的阿里云需要更新下apt.
apt-get update
然后安装mysql, 注意一定记住root 密码,还要在配置文件中设置字符为UTF8!
增加新的数据库,注意名字和你项目的一样,
create database xxx;
安装虚拟环境 Vitualenv
pip install virtualenvwrapper #直接安装虚拟环境容器来顺便安装virtualenv
注意需要先修改环境变量。否则命名无法使用
vim ~/.bashrc 在最底部增加
#这是为了让 WORKON 命令能用。
export WORKON_HOME=$HOME/.virtualenvs
source /usr/local/bin/virtualenvwrapper.sh
然后你需要用 mkvirtualenv xxx 来创建一个叫xxx 的python 虚拟环境
workon xxx #切换到你的虚拟环境
在这个环境里,你可以升级你的python 到python3, 当然根据你的需求。
安装 Git 拿到全部源码
现在在服务器上安装git 拿到源码
apt install git
git init
git remote add origin http:xxx.git
git pull origin master # 拿到全部源码
正式部署源码
进去项目根目录:
pip install -r requirements.txt #导入你项目的flask 依赖
pip manager.py db migerate #初始化数据库
# 这里如果出现初始化失败,那么清空你的 migration 文件夹,你会丢掉你的测试数据,当然有不丢的办法
pip manager.py db upgrade #导入数据库,代码迁移完成
安装Nginx
apt install nginx #安装nginx
贴下配置文件位于: /etc/nginx/conf.d/xxx.conf
# 配置nginx与uwsgi的通信方式和名称,mysite就是名称
upstream xxx {
# nginx使用socket的方式与uwsgi进行通信
# 这里指向你项目的目录下的socket文件,
# 这个socket文件不需要单独创建,在运行的时候会自动创建。
server unix:///srv/xxx/xxx.sock;
}
# 配置服务器
server {
# 监听的端口号
listen 80;
# 域名或者本机的ip地址
server_name dev.wudizu.com 47.104.22.138;
charset utf-8;
# 最大的上传大小
client_max_body_size 75M;
# adjust to taste
# 访问根目录下的任何url的配置
location / {
# 指定访问哪个upstream
uwsgi_pass wudi;
# 包括uwsgi参数的文件
include /etc/nginx/uwsgi_params;
}
location /static {
alias /srv/xxx/static;
}
}
安装Uwsgi
pip install uwsgi
贴下配置文件:
[uwsgi]
chdir = /srv/xxx
# 模块
module = firstweb #注意这里一定要你写的flask 首页文件名
# python的虚拟环境
home = /root/.virtualenvs/flask-py2
# 是否启用mater模式
master = true
# 进程数
processes = 10
# socket文件地址
socket = /srv/xxx/xxx.sock
# wsgi文件
wsgi-file = /srv/xxx/xxx.ini
# wsgi文件中的app变量
callable = app
# socket文件的权限
chmod-socket = 666
安装Supervisor
sudo pip install supervisor
# supervisor的程序名字
[program:mysite]
# supervisor执行的命令
command=uwsgi --ini mysite_uwsgi.ini
# 项目的目录
directory = /srv/xxx
# 开始的时候等待多少秒
startsecs=0
# 停止的时候等待多少秒
stopwaitsecs=0# 自动开始
autostart=true
# 程序挂了后自动重启
autorestart=true
# 输出的log文件
stdout_logfile=/srv/xxx/log/supervisord.log
# 输出的错误文件
stderr_logfile=/srv/xxx/log/supervisord.err
[supervisord]
# log的级别
loglevel=info
# 使用supervisorctl的配置
[supervisorctl]
# 使用supervisorctl登录的地址和端口号
serverurl = http://127.0.0.1:9001
# 登录supervisorctl的用户名和密码
username = admin
password = 123
[inet_http_server]
# supervisor的服务器
port = :9001
# 用户名和密码
username = admin
password = 123
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
然后使用supervisor运行uwsgi:
supervisord -c supervisor.conf
使用supervisorctl管理supervisord:supervisor是一个C/S模型,跟redis一样,有一个服务器,也有一个客户端命令用来管理服务的,使用以下命令进入到命令界面:
supervisorctl -c supervisor.conf
指定的文件必须和supervisord服务端保持一致。 一些常用的命令有:
supervisorctl -c supervisor.conf
> status # 查看状态
> start program_name # 启动程序
> restart program_name # 重新启动程序
> stop program_name # 停止程序
> reload # 重新加载配置文件
> quit # 退出当前的客户端
centos下安装supervisor
centos下安装supervisor
1. 安装
# yum install python-setuptools
# easy_install supervisor
成功安装后可以登陆python控制台输入import supervisor 查看是否能成功加载。
补充:如果easy_install不好使就从官方下载,然后通过python安装:
# tar zxf supervisor-xxxx.tar.gz
# cd supervisor
# python setup.py install
2. 创建配置文件(supervisord.conf)
使用root身份创建一个全局配置文件
#echo_supervisord_conf > /etc/supervisord.conf
#supervisord -c /etc/supervisord.conf
3. 修改配置文件(supervisord.conf)
如果修改了 /etc/supervisord.conf ,需要执行 #supervisorctl reload 来重新加载配置文件,否则不会生效
supervisord 是启动supervisor
supervisorctl 是控制supervisord
打开supervisord.conf 的 [include] 引入 files的配置.
[include]
files = /usr/local/share/supervisor/*.conf
在supervisor建立一个你想守护的进程的文件名, redisQueue.conf
#cd /usr/local/share/supervisor/
#vi redisQueue.conf
新增如下内容
[program:redisQueue]
command=php /var/www/fp_dev_2/html/script/consumerQueue.php dev
user =root
autostart=true
autorestart=true
startsecs=3
stdout_logfile=/usr/local/share/supervisor/redisQueue.log
#supervisorctl reload
说明:command=后面跟着就是你要守护的shell.
4. 运行命令
supervisorctl status
supervisorctl help
Python进程管理工具Supervisor
Linux下安装pip
wget https://bootstrap.pypa.io/get-pip.py
python get-pip.py
pip -V #查看pip版本
Supervisor是基于Python的进程管理工具,可以更简单的监听、启停、重启服务器上的一个或多个后台进程,是Linux服务器管理的高效工具
Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程挂掉后,会自动将它重新拉起
其进程自动恢复的功能,不再需要自己写shell脚本来控制
Supervisor 有两个主要的组成部分:
- supervisord,运行Supervisor时会启动一个进程supervisord,它负责启动所管理的进程,并将所管理的进程作为自己的子进程来启动,而且可以在所管理的进程出现崩溃时自动重启
- supervisorctl,是命令行管理工具,可以用来执行stop、start、restart等命令,来对这些子进程进行管理
安装setuptools
wget --no-check-certificate https://pypi.python.org/packages/source/s/setuptools/setuptools-12.0.3.tar.gz#md5=f07e4b0f4c1c9368fcd980d888b29a65
tar -zxvf setuptools-12.0.3.tar.gz
cd setuptools-12.0.3
python setup.py install
安装pip
easy_install pip
安装supervisor
easy_install supervisor
测试是否安装成功
echo_supervisord_conf
创建配置文件
echo_supervisord_conf > /etc/supervisord.conf
如果出现没有权限的问题,可以使用这条命令
sudo su - root -c "echo_supervisord_conf > /etc/supervisord.conf"
supervisor安装完成后会生成三个执行程序:
* supervisortd【supervisor的守护进程服务(用于接收进程管理命令)】
* supervisorctl【客户端(用于和守护进程通信,发送管理进程的指令)】
* echo_supervisord_conf【生成初始配置文件程序】
配置文件说明
想要了解怎么配置需要管理的进程,只要打开 supervisord.conf 就可以了,里面有很详细的注释信息
打开配置文件
vim /etc/supervisord.conf
默认的配置文件是下面这样的,但是这里有个坑需要注意,supervisord.pid 以及 supervisor.sock 是放在 /tmp 目录下
但是 /tmp 目录是存放临时文件,里面的文件是会被 Linux 系统删除的
一旦这些文件丢失,就无法再通过 supervisorctl 来执行 restart 和 stop 命令了,
将只会得到 unix:///tmp/supervisor.sock 不存在的错误
因此可以单独建一个文件夹,来存放这些文件,比如放在 /etc/supervisord.d/
默认情况下,进程的日志文件达到50MB时,将进行分割,最多保留10个文件,当然这些配置也可以对每个进程单独配置
创建文件夹
mkdir -p /etc/supervisord.d
创建日志目录
mkdir -p /var/log/supervisor/
supervisord.conf
; Sample supervisor config file.
;
; For more information on the config file, please see:
; http://supervisord.org/configuration.html
;
; Notes:
; - Shell expansion ("~" or "$HOME") is not supported. Environment
; variables can be expanded using this syntax: "%(ENV_HOME)s".
; - Quotes around values are not supported, except in the case of
; the environment= options as shown below.
; - Comments must have a leading space: "a=b ;comment" not "a=b;comment".
; - Command will be truncated if it looks like a config file comment, e.g.
; "command=bash -c 'foo ; bar'" will truncate to "command=bash -c 'foo ".
[unix_http_server]
; 修改为/etc/supervisord.d目录,避免被系统删除
file=/etc/supervisord.d/supervisor.sock ; the path to the socket file
;chmod=0700 ; socket file mode (default 0700)
;chown=nobody:nogroup ; socket file uid:gid owner
;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=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)
[supervisord]
; logfile=/tmp/supervisord.log ; main log file; default $CWD/supervisord.log
; 修改为 /var/log 目录,避免被系统删除
logfile=/var/log/supervisor/supervisord.log ;
; 日志文件多大时进行分割
logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
; 最多保留多少份日志文件
logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
loglevel=info ; log level; default info; others: debug,warn,trace
; 修改为/etc/supervisord.d目录,避免被系统删除
pidfile=/etc/supervisord.d/supervisord.pid ; supervisord pidfile; default supervisord.pid
nodaemon=false ; start in foreground if true; default false
minfds=1024 ; min. avail startup file descriptors; default 1024
minprocs=200 ; min. avail process descriptors;default 200
;umask=022 ; process file creation umask; default 022
; 设置启动supervisord的用户,一般情况下不要轻易用root用户来启动,除非你真的确定要这么做
user=root ; default is current user, required if root
;identifier=supervisor ; supervisord identifier, default is 'supervisor'
;directory=/tmp ; default is not to cd during start
;nocleanup=true ; don't clean up tempfiles at start; default false
;childlogdir=/tmp ; 'AUTO' child log dir, default $TEMP
;environment=KEY="value" ; key value pairs to add to environment
;strip_ansi=false ; strip ansi escape codes in logs; def. false
; The rpcinterface:supervisor section must remain in the config file for
; RPC (supervisorctl/web interface) to work. Additional interfaces may be
; added by defining them in separate [rpcinterface:x] sections.
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
; The supervisorctl section configures how supervisorctl will connect to
; supervisord. configure it match the settings in either the unix_http_server
; or inet_http_server section.
[supervisorctl]
; 必须和'unix_http_server'里面的设定匹配
; 修改为/etc/supervisor目录,避免被系统删除
;serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
serverurl=unix:///etc/supervisord.d/supervisor.sock ;
;serverurl=http://127.0.0.1:9001 ; use an http:// url to specify an inet socket
;username=chris ; should be same as in [*_http_server] if set
;password=123 ; should be same as in [*_http_server] if set
;prompt=mysupervisor ; cmd line prompt (default "supervisor")
;history_file=~/.sc_history ; use readline history if available
; The sample program section below shows all possible program subsection values.
; Create one or more 'real' program: sections to be able to control them under
; supervisor.
;[program:theprogramname]
;command=/bin/cat ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1 ; number of processes copies to start (def 1)
;directory=/tmp ; directory to cwd to before exec (def no cwd)
;umask=022 ; umask for process (default None)
;priority=999 ; the relative start priority (default 999)
;autostart=true ; start at supervisord start (default: true)
;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
;startretries=3 ; max # of serial start failures when starting (default 3)
;autorestart=unexpected ; when to restart if exited after running (def: unexpected)
;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2)
;stopsignal=QUIT ; signal used to kill process (default TERM)
;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false ; send stop signal to the UNIX process group (default false)
;killasgroup=false ; SIGKILL the UNIX process group (def false)
;user=chrism ; setuid to this UNIX account to run the program
;redirect_stderr=true ; redirect proc stderr to stdout (default false)
;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10 ; # of stdout logfile backups (0 means none, default 10)
;stdout_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stdout_events_enabled=false ; emit events on stdout writes (default false)
;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10 ; # of stderr logfile backups (0 means none, default 10)
;stderr_capture_maxbytes=1MB ; number of bytes in 'capturemode' (default 0)
;stderr_events_enabled=false ; emit events on stderr writes (default false)
;environment=A="1",B="2" ; process environment additions (def no adds)
;serverurl=AUTO ; override serverurl computation (childutils)
; The sample eventlistener section below shows all possible eventlistener
; subsection values. Create one or more 'real' eventlistener: sections to be
; able to handle event notifications sent by supervisord.
;[eventlistener:theeventlistenername]
;command=/bin/eventlistener ; the program (relative uses PATH, can take args)
;process_name=%(program_name)s ; process_name expr (default %(program_name)s)
;numprocs=1 ; number of processes copies to start (def 1)
;events=EVENT ; event notif. types to subscribe to (req'd)
;buffer_size=10 ; event buffer queue size (default 10)
;directory=/tmp ; directory to cwd to before exec (def no cwd)
;umask=022 ; umask for process (default None)
;priority=-1 ; the relative start priority (default -1)
;autostart=true ; start at supervisord start (default: true)
;startsecs=1 ; # of secs prog must stay up to be running (def. 1)
;startretries=3 ; max # of serial start failures when starting (default 3)
;autorestart=unexpected ; autorestart if exited after running (def: unexpected)
;exitcodes=0,2 ; 'expected' exit codes used with autorestart (default 0,2)
;stopsignal=QUIT ; signal used to kill process (default TERM)
;stopwaitsecs=10 ; max num secs to wait b4 SIGKILL (default 10)
;stopasgroup=false ; send stop signal to the UNIX process group (default false)
;killasgroup=false ; SIGKILL the UNIX process group (def false)
;user=chrism ; setuid to this UNIX account to run the program
;redirect_stderr=false ; redirect_stderr=true is not allowed for eventlisteners
;stdout_logfile=/a/path ; stdout log path, NONE for none; default AUTO
;stdout_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stdout_logfile_backups=10 ; # of stdout logfile backups (0 means none, default 10)
;stdout_events_enabled=false ; emit events on stdout writes (default false)
;stderr_logfile=/a/path ; stderr log path, NONE for none; default AUTO
;stderr_logfile_maxbytes=1MB ; max # logfile bytes b4 rotation (default 50MB)
;stderr_logfile_backups=10 ; # of stderr logfile backups (0 means none, default 10)
;stderr_events_enabled=false ; emit events on stderr writes (default false)
;environment=A="1",B="2" ; process environment additions
;serverurl=AUTO ; override serverurl computation (childutils)
; The sample group section below shows all possible group values. Create one
; or more 'real' group: sections to create "heterogeneous" process groups.
;[group:thegroupname]
;programs=progname1,progname2 ; each refers to 'x' in [program:x] definitions
;priority=999 ; the relative start priority (default 999)
; The [include] section can just contain the "files" setting. This
; setting can list multiple files (separated by whitespace or
; newlines). It can also contain wildcards. The filenames are
; interpreted as relative to this file. Included files *cannot*
; include files themselves.
[include]
files = /etc/supervisord.d/config/*.ini
使用include
在配置文件的最后,有一个 [include] 的配置项,跟 Nginx 一样,可以 include 某个文件夹下的所有配置文件,这样我们就可以为每个进程或相关的几个进程的配置单独写成一个文件
[include]
files = /etc/supervisord.d/config/*.ini
创建进程的配置文件
编辑/etc/supervisord.d/config/matrix.ini
[group:matrix]
programs = matrix.print
[program:matrix.print]
; 启动命令,可以看出与手动在命令行启动的命令是一样的
command=python /data/demo/demo.py
numprocs=1
numprocs_start=0
priority=999
; 在supervisord启动的时候也自动启动
autostart=true
; 启动3秒后没有异常退出,就当作已经正常启动了
startsecs=3
; 启动失败自动重试次数,默认是3
startretries=3
exitcodes=0,2
stopsignal=QUIT
stopwaitsecs=60
directory=/data/demo
user=root
; 默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
stopasgroup=false
; 默认为false,向进程组发送kill信号,包括子进程
killasgroup=false
redirect_stderr=true
;stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile=/data/demo/data.log
; 日志文件多大时进行分割
stdout_logfile_maxbytes=250MB
; 最多保留多少份日志文件
stdout_logfile_backups=10
stderr_logfile=/data/demo/data.err
stderr_logfile_maxbytes=250MB
stderr_logfile_backups=10
; 可以通过 environment 来添加需要的环境变量,一种常见的用法是修改 PYTHONPATH
environment=PYTHONPATH="/data/demo"
创建测试脚本&日志目录
mkdir -p /data/demo
/data/demo/demo.py
vi /data/demo/demo.py
# -*- coding: utf-8 -*-
import os
import time
while 1:
f = open('data.log', 'a+')
time.sleep(5)
f.write('This is a Test!')
启动supervisor
supervisord -c /etc/supervisord.conf
查看supervisor是否启动成功
ps -ef | grep '/etc/supervisord.conf'
root 23542 1 0 17:46 ? 00:00:00 /usr/bin/python /usr/bin/supervisord -c /etc/supervisord.conf
root 23566 23079 0 17:54 pts/0 00:00:00 grep /etc/supervisord.conf
查看supervisord.log
tail -f /var/log/supervisor/supervisord.log
2017-10-31 17:46:18,672 CRIT Set uid to user 0
2017-10-31 17:46:18,678 INFO RPC interface 'supervisor' initialized
2017-10-31 17:46:18,678 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2017-10-31 17:46:18,679 INFO daemonizing the supervisord process
2017-10-31 17:46:18,679 INFO supervisord started with pid 23542
2017-10-31 17:48:14,042 INFO spawned: 'matrix.print' with pid 23555
2017-10-31 17:48:17,047 INFO success: matrix.print entered RUNNING state, process has stayed up for > than 3 seconds (startsecs)
supervisord.conf配置文件参数说明
[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结束的配置文件
supervisorctl命令介绍
# 停止某一个进程,program_name 为 [program:x] 里的 x
supervisorctl stop program_name
# 启动某个进程
supervisorctl start program_name
# 重启某个进程
supervisorctl restart program_name
# 结束所有属于名为 groupworker 这个分组的进程 (start,restart 同理)
supervisorctl stop groupworker:
# 结束 groupworker:name1 这个进程 (start,restart 同理)
supervisorctl stop groupworker:name1
# 停止全部进程,注:start、restart、stop 都不会载入最新的配置文件
supervisorctl stop all
# 载入最新的配置文件,停止原有进程并按新的配置启动、管理所有进程
supervisorctl reload
# 根据最新的配置文件,启动新配置或有改动的进程,配置没有改动的进程不会受影响而重启
supervisorctl update
# 查看进程的状态
supervisorctl status
查看进程状态
supervisorctl status
matrix:matrix.print RUNNING pid 23555, uptime 0:10:00
查看supervisord所在路径
which supervisord
使用浏览器来管理
supervisor 同时提供了通过浏览器来管理进程的方法,只需要注释掉如下几行就可以了
;[inet_http_server] ; inet (TCP) server disabled by default
;port=192.168.3.244: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))
[supervisorctl]
...
;serverurl=http://192.168.3.244:9001 ; use an http:// url to specify an inet socket
;username=chris ; should be same as http_username if set
;password=123 ; should be same as http_password if set
访问浏览器地址栏:http://192.168.3.244:9001/
开机自动启动Supervisord
Linux在启动的时候会执行/etc/rc.local里面的脚本,所以在这里添加执行命令就可以
# 如果是 Ubuntu 添加以下内容
/usr/local/bin/supervisord -c /etc/supervisord.conf
# 如果是 Centos 添加以下内容
/usr/bin/supervisord -c /etc/supervisord.conf
启用rc.local服务
sudo systemctl enable rc-local.service
Supervisor进程监视管理
supervisor是用Python开发的一套通用的进程管理程序,可以将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时可以自动重启。
安装Supervisor
# yum search setuptools
....
python-setuptools.noarch : Easily build and distribute Python packages
#yum install -y python-setuptools.noarch
#easy_install supervisor
创建主配置文件
# mkdir -m 755 -p /etc/supervisor \创建supervisor配置文件目录
# echo_supervisord_conf >/etc/supervisor/supervisord.conf \创建主配置文件
# cd /etc/supervisor/
# mkdir -m 755 conf.d \创建项目配置文件目录
创建项目配置文件(运行3个脚本)
# vim conf.d/test.ini
[program:tjapp_sendmessage]
command=/bin/bash /data/shell/sendmessage.sh
numprocs=1
autostart=true
autorestart=true
[program:bbscollection]
command=/bin/bash /data/shell/bbscollection.sh
numprocs=1
autostart=true
autorestart=true
[program:test_sbbscollection]
command=/bin/bash /data/shell/test_sbbscollection.sh
numprocs=1
autostart=true
autorestart=true
在主配置文件中引入test.ini
#cat supervisord.conf
...
[include]
files = ./conf.d/*.ini
启动supervisor
# supervisord -c /etc/supervisor/supervisord.conf
查看supervisor运行的脚本
# supervisorctl
bbscollection RUNNING pid 10090, uptime 4 days, 17:20:10
test_sbbscollection RUNNING pid 10088, uptime 4 days, 17:20:10
tjapp_sendmessage RUNNING pid 10089, uptime 4 days, 17:20:10
停止bbscollection脚本
# supervisorctl stop bbscollection
bbscollection: stopped
# supervisorctl
bbscollection STOPPED Apr 06 10:23 AM
test_sbbscollection RUNNING pid 10088, uptime 4 days, 17:23:13
tjapp_sendmessage RUNNING pid 10089, uptime 4 days, 17:23:13
# supervisor> start bbscollection \启动
# supervisor> status
bbscollection RUNNING pid 7310, uptime 0:00:24
test_sbbscollection RUNNING pid 10088, uptime 4 days, 17:23:54
tjapp_sendmessage RUNNING pid 10089, uptime 4 days, 17:23:54
systemctl管理
vim /usr/lib/systemd/system/supervisord.service
# supervisord service for sysstemd (CentOS 7.0+)
# by ET-CS (https://github.com/ET-CS)
[Unit]
Description=Supervisor daemon
[Service]
Type=forking
ExecStart=/usr/bin/supervisord
ExecStop=/usr/bin/supervisorctl $OPTIONS shutdown
ExecReload=/usr/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
#systemctl enable supervisord.service
PostgreSQL 数据库的备份
一、建立数据库连接
命令:
psql -h IP地址 -p 端口 -U 数据库用户名 -d 数据库名
psql -h 127.0.0.1 -p 5432 -U postgres -d postgres
psql命令连接选项
Connection options:
-h, --host=HOSTNAME 主机 默认local
-p, --port=PORT 端口 默认5432
-U, --username=USERNAME 用户名 默认postgres
-w, --no-password 从不提示密码
-W, --password 强制 psql 提示输入密码,即使没有密码也会提示。
-d 指定要连接的库名
=============================================
二、数据备份还原
pg_restore可以恢复由pg_dump备份的文件,它会重新生成包括数据在内的所有用户定义的类型、函数、表、索引的所有别要的命令
使用-d选项执行数据库的名称,-C指定备份文件的路径
pg_restore -d testdb -U postgres -C /home/postgres/testdb.sql
psql是一个PostgreSQL的终端,它可以运行用户输入的语句,输入的语句还可以来自一个文件,
所以对于备份的包含create、insert语句的文本文件,可以使用psql恢复到数据中。
psql -d testdb -U postgres -f /home/postgres/testdb.sql
1. pg_dump备份数据库
命令:pg_dump -h IP地址 -p 端口 -U 数据库用户名 -f 目标存储文件及路径 目标数据库名
备份testdb数据库到/home/postgres/testdb.sql文件
pg_dump -U postgres -f /home/postgres/testdb.sql testdb
恢复
psql -U postgres -d testdb -f /home/postgres/testdb.sql
备份testdb库中的pmp_login_log表
pg_dump -U postgres -t pmp_login_log -f /home/postgres/login_log.sql testdb
恢复
psql -U postgres -d testdb -f /home/postgres/login_log.sql
2. pg_dumpall备份数据库
使用pg_dumpall备份整个服务器的数据库
备份
pg_dumpall -U postgres -f /home/postgres/postgres.sql
恢复
psql -U postgres -f /home/postgres/postgres.sql
三、PostgreSQL 无须手动输入密码
PostgreSQL里没有加入密码选项,一般备份命令需要手动输入密码,所以会给自动备份带来一定的不便。
查看了官方文档,(英文不好,全程都翻译/(ㄒoㄒ)/~~)
PGPASSWORD behaves the same as the password connection parameter. Use of this environment variable is not recommended for security reasons, as some operating systems allow non-root users to see process environment variables via ps; instead consider using the ~/.pgpass file (see Section 32.15).
PGPASSFILE specifies the name of the password file to use for lookups. If not set, it defaults to ~/.pgpass (see Section 32.15).
On Unix systems, the permissions on.pgpassmust disallow any access to world or group; achieve this by the command chmod 0600 ~/.pgpass. If the permissions are less strict than this, the file will be ignored.
文档中提到两种方法;
第一种方法:通过PostgreSQL的环境变量参数来实现保存密码。
export PGPASSWORD="123456"
第二种方法:创建 ~/.pgpass 文件来保存密码
密码文件的格式: hostname:port:database:username:password
cat ~/.pgpass
localhost:5432:testdb:postgres:123456
注意:
根据官方文档的说明,因为安全的原因,不推荐环境变量的方式,推荐使用~/.pgpass 来保存密码,此文件必须设置0600权限,如果权限不那么严格,则该文件将被忽略。
chmod 0600 ~/.pgpass
Centos 7.3 安装配置 PostgreSQL 9.x
一、安装 PostgresSQL
Centos 7 自带的 PostgresSQL 是 9.2 版的。因为,yum 已经做了国内源,速度飞快,所以直接就用 yum 安装了。依次执行以下命令即可,非常简单。
sudo yum -y install postgresql-server postgresql
sudo service postgresql initdb
sudo chkconfig postgresql on
sudo systemctl enable postgresql
sudo systemctl start postgresql
如果需要安装最新的版本,那就按官网上的说明来吧,但那个下载速度实在是……
https://www.postgresql.org/download/linux/redhat/
二、控制台 & 基本命令 & 数据库操作
PostgresSQL 默认已经创建了名为 postgres 的超级用户。
执行以下命令,登录控制台
sudo -u postgres psql postgres
可以试试以下一些常用的控制台命令
(这一部分的内容,基本是照搬阮兄的博文,我把操作逻辑贯通了一些,另做了少量补充,方便实验,原文在此)
# 查看 SQL 命令的解释,比如 h select
h
# 查看 PostgresSQL 命令列表
?
# 列出所有数据库
l
# 连接(使用)某个数据库
c [database_name]
# 列出当前数据库的所有表格
d
# 列出某一张表格的结构
d [table_name]
# 列出所有用户
du
# 打开文本编辑器
e
# 列出当前数据库和连接的信息
conninfo
# 退出控制台
q
创建名为 testdb 的数据库(注意:不要忘了命令末尾的分号!)
CREATE DATABASE testdb;
再用执行 l,确认数据库已经创建成功了
连接到(使用)testdb 数据库
c testdb
查看连接信息,确认已经连接到 testdb 上了
conninfo
接下来就是数据库的基本操作了,其实就是各种 SQL 语句,例如:
# 创建新表(执行后,用 d 可以看到当前数据库的表格列表中,出现了 user_tb)
CREATE TABLE user_tb(name VARCHAR(20), signup_date DATE);
# 插入数据
INSERT INTO user_tb(name, signup_date) VALUES('张三', '2017-08-03');
# 选择记录
SELECT * FROM user_tb;
# 更新数据
UPDATE user_tb set name = '李四' WHERE name = '张三';
# 删除记录
DELETE FROM user_tb WHERE name = '李四';
# 添加栏位(使用 d user_tb 查看 SQL 执行前后,表结构的变化)
ALTER TABLE user_tb ADD email VARCHAR(40);
# 更新结构
ALTER TABLE user_tb ALTER COLUMN signup_date SET NOT NULL;
# 更名栏位
ALTER TABLE user_tb RENAME COLUMN signup_date TO signup;
# 删除栏位
ALTER TABLE user_tb DROP COLUMN email;
# 表格更名
ALTER TABLE user_tb RENAME TO user_dt_new;
# 删除表格
DROP TABLE IF EXISTS user_dt_new;
三、创建用户 & 启用密码登录 & shell 命令
真正我们在产品中不会直接使用默认的 postgres 用户,而是自己创建一个用户。
有两种操作方法:
- 进入 PostgresSQL 的控制台操作
- 使用 PostgresSQL 提供的 shell 命令
第二部分,使用的是控制台操作,这一部分,我们要试试 shell 命令
创建名为 dbuser 的用户并设置密码
sudo -u postgres createuser dbuser -P
创建名为 mydb 的数据库,并设置其所有者为我们新创建的用户 dbuser
sudo -u postgres createdb -O dbuser mydb
这时候,如果我们尝试用新创建的用户 dubser 登录数据库 mydb
psql -U dbuser -d mydb -h localhost
会遇到下面的错误
psql: FATAL: Ident authentication failed for user "dbuser"
这是因为 PostgresSQL 默认没有启用密码登录,所以我们要先修改配置启用之
打开配置文件
sudo vim /var/lib/pgsql/data/pg_hba.conf
找到下面这两行
host all all 127.0.0.1/32 ident
host all all ::1/128 ident
将 ident 改为 md5
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
保存配置文件后,PostgresSQL
sudo systemctl restart postgresql
接下来,我们就可以用新创建的用户 dbuser 愉快地登录数据库啦
psql -U dbuser -d mydb -h localhost
注意 -h localhost 不能省略,否则 PostgresSQL 还会走 ident 的验证,会出现下面的错误
psql: FATAL: Peer authentication failed for user "dbuser"
四、允许远程访问(连接)
PostgresSQL 默认是不允许远程访问的,如果我们想要在别的机器上访问数据库,还需要做一些设置。
打开 postgresql.conf 文件
sudo vim /var/lib/pgsql/data/postgresql.conf
加入下面一行配置,表示允许来自任意 IP 的连接请求
listen_addresses = '*'
打开 pg_hba.conf 文件
sudo vim /var/lib/pgsql/data/pg_hba.conf
加入下面一行配置,表示对任意 IP 访问进行密码验证
host all all 0.0.0.0/0 md5
最后,重启 PostgreSQL 使配置生效
sudo systemctl restart postgresql
现在就可以远程访问 PostgreSQL 数据库啦