[Flask教程] 5.ORM与SQLAlchemy (1) – 建立第一个模型

后端一个重要的点就是与数据库联系,例如网页的注册、登录,内容的更新等都需要与数据库建立关系。以MySQL数据库为例,平时我们会用mysqldb(python 2)或者pymysql(python 3)去操作MySQL数据库,但这种方法也是需要自己编写SQL语句的。现在我们有了ORM模型,简单来说,ORM是把数据库中的表抽象成模型,表的列名对应模型的属性,这样我们可以调用类的属性或方法去获得数据库中的数据。例如假设MySQL数据库中有一张表名为table1,使用SELECT * FROM table1 WHERE id=1获取id为1的数据,如果将表table1映射成ORM模型Table,那么可以直接使用Table.query.filter(id=1),这样操作简单了很多,也很利于理解。

SQLAlchemy就是一个这样的ORM,我们可以直接安装flask_sqlalchemy来使用。在这之前我们先在MySQL中手动建立一个数据库harp,在建立的时候把charset设置为utf8,避免存入中文时变成乱码,然后在配置文件config.py中填写好数据库的连接信息:

HOST = "127.0.0.1"
PORT = "3306"
DB = "harp"
USER = "root"
PASS = "Your Password"
CHARSET = "utf8"
DB_URI = "mysql+pymysql://{}:{}@{}:{}/{}?charset={}".format(USER, PASS, HOST, PORT, DB, CHARSET)
SQLALCHEMY_DATABASE_URI = DB_URI

SQLAlchemy依赖mysqldb或者pymysql去连接数据库和执行SQL语句,因为我们用的是python 3,所以需要在配置信息中指明使用pymysql,如果是python 2可以省略,默认是使用mysqldb。

建立好了数据库,我们开始建表,首先建立一张用户表,我们设想它应该有id(作为主键)、用户名、密码、注册时间这些基本的字段,有了ORM,我们就不用再写SQL去建表了,在项目的主py文件中添加以下代码:

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

import config

app = Flask(__name__)
app.config.from_object(config)

db = SQLAlchemy(app)


class Users(db.Model):
    __tablename__ = 'users_info'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(32), nullable=False)
    password = db.Column(db.String(100), nullable=False)
    register_time = db.Column(db.DateTime, nullable=False, default=datetime.now())


db.create_all()

解读一下这段代码,导入SQLAlchemy和含有数据库连接信息的config,实例化一个SQLAlchemy对象名为db,其传入的参数为Flask实例app。接下来定义了一个User类,这个类就是ORM中的模型,也就是数据库中的表映射的模型,它需要继承自db.Model,tablename这个属性就是建表后,数据库生成的表名;然后使用db.Column来实例化id/username/password/register_time这几个列,db.Column的参数描述列的类型、主键等信息,如db.Integer/db.String(32)/db.DateTime分别代表整形、字符串(最大长度)、时间,primary_key=True说明该字段为主键,autoincrement=True代表自增长,nullable决定是否可为空,default代表默认值。最后用db.create_all()来实现创建。我们暂时不用理解为何SQLAlchemy需要传入Flask实例作为参数,为何模型要继承自db.Model,重要的是可以先把想要的表建立起来。

进入数据库,输入desc user_info;,我们发现表已经建立好了,其结构图如下:

未分类

但它现在还是空的,我们来试着插入一条语句,将视图函数修改为:

@app.route('/')
def index():
    user = Users(username='Harp', password='123456')
    db.session.add(user)
    db.session.commit()
    return render_template('home.html')

代码实例化一个Users的对象user,传入username和password,使用db.session.add(user)将其加入到数据库的session(可以理解为事务)中,然后使用db.session.commit()提交。我们运行程序,然后用浏览器访问,浏览器正常显示了结果,这时再看一眼数据库,发现这条数据已经写入到了数据库:

未分类

查询、修改数据也同样很简单:

@app.route('/')
def index():
    user = Users.query.filter(Users.id == 1).first()    #查找
    print(user.username)
    user.username = 'Harp1207'    #修改
    db.session.commit()    #修改后需提交
    print(user.username)
    return render_template('home.html')

思考问题:

  1. 为何要把模型的操作语句放在视图函数中?(搜索上下文这个概念)
  2. 数据查找,我们用的是Model.query,其实还可以用db.session.query,两者有何区别?filter和filter_by又有何区别?

[Flask教程] 4.模板的继承及Bootstrap实现导航条

在建设一个网站的时候,不同的页面有很多元素是一样的,比如导航条、侧边栏等,我们可以使用模板的继承,避免重复编写html代码。现在我们打算实现一个在网页上方的导航条,并在所有的页面中继承这个导航条。导航条的建立,我们直接使用Bootstrap提供的如下导航条的样式。

未分类

但在使用Bootstrap的导航条样式之前,需要先引用Bootstrap所需要的css和js文件以及jQuery,我们在html的header中插入以下代码完成引用:

<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous">

这里都是通过链接引用网络上的css和js文件,而不是将其下载下来并从本地引用。之后我们把上图中的所有html代码复制到html的body中,浏览器就能显示和图中一样的导航条了。我们再做一些简单的修改和优化,最终我们的导航条是这样的:

未分类

具体修改的点是,我把原始的Brand替换成了一个图片作为logo,第一个下拉控件Dropdown删掉了,最右侧的下拉控件增加了一个选项,并把文字都替换成了我们想要的。然后建立了一个base.css文件来调整图片大小、控件位置、背景色等等,这一部分都是基础的html/css知识,也就不多说。后续所有的网页都要使用这个导航条,我们将含有导航条这个html命名为base.html,并在其body中,导航条代码的下方增加以下代码:

{% block body_part %}
{% endblock %}

然后新建一个home.html,输入以下内容:

{% extends 'base.html' %}
{% block body_part %}
<p>This is body content under nav-bar</p>
{% endblock %}

渲染home.html并访问,我们可以看到这样的结果:

未分类

因此我们不难理解,在home.html中,{% extends ‘base.html’ %}表示继承自base.html,home.html中block和endblock区间的代码块会自动替换到base.html同样名为body_part的block区域。block可以使用多个,例如在中也可以使用,以便于不同的页面设置不同的标题。<br /> 最终base.html代码如下:</p> <pre><code><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <link rel="stylesheet" href="{{ url_for('static',filename='css/base.css') }}"/> <link rel="shortcut icon" href="{{ url_for('static', filename='images/favicon.ico') }}"> <title>{% block page_name %}{% endblock %}-HarpQA</title> </head> <body> <nav class="navbar navbar-default"> <div class="container"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand"> <img class="logo" src="{{ url_for('static',filename='images/logo.png') }}"> </a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li class="active"><a href="#">首页 <span class="sr-only">(current)</span></a></li> <li><a href="#">发布问答</a></li> </ul> <form class="navbar-form navbar-left"> <div class="form-group"> <input type="text" class="form-control" placeholder="Key Words"> </div> <button type="submit" class="btn btn-default">搜索</button> </form> <ul class="nav navbar-nav navbar-right"> <li><a href="#">登录</a></li> <li><a href="#">注册</a></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">友情链接<span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="mailto:liutao25@baidu.com">联系我</a></li> <li><a href="http://flask.pocoo.org" target="_blank">Flask官网</a></li> <li><a href="https://www.python.org/">Python官网</a></li> <li role="separator" class="divider"></li> <li><a href="https://www.baidu.com" target="_blank">百度搜索</a></li> <li role="separator" class="divider"></li> <li><a href="https://www.google.com.hk" target="_blank">Google Search</a></li> </ul> </li> </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> {% block body_part %} {% endblock %} </body> </html> </code></pre> <p>请注意一下base.css和logo图片的引用,我们也使用了url_for函数,第一个参数是static,代表项目文件夹下static文件夹,第二个参数是以static文件夹为基准静态文件的相对路径,我们把js文件/css文件/图片文件等都放在这个文件夹下,所以这个用法以后会经常使用。我们在收藏网页的时候,网页都有一个小图标,我们也可以在header中使用这行html代码来实现:</p> <pre><code><link rel="shortcut icon" href="{{ url_for('static', filename='images/favicon.ico') }}"> </code></pre> <p>把favicon.ico文件放在static/images文件夹即可,我们使用了Flask的图标,效果如下图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-4176820475cb8028cba5de1555563148.png" alt="未分类" width="120" height="33" class="aligncenter size-full wp-image-7238" /></p> <p>base.css代码如下:</p> <pre><code>body{ background: #F3F3F3; } .navbar-brand{ padding: 0 5px; padding-right: 10px; } .logo{ height: 50px; } </code></pre> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/flask-tutorial-4-inheritance-of-template-and-bootstrap-implementation-of-navigation-bar/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:31:30+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/bootstrap/" rel="tag">Bootstrap</a>、<a href="https://devops.webres.wang/tag/flask/" rel="tag">flask</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7234" class="post-7234 post type-post status-publish format-standard hentry category-undefine tag-flask tag-jinja2 tag-render_template"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/flask-tutorial-3-render-template-rendering-template-and-jinja2/" rel="bookmark">[Flask教程] 3.render_template渲染模板及jinja2</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <p>我们之前的视图函数,返回的都是简单的’Hello Wolrd’之类的字符串,怎么返回一个html呢?首先我们在templates文件夹建立一个html文件,内容随便写一点如下:</p> <pre><code><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Index</title> </head> <body> <h1>This is index page</h1> </body> </html> </code></pre> <p>我们可以使用Flask对象app的send_static_file方法,使视图函数返回一个静态的html文件,但现在我们不使用这种方法,而是使用flask的render_template函数,它功能更强大。<br /> 从flask中导入render_template,整体代码如下:</p> <pre><code>from flask import Flask, render_template import config app = Flask(__name__) app.config.from_object(config) @app.route('/') def index(): return render_template('index.html') if __name__ == '__main__': app.run() </code></pre> <p>render_template函数会自动在templates文件夹中找到对应的html,因此我们不用写完整的html文件路径。用浏览器访问’/’这个地址,显示结果如下:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-9530293985cb802888c8b71555563144.png" alt="未分类" width="316" height="142" class="aligncenter size-full wp-image-7230" /></p> <p>那么为何称之为模板呢?因为render_template不仅能渲染静态的html文件,也能传递参数给html,使一个html模板根据参数的不同显示不同的内容,这是因为flask使用了jinja2这个模板引擎。要使用模板,在render_template参数中以key=value形式传入变量,在html中使用{{key}}来显示传入的变量,例如:</p> <pre><code># 视图函数 @app.route('/') def index(): return render_template('index.html', contents='This is index page') # html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Index</title> </head> <body> <h1>{{ contents }}</h1> </body> </html> </code></pre> <p>浏览器显示的结果与上文是一样的。我们还可以直接把一个类的实例传递过去,并在模板中访问类的属性,例如假设一个类对象obj有a和b属性,关键部分的代码如下:</p> <pre><code># 视图函数中 return render_template('index.html', object=obj) ... # html中 <p>a: {{ object.a }}</p> <p>b: {{ object.b }}</p> </code></pre> <p>传入一个字典也可以,并且在模板中既可以用dict[key],也可以用dict.key。</p> <p>使用过滤器,可以在html中对传入的变量进行处理,其格式是{{ 变量 | 过滤器 }},例如将前文的{{ contents }}修改为{{ contents | upper}},浏览器显示的内容就变成了:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-20056848865cb80288d576a1555563144.png" alt="未分类" width="344" height="139" class="aligncenter size-full wp-image-7231" /></p> <p>所以我们可以很容易就理解,过滤器其实就是以变量为参数的函数,返回处理后的结果,在后端一样可以先用字符串对象的upper()函数处理好再传递给模板,效果是完全一样的。jinja2自带了一些过滤器,例如length/reverse/lower等等 ,并且我们也可以自己按照需求自定义过滤器,模板还支持{{ 变量 | 过滤器1 | 过滤器2 | … }}这样的操作。想要深入了解的话,可以搜索jinja2过滤器去进一步学习。</p> <p>模板中还可以使用if else和for in控制语句,与变量使用{{ }}不同,控制语句要放在{% %}里,例如前文的contents传入一个list:</p> <pre><code>contents=[i for i in range(10)] </code></pre> <p>html中代码如下:</p> <pre><code><h1> {% for i in contents %} {{ i }}{# 注意i也要用两个大括号 #} {% endfor %} </h1> </code></pre> <p>使用for遍历contents的内容,并用{{ i }}显示出来,同时还用{# #}加了一个注释,还要注意需要使用{% endfor %}来提示循环区域的结束,因为html不像python那样通过缩进来判断循环的区域,if也是同理。浏览器显示结果:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-3683356425cb80289270de1555563145.png" alt="未分类" width="310" height="136" class="aligncenter size-full wp-image-7232" /></p> <p>最后for和if结合使用:</p> <pre><code><h1>header</h1> {% for i in contents %} <p> {% if i%2 == 0 %} {{ i }}是偶数{# 注意i也要用两个大括号 #} {% else %} {{ i }}是奇数 {% endif %} </p> {% endfor %} </code></pre> <p>结果如下(只是演示一下,不要吐槽排版和美观):</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-3958481005cb802896cd471555563145.png" alt="未分类" width="256" height="333" class="aligncenter size-full wp-image-7233" /></p> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/flask-tutorial-3-render-template-rendering-template-and-jinja2/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:28:43+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/flask/" rel="tag">flask</a>、<a href="https://devops.webres.wang/tag/jinja2/" rel="tag">jinja2</a>、<a href="https://devops.webres.wang/tag/render_template/" rel="tag">render_template</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7229" class="post-7229 post type-post status-publish format-standard hentry category-undefine tag-flask tag-redirect tag-url_for"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/flask-tutorial-2-reversal-function-url-for-and-redirect/" rel="bookmark">[Flask教程] 2.反转函数url_for与重定向redirect</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <p>在flask中,我们导入url_for和redirect两个函数。</p> <pre><code>from flask import Flask, url_for, redirect </code></pre> <p>首先看url_for,简单来说,这个函数接受视图函数的名字(字符串形式)作为参数,返回视图函数对应的url,例如:</p> <pre><code>@app.route('/') def hello_world(): print(url_for('index')) return 'Hello World' @app.route('/index/') def index(): return 'index' </code></pre> <p>在hello_world函数中使用print(url_for(‘index’)),将会打印出/index/。<br /> 有传参的视图函数怎么办?同样将函数名字符串作为第一个参数,将参数以key=value的形式写在后面,如:</p> <pre><code>@app.route('/') def hello_world(): print(url_for('hello',name='harp')) return 'Hello World' @app.route('/<name>/') def hello(name): return 'Hello %s' % name </code></pre> <p>打印结果为/harp/。</p> <p>redirect则更简单,功能就是跳转到指定的url,大部分情况下,我们都是和url_for一起使用的,例如:</p> <pre><code>@app.route('/') def hello_world(): return 'Hello World' @app.route('/<name>/') def hello(name): if name == 'Harp': return 'Hello %s' % name else: return redirect(url_for('hello_world')) </code></pre> <p>在hello这个视图函数中,如果url传入的参数是Harp(即请求的网址是http://127.0.0.1:5000/Harp/),则返回’Hello Harp’,其他情况则重定向到hello_world这个视图函数对应的网址’/’。</p> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/flask-tutorial-2-reversal-function-url-for-and-redirect/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:25:04+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/flask/" rel="tag">flask</a>、<a href="https://devops.webres.wang/tag/redirect/" rel="tag">redirect</a>、<a href="https://devops.webres.wang/tag/url_for/" rel="tag">url_for</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7228" class="post-7228 post type-post status-publish format-standard hentry category-undefine tag-debug tag-flask tag-url"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/flask-tutorial-1-the-first-flask-program-debug-mode-and-url-reference/" rel="bookmark">[Flask教程] 1.第一个Flask程序、DEBUG模式及URL传参</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <p>–引言请见我的简书https://www.jianshu.com/p/e27…</p> <p>我们打开PyCharm,新建一个Project,在左侧选择Flask,右侧Location可选择项目存放的位置,Project Interpreter选择使用的编译器,我们当然可以选择已经安装在电脑上的python 3.6,但我建议选择使用Virtualenv来建立一个虚拟的环境,这样在虚拟环境里安装包之类的,不会影响电脑上本身已经装的,并且后续如果项目较多的话也便于区分和管理。</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-9640389665cb802846d3a31555563140.png" alt="未分类" width="554" height="348" class="aligncenter size-full wp-image-7226" /></p> <p>点击Create,创建好之后,pycharm自动在项目的文件夹下建立了static/templates文件夹和HarpQA.py,此外还有一个venv文件夹,是虚拟环境用的,我们可以暂时不用管它。(如果你的PyCharm是社区版的,那么无法像专业版那样建立Flask项目,对应的文件夹需要自己手动建立。)HarpQA.py中的代码如下:</p> <pre><code>from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': app.run() </code></pre> <p>首先从flask导入Flask,(我并没有pip install flask,为何能导入呢?我理解是建立flask项目的时候PyCharm自动帮我们做了这个事情),然后初始化一个Flask对象app,参数是<strong>name</strong>,<strong>name</strong>代表的是本身这个模块的名字,我们暂时不用理解为何要传入这个参数。接下来是一个hello world函数,并且有一个装饰器@app.route(‘/’),意思是说,当接收到’/’这个网址的请求时,执行hello world这个函数,即返回字符串’Hello World!’(add_url_rule()也可以实现和@app.route一样的功能 ,但使用装饰器应该更pythonic吧),最后使用app.run()运行。运行脚本后,提示Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)<br /> 我们在浏览器中输入http://127.0.0.1:5000/,就可以在网页中看到’Hello World!’了。</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-216373615cb80284bc3201555563140.png" alt="未分类" width="272" height="101" class="aligncenter size-full wp-image-7227" /></p> <p>我们还可以使用debug模式来运行flask的服务端,开启debug模式后,修改代码不需要关闭程序,可以实时生效。有多种方法来开启debug模式:</p> <ol> <li>在app.run()中添加参数app.run(debug=True);</li> <li>在run之前增加app.debug = True;</li> <li>新建config文件,在config文件中添加DEBUG = True,然后在程序中引入app.config.from_object(config);</li> <li>在run之前增加app.config[‘DEBUG’] = True;</li> </ol> <p>我们使用第3种方法,新建一个独立的文件来保存各种参数,以后项目增大的时候参数增多,便于管理。同样在项目文件夹下新建config.py文件,添加DEBUG = True,DEBUG需大写。在HarpQA.py中import config并添加app.config.from_object(config),再次运行HarpQA.py,提示:</p> <pre><code> * Restarting with stat * Debugger is active! * Debugger PIN: 229-291-890 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) </code></pre> <p>说明Debug模式已经打开,我们可以直接修改代码,例如把’Hello World!’<br /> 修改成’Hello’并保存,显示提示:</p> <pre><code> * Detected change in 'D:\Flask\HarpQA\HarpQA.py', reloading * Restarting with stat * Debugger is active! * Debugger PIN: 229-291-890 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) </code></pre> <p>说明DEBUG模式检测到了代码的变化并自动重载了,这时候刷新网页,结果也变成了只显示’Hello’。</p> <p>如果我们要处理很多URL,可以一个一个去给他们写对应的视图函数,这有时候是不切实际的,我们有更高效的做法,如以下代码:</p> <pre><code>@app.route('/<name>/') def hello(name): return 'Hello %s' % name </code></pre> <p>@app.route中装饰的地址是’//’,注意name外面用了尖括号,代表name是一个参数,例如我们请求http://127.0.0.1:5000/Harp/这个网址,其中的name就是Harp,这个参数会传递给视图函数hello,最终会返回’Hello Harp’。我们可以给将写成,表示传入的参数是字符串类型的,其他的类型还有int,float,path,几种类型的区别可以在网上搜索对比一下,例如path可以将参数里的’/’也传递过来。</p> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/flask-tutorial-1-the-first-flask-program-debug-mode-and-url-reference/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:22:23+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/debug/" rel="tag">DEBUG</a>、<a href="https://devops.webres.wang/tag/flask/" rel="tag">flask</a>、<a href="https://devops.webres.wang/tag/url/" rel="tag">URL</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7225" class="post-7225 post type-post status-publish format-standard hentry category-undefine tag-cesi tag-supervisor tag-web"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/supervisor-centralized-management-web-tool-cesi/" rel="bookmark">supervisor集中化管理web工具cesi</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <p>linux进程管理器supervisor是会经常被用到的,但服务器多了之后,每个服务器的进程也不方便管理。同时,supervisor自带的web界面比较简陋,所以尝试了一下官网推荐的一些第三方开源软件,推荐一下这个cesi。</p> <p>最终效果如下:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-10068921085cb80281d98641555563137.jpg" alt="未分类" width="768" height="362" class="aligncenter size-full wp-image-7223" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-4480690785cb802823ce771555563138.jpg" alt="未分类" width="768" height="215" class="aligncenter size-full wp-image-7224" /></p> <p><strong>1. 首先是关于supervisor</strong></p> <p>通过apt或pip安装都可以</p> <pre><code>apt-get install supervisor #pip install supervisor echo_supervisord_conf > /etc/supervisor/supervisord.conf </code></pre> <p>关于supervisor配置文件</p> <pre><code>[unix_http_server] #这里配置是否用unix socket通信来让supervisor与supervisorctl做通信 [inet_http_server] #这里是用的http的方式做通信 [supervisorctl] #这里选择supervisorctl到底用以上两种中的哪种方式来与supervisor通信,选择一种即可,记得填写密码 [program:pro_name] stdout_logfile = {path} redirect_stderr = true ;让stderr也写入stdout中 </code></pre> <p>常用操作</p> <pre><code>supervisorctl reload #重启加载supervisor配置文件 supervisorctl update #只增加新增的配置文件 </code></pre> <p><strong>2. 关于cesi</strong></p> <p>项目地址:https://github.com/Gamegos/cesi</p> <p>安装cesi</p> <pre><code>apt-get install sqlite3 python python-flask git clone https://github.com/Gamegos/cesi cd cesi sqlite3 ./userinfo.db < userinfo.sql cp cesi.conf /etc/cesi.conf </code></pre> <p>配置cesi.conf,我的配置如下,一看就懂了</p> <pre><code>[node:118] username = *** password = *** host = 127.0.0.1 port = 9001 [node:calmkart] username = *** password = *** host = 45.76.71.69 port = 9001 [node:121] username = *** password = *** host = *.*.*.121 port = 9001 [environment:my_env] members = 118,calmkart,121 [cesi] database = /root/cesi/userinfo.db activity_log = /var/log/cesi.log host = 0.0.0.0 </code></pre> <p>用supervisor运行cesi,配置文件如下</p> <pre><code>[program:cesi] directory = /root/cesi/cesi/ command = python web.py autostart = true startsecs = 5 autorestart = true startretries = 3 user = root redirect_stderr = true stdout_logfile = /var/log/cesi1.log </code></pre> <p>开启任务</p> <pre><code>supervisorctl start cesi </code></pre> <p>默认账号密码:admin,admin<br /> 端口需要改的自己去web.py里面改</p> <p>you get it</p> <p>本测试服地址:<br /> http://cesi.calmkart.com</p> <p>此外,其他常用的supervisor相关第三方工具还有</p> <ul> <li>suponoff</li> <li>gosuv</li> </ul> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/supervisor-centralized-management-web-tool-cesi/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:17:14+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/cesi/" rel="tag">cesi</a>、<a href="https://devops.webres.wang/tag/supervisor/" rel="tag">supervisor</a>、<a href="https://devops.webres.wang/tag/web/" rel="tag">web</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7222" class="post-7222 post type-post status-publish format-standard hentry category-undefine tag-gunicorn tag-nginx-2 tag-python-3 tag-webpy"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/python-programming-webpy-gunicorn-nginx-deployment/" rel="bookmark">python编程(webpy + gunicorn + nginx部署)</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <p>之前虽然也用nginx + uwsgi + webpy的方法部署过网站,但是用了gunicorn之后,发现用这种方法部署网站更为简单。下面我详细描述一下如何用这种方法进行网站部署。</p> <h2>1、准备server.py</h2> <p>和uwsgi部署的时候一样,这里仅仅需要设置一个application就可以了。</p> <pre><code>#!/usr/bin/python import web urls = ('/', 'Hello') class Hello(object): def GET(self): return 'Hello world' app = web.application(urls, globals()) application = app.wsgifunc() </code></pre> <h2>2、安装gunicorn</h2> <p>安装gunicorn的方法非常简单,在ubuntu下面一条命令就可以解决,</p> <pre><code>sudo apt-get install gunicorn </code></pre> <h2>3、用gunicorn启动server.py文件</h2> <p>启动的时候注意,最后一个选项是由文件名+wsgifunc组成的。同时,我们在选项中添加了gevent的属性,gunicorn本身支持gevent机制,可以有效提高server的性能。</p> <pre><code>gnicorn -b 127.0.0.1:8080 --worker-class gevent server:application </code></pre> <h2>4、用浏览器做测试</h2> <p>这个时候不出意外,你已经可以用127.0.0.1:8080访问我们的网站了。</p> <h2>5、准备nginx.conf文件</h2> <p>通常为了利用nginx做static文件加速,或者利用nginx做均衡负载,我们常常需要另外安装一下nginx软件。因此,此时nginx.conf必须准备好。当然,为了简单起见,我们这里只做一个代理就可以了,整个conf文件内容如下,</p> <pre><code>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; } } } </code></pre> <h2>6、重启启动nginx</h2> <p>nginx.conf准备好后,这个时候先将它copy到/etc/nginx目录下。接下来,我们需要重新启动nginx软件,一个命令就可以了,</p> <pre><code>service nginx restart </code></pre> <h2>7、用浏览器测试80端口</h2> <p>有了nginx做代理,这个时候就可以用浏览器访问127.0.0.1了,因为一般网站默认用80做端口,所以没有意外的话,这个时候你就可以看到webpy给出的打印消息了。</p> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/python-programming-webpy-gunicorn-nginx-deployment/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:13:59+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/gunicorn/" rel="tag">gunicorn</a>、<a href="https://devops.webres.wang/tag/nginx-2/" rel="tag">nginx</a>、<a href="https://devops.webres.wang/tag/python-3/" rel="tag">python</a>、<a href="https://devops.webres.wang/tag/webpy/" rel="tag">webpy</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7221" class="post-7221 post type-post status-publish format-standard hentry category-undefine tag-django tag-gunicorn tag-supervisor"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/supervisor-and-gunicorn-deploy-django-project/" rel="bookmark">supervisor 和gunicorn部署django项目</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <p>安装使用到的基本软件nginx、supervisor、gunicorn</p> <pre><code>vi /etc/supervisor/conf.d/django_project.conf </code></pre> <pre><code>[program:django_project] command=gunicorn xxx.wsgi:application -b 127.0.0.1:8080 -w 8 user=user #当前用户 directory=/home/user/django_project stdout_logfile=/tmp/var/logs/supervisor/%(program_name)s-stdout.log stderr_logfile=/tmp/var/logs/supervisor/%(program_name)s-stderr.log killasgroup=true stopasgroup=true autorstart=true autorestart=true # rq队列的配置 [program:rqworker] command=python manage.py rqworker default low user=user directory=/home/django_project/platform stdout_logfile=/tmp/var/logs/supervisor/%(program_name)s-stdout.log stderr_logfile=/tmp/var/logs/supervisor/%(program_name)s-stderr.log killasgroup=true stopasgroup=true autorstart=true autorestart=true``` </code></pre> <p>再在nginx配置中的location,添加</p> <p>proxy_pass http://127.0.0.1:8080;</p> <p>另外静态文件的代理可以添加下面的配置</p> <pre><code>location /static/ { alias /django_project/statics/; } </code></pre> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/supervisor-and-gunicorn-deploy-django-project/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:10:25+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/django/" rel="tag">Django</a>、<a href="https://devops.webres.wang/tag/gunicorn/" rel="tag">gunicorn</a>、<a href="https://devops.webres.wang/tag/supervisor/" rel="tag">supervisor</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7220" class="post-7220 post type-post status-publish format-standard hentry category-undefine tag-postgresql"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/postgresql-shared-cache-management/" rel="bookmark">PostgreSQL共享缓存区管理</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <h2>一、共享缓冲区</h2> <p>KingbaseES中的buffer主要是用来将外存中的数据内容读入到内存中,加速运算过程中对数据的访问速度,同时将数据的修改进行缓存,在必要时再将其写出到外存,避免频繁的I/O,以提高效率。<br /> Buffer的种类有很多如Audit buffers、Clog buffers、Data buffers和Xlog buffers,此处所介绍的buffer管理是针对Data buffers而言的。</p> <h2>二、数据结构</h2> <ul> <li>BufferTag</li> <li>BufferDesc</li> <li>BufferStrategyControl</li> </ul> <h3>1、BufferTag</h3> <pre><code>typedef struct buftag { Oid dbid; /* database identifier */ FileBlock blockNum; /* file and blocknumber */ } BufferTag; </code></pre> <h3>2、BufferDesc</h3> <pre><code>typedef struct sbufdesc { BufferTag tag; /* ID of page contained in buffer */ RelFileNode rnode; /* relation this block belongs to */ BufFlags flags; /* see bit definitions above */ uint16 usage_count; /* usage counter for clock sweep code */ unsigned refcount; /* # of backends holding pins on buffer */ int wait_backend_pid; /* backend PID of pin-count waiter */ slock_t buf_hdr_lock; /* protects the above fields */ int buf_id; /* buffer's index number (from 0) */ int freeNext; /* link in freelist chain */ LWLockId io_in_progress_lock; /* to wait for I/O to complete */ LWLockId content_lock; /* to lock access to buffer contents */ } BufferDesc; </code></pre> <h3>3、引用计数(BufferDesc.refcount)</h3> <p>引用计数(refcount)用于跟踪访问buffer的后台数量,防止错误的将正在被使用的Buffer淘汰。当使用Buffer时,需要将其引用计数(refcount)加1(PinBuffer)。当Buffer不再使用,需要将其引用计数(refcount)减1(UnpinBuffer)。这里需要注意,由于一个后台可以多次访问同一个Buffer,因此后台通过PrivateRefCount来记录自己的引用次数,只有当自己对一个Buffer的引用减少到0,才会真正去修改refcount。PrivateRefCount在后台PinBuffer时将其值加1,UnpinBuffer时将其值减1。</p> <h3>4、使用计数(BufferDesc.usage_count)</h3> <p>usage_count用来标记Buffer被使用的次数,usage_count值越大,说明该Buffer经常被使用,那么在未来的一段时间里被使用的可能就比较大,所以这样的Buffer不能作为被替换的对象;相反,usage_count值越小,说明经常不被使用,可以作为替换的对象。在KingbaseES中,只有当usage_count为0时,才可能作为替换的对象。<br /> usage_count是在一个后台不再使用该Buffer即UnpinBuffer将后台的PrivateRefCount减少为0的时候将其值加1,以表示该Buffer最近被一个后台使用了。对VACUUN操作来说,不会修改usage_count的值,且如果refcount和usage_count的值都为0,则将buffer放入到FreeList的尾部。</p> <h3>5、BufferStrategyControl</h3> <pre><code>typedef struct { int nextVictimBuffer; // 指向下一Buffer int firstFreeBuffer; // 第一个空闲缓冲块id int lastFreeBuffer; // 最后一个空闲缓冲块id } BufferStrategyControl; /* Pointers to shared state */ static MT_LOCAL BufferStrategyControl *StrategyControl = NULL; </code></pre> <h3>6、Buffer Descriptors</h3> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-11250682355cb8027ae72fd1555563130.png" alt="未分类" width="873" height="546" class="aligncenter size-full wp-image-7216" /></p> <h2>三、主要函数</h2> <ul> <li>InitBufferPool</li> <li>BufferAlloc</li> <li>StrategyGetBuffer</li> <li>FlushBuffer</li> <li>PinBuffer</li> <li>UnpinBuffer</li> </ul> <h3>1、InitBufferPool流程</h3> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-18796337075cb8027b381dc1555563131.png" alt="未分类" width="278" height="546" class="aligncenter size-full wp-image-7217" /></p> <h3>2、BufferAlloc流程</h3> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-21230926085cb8027b87ff11555563131.png" alt="未分类" width="729" height="678" class="aligncenter size-full wp-image-7218" /></p> <h2>四、缓冲区替换策略</h2> <ul> <li>FreeList</li> <li>Clock-sweep</li> <li>buffer-ring</li> </ul> <h3>1、FreeList</h3> <p>当执行DROP TABLE时,可以确定该表的所有buffer都会失效,因此将此表的所有buffer都放入到Freelist的头部,这样可以在下一次分配buffer时,直接从Freelist中得到buffer,而不需要执行Clock Sweep算法。</p> <h3>2、Clock-sweep</h3> <p>当Buffer的refcount计数变成0的时候,代表当前系统没有后台引用此数据块。在KingbaseES中,为了能够减低锁的粒度、提高并发性,引用计数等于0的的Buffer并没有被放入Freelist中。在随机访问大量磁盘块、并且没有VACUUM的干扰下,Freelist几乎是空的(除了刚刚启动时)。这里的策略主要是为了避免不必要的持有操作Freelist的互斥锁。<br /> 由于大部分时候Buffer不会立即被放入到Freelist中,因此使用了一种被称为Clock Sweep的算法来分配Buffer。此算法类似教科书中时钟算法,每当需要使用Clock Sweep算法选择一个Buffer时,就从上次分配的Buffer的下一个位置开始,搜索引用计数为0(既没有被pin的Buffer)且usage_count为0的Buffer。如果该Buffer不满足上述条件,就将usage_count减1。</p> <h3>3、Clock-sweep</h3> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-10425009965cb8027be232c1555563131.png" alt="未分类" width="552" height="325" class="aligncenter size-full wp-image-7219" /></p> <p>在上图中Clock Sweep算法从4号buffer开始查找(记录在StrategyControl结构体中)可用的buffer。4号buffer因为引用计数大于0,因此不能被替换。5号buffer虽然没有人引用,但是其usage_count大于0,因此表示此buffer使用频率较高,因此将usage_count减1,并查看6号buffer。6号buffer的引用计数和usage_count都为0,因此选择将6号buffer淘汰。记录下一次搜索的位置是7号,并退出选择算法。</p> <h3>4、buffer-ring</h3> <p>批量读或者vacuum等操作可能会需要占据大量的buffer,影响其他正常业务。buffer-ring机制在批量读等占用的buffer数量达到某个程度(比如总buffer的1/4)时,分配给该操作固定的buffer数量,之后只能使用为其分配的buffer,而不能替换其他buffer。</p> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/postgresql-shared-cache-management/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T15:04:58+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/postgresql/" rel="tag">PostgreSQL</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <article id="post-7215" class="post-7215 post type-post status-publish format-standard hentry category-undefine tag-centos tag-linux tag-postfix"> <header class="entry-header"> <h2 class="entry-title"><a href="https://devops.webres.wang/2018/01/building-posfix-server-and-its-configuration-application-under-linux-system-and-centos7-version/" rel="bookmark">Linux系统,Centos7版本下搭建postfix服务器及其相关配置应用</a></h2> </header><!-- .entry-header --> <div class="entry-content"> <h2>实验报告</h2> <p>一、 实验名称:邮件服务器的搭建和相关使用功能的配置</p> <p>二、 实验环境与要求:Linux系统 centos7版本</p> <p>搭建邮件服务器实现发信收信基本功能<br /> 实现发信认证功能<br /> 搭建好LAMP环境,配置squirrelmail收发邮件</p> <p>三、 实验内容:</p> <p>1、 检查版信息,postfix安装情况与支持的功能,启动运行;<br /> 2、 搭建与之相关的DNS服务器,配置DNS解析功能;<br /> 3、 配置postfix基本发信功能,进行测试;<br /> 4、 安装dovecot提供收信服务,进行测试;<br /> 5、 客户端利用邮箱软件配置测试服务器功能;<br /> 6、 进行发信认证配置;<br /> 7、 搭建LAMP环境,配置squirrelmail收发邮件</p> <h2>实验步骤</h2> <h3>第一步</h3> <pre><code>cat /etc/redhat-release //检查版本信息 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-16009763125cb80268523ab1555563112.png" alt="未分类" width="693" height="125" class="aligncenter size-full wp-image-7174" /></p> <p>※安装postfix(版本自带) centos7版本即使用最小化安装仍自带此功能</p> <pre><code>postconf -a // 验证是否支持cyrus dovecot功能 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-2128036935cb80268a2b1d1555563112.png" alt="未分类" width="474" height="141" class="aligncenter size-full wp-image-7175" /></p> <p>启动服务器systemctl start postfix</p> <pre><code>netstat -anpt | grep 25 //查看监听端口号 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-18774432715cb80269002411555563113.png" alt="未分类" width="964" height="198" class="aligncenter size-full wp-image-7176" /></p> <h3>第二步</h3> <p>※搭建与之相关的DNS服务器,配置DNS解析功能</p> <pre><code>yum install bind //安装服务器程序 </code></pre> <pre><code>vi /etc/named.conf //编辑主配置文件 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-11685913975cb80269715171555563113.png" alt="未分类" width="1222" height="284" class="aligncenter size-full wp-image-7177" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-4820930425cb80269daac11555563113.png" alt="未分类" width="694" height="405" class="aligncenter size-full wp-image-7178" /></p> <p>※以下进行区域文件配置:</p> <pre><code>cd /var/named/ 进入文件目录下 </code></pre> <p>复制配置文件的模板进行修改:</p> <pre><code>cp -p named.localhost yyf.com.zone cp -p named.localhost yyf.com.local </code></pre> <pre><code>vi /var/named/yyf.com.zone 编辑正向区文件 </code></pre> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-8843049335cb8026a34a751555563114.png" alt="未分类" width="1056" height="329" class="aligncenter size-full wp-image-7179" /></p> <pre><code>vi /var/named/yyf.com.local 编辑反向区文件 </code></pre> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-5118321415cb8026a83c831555563114.png" alt="未分类" width="1051" height="352" class="aligncenter size-full wp-image-7180" /></p> <p>以上基本配置编辑完成</p> <pre><code>systemctl start named //启动服务器 netstat -anpu | grep name //检测到UDP 53端口在监听 服务器正常工作。 </code></pre> <p>进行DNS验证</p> <pre><code>vi /etc/resolv.conf nameserver 192.168.80.18 //指向DNS服务器地址 rpm -ivh /mnt/Packages/bind-utils-9.9.4-50.el7.x86_64.rpm //安装检测命令nslookup </code></pre> <p>解析如图:</p> <pre><code>~nslookup mail.yyf.com //正向解析 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-5613077345cb8026adf0b51555563114.png" alt="未分类" width="646" height="201" class="aligncenter size-full wp-image-7181" /></p> <pre><code>~nslookup 192.168.80.18 //反向解析 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-4700669135cb8026b4a12a1555563115.png" alt="未分类" width="942" height="194" class="aligncenter size-full wp-image-7182" /></p> <p>解析成功!</p> <h3>第三步</h3> <p>—–配置postfix基本发信功能</p> <p>vi /etc/postfix/main.cf 编辑配置文件 修改项如下:</p> <pre><code>myhostname = mail.aa.com //本机系统主机名 mydomain = aa.com //主机域名 myorigin = $mydomain //根源、起点 inet_interfaces = 192.168.80.18, 127.0.0.1 //接口地址 inet_protocols = ipv4 mydestination = $myhostname, $mydomain //预定、指定范围 home_mailbox = Maildir/ //信箱家目录 </code></pre> <p>完成后保存退出,用postfix check命令检查是否存在语法错误:</p> <pre><code>service postfix restart //重启邮件服务器 </code></pre> <p>※要测试发信功能是否生效,我们需要添加用户并加入同一组管理:<br /> —–增加邮件测试账号</p> <pre><code>groupadd mailusers //增加组账号 useradd -g mailusers -s /sbin/nologin jack //建用户jack 加入mailusers组且不可登陆系统 passwd jack(密码随便设置123) useradd -g mailusers -s /sbin/nologin tom //建用户tom 加入mailusers组且不可登陆系统 passwd tom(密码随便设置123) </code></pre> <p>测试准备:安装yum install telnet<br /> 开始测试:连接服务器的25端口进行简单发信测试<br /> telnet mail.yyf.com 25</p> <p>依次输入以下内容:</p> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-4942884885cb8026bd0fd31555563115.png" alt="未分类" width="696" height="557" class="aligncenter size-full wp-image-7183" /></p> <p>由于目前未安装收件服务,所以只能通过root超级管理员来查看邮件是否发送成功</p> <p>使用root查看tom用户是否收到测试邮件:</p> <pre><code>ls -l /home/tom/Maildir/new/ 在此目录下应该会有刚才发的邮件 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-9951463945cb8026c3a8291555563116.png" alt="未分类" width="1417" height="101" class="aligncenter size-full wp-image-7184" /></p> <p>可用cat命令查看内容</p> <p>邮件发送功能测试成功!</p> <h3>第四步</h3> <p>※安装dovecot提供收信服务</p> <pre><code>yum install dovecot //安装系统程序 </code></pre> <pre><code>vi /etc/dovecot/dovecot.conf //配置程序文件 </code></pre> <p>※修改项如下:</p> <pre><code>protocols = imap pop3 lmtp listen = //监听 所有端口 !include conf.d/10-auth.conf //此处* 必须替换 </code></pre> <p>※手动增加以下内容:</p> <pre><code>ssl = no disable_plaintext_auth = no mail_location = maildir:~/Maildir </code></pre> <p>※以上基本配置完成</p> <pre><code>service dovecot start //启动服务 netstat -anpt | grep dovecot // 110 143端口需要监听 </code></pre> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-15194853995cb8026c9fb3f1555563116.png" alt="未分类" width="995" height="299" class="aligncenter size-full wp-image-7185" /></p> <p>服务启动成功,正常工作</p> <p>※现在可以用户进行测试收信了(以前是用root,现在可以用账户)<br /> 测试开始:telnet mail.yyf.com 110</p> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-1067309555cb8026d182bb1555563117.png" alt="未分类" width="679" height="572" class="aligncenter size-full wp-image-7186" /></p> <p>我们来读取第一份邮件看看</p> <p>显示如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-4758855555cb8026d924371555563117.png" alt="未分类" width="554" height="467" class="aligncenter size-full wp-image-7187" /></p> <p>用quit 可以退出<br /> 测试结构说明收件系统工作正常</p> <p>鉴于目前的收信发信方式太不友好,安装MUA软件连接服务器收发邮件</p> <p>※客户端装测设软件:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-18243163165cb8026de43311555563117.png" alt="未分类" width="117" height="103" class="aligncenter size-full wp-image-7188" /></p> <p>用此软件进行测试,看服务器是否正常工作<br /> 基本设置如图所示:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-13771194845cb8026e5ba0a1555563118.png" alt="未分类" width="515" height="285" class="aligncenter size-full wp-image-7189" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-3427593855cb8026ed276c1555563118.png" alt="未分类" width="511" height="511" class="aligncenter size-full wp-image-7190" /></p> <p>登陆后收件:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-9701595165cb8026f630531555563119.png" alt="未分类" width="1281" height="486" class="aligncenter size-full wp-image-7191" /></p> <p>邮件接受正常<br /> 服务器工作正常</p> <h3>第五步</h3> <p>以下进行发信认证配置</p> <pre><code>yum install cyrus-sasl* 安装相关软件包 </code></pre> <p>vi /etc/sasl2/smtpd.conf 开始编辑配置文件,内容如下:</p> <pre><code>pwcheck_method: saslauthd mech_list: plain login log_level:3 //设置日志级别为3 </code></pre> <pre><code>vi /etc/sysconfig/saslauthd //编辑认证系统配置文件 MECH=shadow service saslauthd start //开启认证功能服务器 </code></pre> <pre><code>vi /etc/postfix/main.cf //编辑配置文件 在末尾新增 smtpd_sasl_auth_enable = yes //开启认证 smtpd_sasl_security_options = noanonymous //不允许匿名发信 mynetworks = 127.0.0.0/8 //允许的网段,如果增加本机所在网段就会出现允许 不验证也能向外域发信 smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject_unauth_destination //允许本地域以及认证成功的发信,拒绝认证失败的发信 postfix check //检查语法 service postfix restart //重启服务器 </code></pre> <p>※测试普通发信</p> <pre><code>telnet mail.yyf.com 25 </code></pre> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-486784075cb8026fe11971555563119.png" alt="未分类" width="908" height="364" class="aligncenter size-full wp-image-7192" /></p> <p>向未认证区域发件失败说明认证系统是生效的<br /> quit 退出</p> <p>※下面用系统账户进行发信测试</p> <p>将用户名与密码生成密文加密:</p> <pre><code>printf "jack" | openssl base64 //生成密文 amFjaw== (密文) printf "123" | openssl base64 //生成密文 MTIz (密文) </code></pre> <p>※开始用字符终端测试认证发信:</p> <pre><code>telnet mail.aa.com 25 </code></pre> <p>如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-2736457035cb8027050adb1555563120.png" alt="未分类" width="669" height="373" class="aligncenter size-full wp-image-7193" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-15705159765cb80270b68eb1555563120.png" alt="未分类" width="676" height="334" class="aligncenter size-full wp-image-7194" /></p> <p>若在真机用邮箱软件测试,只需做如下操作:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-11986136005cb80271108691555563121.png" alt="未分类" width="442" height="256" class="aligncenter size-full wp-image-7195" /></p> <p>将高级设置中的选项勾选即可验证</p> <h3>第六步</h3> <p>—-以下是配置squirrelmail收发邮件内容<br /> 需要搭建好LAMP环境</p> <pre><code>--------安装LAMP--------- yum install -y httpd mariadb-server mariadb php php-mysql php-gd libjpeg* php-ldap php-odbc php-pear php-xml php-xmlrpc </code></pre> <p>php-mhash 注: 安装相关软件包,其中表示强制换行操作</p> <pre><code>vi /etc/httpd/conf/httpd.conf //编辑配置文件 </code></pre> <p>需要修改的内容:</p> <pre><code>ServerName www.aa.com ///服务器名称 DirectoryIndex index.html index.php ///支持静态与动态网页 </code></pre> <pre><code>vi /etc/php.ini //编辑此配置文件 date.timezone = PRC //设置时区 </code></pre> <p>提醒:系统中的防火墙与安全Linux要确认关闭,否则影响实验</p> <pre><code>systemctl stop firewalld.service setenforce 0 systemctl start httpd.service 网页系统启动 systemctl start mariadb.service 数据库系统启动 netstat -anpt | grep 80 netstat -anpt | grep 3306 //端口在监听,说明服务器启动正常 </code></pre> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-18516945415cb80271547ac1555563121.png" alt="未分类" width="511" height="110" class="aligncenter size-full wp-image-7196" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-13111783805cb802719c1f11555563121.png" alt="未分类" width="706" height="107" class="aligncenter size-full wp-image-7197" /></p> <p>※mysql_secure_installation 进行安全校验,也可不设置</p> <p>接下来</p> <pre><code>vi /var/www/html/index.php 编辑数据库文件 </code></pre> <p>在里面写入脚本如下:</p> <pre><code><?php phpinfo(); ?> </code></pre> <p>用真机浏览器测试:</p> <p>http://192.168.80.18 将出现如下页面,说明设置成功</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-18766365865cb802721f44c1555563122.png" alt="未分类" width="1424" height="857" class="aligncenter size-full wp-image-7198" /></p> <p>※进入数据库,建立授权账号</p> <pre><code>mysql -u root -p </code></pre> <p>进数据库时设置一下密码:123456</p> <pre><code>CREATE DATABASE bcd; //建立数据库名字bcd GRANT all ON bcd.* TO 'mail'@'%' IDENTIFIED BY '123456'; flush privileges; //刷新一下权限 </code></pre> <p>下面测试数据库工作是否正常</p> <pre><code>vi /var/www/html/index.php //编辑配置文件,该文件为数据库工作目录 </code></pre> <p>删除以前内容,写入如下内容:</p> <pre><code><?php $link=mysql_connect('192.168.80.18','mail','123456'); //说明地址与密码 if($link) echo "<h1>Success!!</h1>"; //如果连接成功则会显示Success!! else echo "Fail!!"; //如果连接失败则显示Fail!! mysql_close(); ?> </code></pre> <p>保存退出</p> <p>测试:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-18000485335cb80272721401555563122.png" alt="未分类" width="431" height="170" class="aligncenter size-full wp-image-7199" /></p> <p>连接成功!</p> <p>到下面网址下载小松鼠包与汉化语言包</p> <p>http://www.squirrelmail.org/download.php</p> <p>1、squirrelmail-webmail-1.4.22.tar.gz<br /> 2、all_locales-1.4.18-20090526.tar.gz</p> <p>将安装包上传到Linux系统中进行解压</p> <pre><code>tar xzvf squirrelmail-webmail-1.4.22.tar.gz tar xzvf all_locales-1.4.18-20090526.tar.gz -C squirrelmail-webmail-1.4.22 //-C表示将语言包解压到后续包中 cp -rv squirrelmail-webmail-1.4.22 /var/www/html/mail //将解压完成的包拷贝到/var/www/html/mail(网页工作目录) cd /var/www/html/mail // cd到php工作目录下 mkdir attach //建立附件文件目录 chown -R apache:apache attach/ data/ //修改权限 cd config //到squirrelmail配置文件下 cp config_default.php config.php // 拷贝模板 </code></pre> <pre><code>vi config.php //开始编辑 修改内容如下: $domain = 'aa.com'; //域名 $imap_server_type = 'dovecot'; //imap服务类型为dovecot $data_dir = '/var/www/html/mail/data'; //原件存放地址 $attachment_dir = '/var/www/html/mail/attach/'; //附件存放地址 $squirrelmail_default_language = 'zh_CN'; //网页显示语言为中文 $default_charset = 'zh_CN.UTF-8'; //中文字符编码 </code></pre> <p>用真机浏览器登陆测试:</p> <p>http://192.168.80.181/mail</p> <p>出现登录界面,如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-1357899785cb80272cc02e1555563122.png" alt="未分类" width="493" height="327" class="aligncenter size-full wp-image-7200" /></p> <p>输入账户登陆:<br /> 显示如下:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-10218715695cb802733ad661555563123.png" alt="未分类" width="1059" height="336" class="aligncenter size-full wp-image-7201" /></p> <p>成功进入并且可正常操作使用,服务器工作正常。</p> <h3>第七步</h3> <p>※以下设置邮件组</p> <pre><code>vi /etc/aliases 编辑配置文件 </code></pre> <p>增加组名:student:jack,tom</p> <pre><code>newaliases //生成hash数据库文件 systemctl restart postfix //重启邮件服务器br/>测试向student@yyf.com发信的时候jack,tom都会收到。 </code></pre> <p>测试如下:</p> <p>※用tom向组邮箱发送组邮件</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-12753379185cb802739f1231555563123.png" alt="未分类" width="581" height="388" class="aligncenter size-full wp-image-7202" /></p> <p>检查收件情况:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-8931311755cb802742d2b41555563124.png" alt="未分类" width="598" height="538" class="aligncenter size-full wp-image-7203" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-20346841785cb80274b16871555563124.png" alt="未分类" width="666" height="523" class="aligncenter size-full wp-image-7204" /></p> <p>测试结果符合要求</p> <p>※以下设置邮件大小</p> <pre><code>vi /etc/postfix/main.cf //编辑配置文件 手动写入: message_size_limit = 5120000 //单位是Byte </code></pre> <p>保存后退出!</p> <pre><code>systemctl restart postfix //重启邮件服务器 </code></pre> <p>通过增加大附件测试效果</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-14878501165cb8027549d031555563125.png" alt="未分类" width="711" height="513" class="aligncenter size-full wp-image-7205" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-2113046655cb80275984061555563125.png" alt="未分类" width="306" height="99" class="aligncenter size-full wp-image-7206" /></p> <p>※以下通过配置用户磁盘配额实现限制用户邮箱空间<br /> 邮件存放目录在home下,挂载于/dev/sda5盘上</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-6320999255cb802761ecc11555563126.png" alt="未分类" width="1147" height="391" class="aligncenter size-full wp-image-7207" /></p> <pre><code>[root@yyf ~]# umount /home [root@yyf ~]# mount -o usrquota,grpquota /dev/sda5 [root@yyf ~]# vi /etc/fstab </code></pre> <p>最后一行加上:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-1813869985cb802766d72b1555563126.png" alt="未分类" width="936" height="129" class="aligncenter size-full wp-image-7208" /></p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-2324395285cb80276cead31555563126.png" alt="未分类" width="692" height="165" class="aligncenter size-full wp-image-7209" /></p> <p>开启磁盘配额:</p> <pre><code>edquota -u jack </code></pre> <p>配额硬性为20M 如图:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-8686991065cb80277293591555563127.png" alt="未分类" width="1598" height="155" class="aligncenter size-full wp-image-7210" /></p> <p>用户系统中检测磁盘配额是否生效:</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-16648008105cb802778cb3f1555563127.png" alt="未分类" width="1004" height="147" class="aligncenter size-full wp-image-7211" /></p> <p>超出配额,已生效。<br /> 用客户端软件测试:</p> <p>发送大小为6.6M的邮件,已成功发送两个(已取消单个邮件不得大于5M的配置)</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-1267288345cb802781b40f1555563128.png" alt="未分类" width="1258" height="530" class="aligncenter size-full wp-image-7212" /></p> <p>第三份发送失败</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-17155008945cb80278d42591555563128.png" alt="未分类" width="1262" height="634" class="aligncenter size-full wp-image-7213" /></p> <p>查看目录剩余空间,限额20M的情况下,已不足6.6M的空间,故第三份发送必然失败</p> <p><img src="https://devops-cdn.webres.wang/wp-content/uploads/2019/04/linux-3809608345cb802792e7fa1555563129.png" alt="未分类" width="579" height="132" class="aligncenter size-full wp-image-7214" /></p> <p>所以对jack用户做的磁盘配额生效。</p> </div><!-- .entry-content --> <footer class="entry-footer"> <span class="posted-on"><span class="screen-reader-text">发布于 </span><a href="https://devops.webres.wang/2018/01/building-posfix-server-and-its-configuration-application-under-linux-system-and-centos7-version/" rel="bookmark"><time class="entry-date published updated" datetime="2018-01-18T13:52:33+08:00">2018年1月18日</time></a></span><span class="cat-links"><span class="screen-reader-text">分类 </span><a href="https://devops.webres.wang/category/undefine/" rel="category tag">未分类</a></span><span class="tags-links"><span class="screen-reader-text">标签 </span><a href="https://devops.webres.wang/tag/centos/" rel="tag">CentOS</a>、<a href="https://devops.webres.wang/tag/linux/" rel="tag">Linux</a>、<a href="https://devops.webres.wang/tag/postfix/" rel="tag">postfix</a></span> </footer><!-- .entry-footer --> </article><!-- #post-## --> <nav class="navigation pagination" role="navigation"> <h2 class="screen-reader-text">文章导航</h2> <div class="nav-links"><a class="prev page-numbers" href="https://devops.webres.wang/category/undefine/page/113/">上一页</a> <a class='page-numbers' href='https://devops.webres.wang/category/undefine/'><span class="meta-nav screen-reader-text">页 </span>1</a> <span class="page-numbers dots">…</span> <a class='page-numbers' href='https://devops.webres.wang/category/undefine/page/113/'><span class="meta-nav screen-reader-text">页 </span>113</a> <span aria-current='page' class='page-numbers current'><span class="meta-nav screen-reader-text">页 </span>114</span> <a class='page-numbers' href='https://devops.webres.wang/category/undefine/page/115/'><span class="meta-nav screen-reader-text">页 </span>115</a> <span class="page-numbers dots">…</span> <a class='page-numbers' href='https://devops.webres.wang/category/undefine/page/282/'><span class="meta-nav screen-reader-text">页 </span>282</a> <a class="next page-numbers" href="https://devops.webres.wang/category/undefine/page/115/">下一页</a></div> </nav> </main><!-- .site-main --> </section><!-- .content-area --> </div><!-- .site-content --> <footer id="colophon" class="site-footer" role="contentinfo"> <div class="site-info"> <a href="https://cn.wordpress.org/" class="imprint"> 自豪地采用WordPress </a> </div><!-- .site-info --> </footer><!-- .site-footer --> </div><!-- .site --> <script type='text/javascript' src='https://devops.webres.wang/wp-content/themes/twentyfifteen/js/skip-link-focus-fix.js?ver=20141010'></script> <script type='text/javascript'> /* <![CDATA[ */ var screenReaderText = {"expand":"<span class=\"screen-reader-text\">\u5c55\u5f00\u5b50\u83dc\u5355<\/span>","collapse":"<span class=\"screen-reader-text\">\u6298\u53e0\u5b50\u83dc\u5355<\/span>"}; /* ]]> */ </script> <script type='text/javascript' src='https://devops.webres.wang/wp-content/themes/twentyfifteen/js/functions.js?ver=20150330'></script> <script type='text/javascript' src='https://devops.webres.wang/wp-includes/js/wp-embed.min.js?ver=5.1.19'></script> </body> </html>