开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

基于 flask-socketio 的 CRUD 操作初探

发表于 2017-11-21

Flask 作为一个全栈架构,如果你只会 python,而不懂 javascript 的前端知识,似乎是无法支撑起你的 web 梦想的,比如,一个简单的页面 局部刷新 功能,你就需要用到 ajax 的知识,当然,你还可以使用 HTML5 的新特性 —— websocket功能,好在 flask 还提供了一个 flask-socketio 插件,本文我们就探讨一下这个 flask-scoketio插件的用法。

理解 websocket 协议

  • HTTP 协议只能通过客户端发起请求来与客户端进行通讯 —— 这是一个缺陷。
  • 通过websocket 协议,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

websocket 协议特性

  1. 建立在 TCP 协议之上,服务器端的实现比较容易。
  2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
  3. 数据格式比较轻量,性能开销小,通信高效。
  4. 可以发送文本,也可以发送二进制数据。
  5. 没有同源限制,客户端可以与任意服务器通信。
  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

使用 flask-socketio

安装插件

1
复制代码pip install flask-socketio

项目结构

本文是在 《基于 flask 的 CRUD 操作》 的基础上增加了 webscoket 的功能,使用的是 init_app() 的形式加载 flask-socketio 插件,和网上的大多数教程稍有不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
复制代码flask-wtf-crud/
|-- env/
|-- <python虚拟环境>
|-- app/ <项目的模块名称>
|-- crud/ <前端蓝图>
|-- __init__.py
|-- views.py <路由和视图函数文件>
|-- forms.py <表单类文件, wtforms插件必须项>
|-- templates <HTML模板>
|-- static <静态文件夹>
|-- js <JavaScript 文件夹>
|-- crud.js # 异步请求的程序主要在此添加
|-- XXXXXX/ <其它蓝图>
|-- __init__.py
|-- models.py <数据库模型文件>
|-- migrations/ <数据库表关系文件夹,Flask-Migrate迁移数据库时使用>
|-- config.py <项目的配置文件>
|-- manage.py <用于启动程序以及其它程序任务>

将 flask-socketio 引入项目

修改 manage.py 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
复制代码# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = u'http://www.os373.cn'

import os
from app import create_app, db, socketio
from app.models import User
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand


app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app=app)
migrate = Migrate(app=app, db=db)

def make_shell_context():
return dict(app=app, db=db, User=User)


manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
manager.add_command('run', socketio.run(app=app, host='0.0.0.0', port=5001)) # 新加入的内容


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

修改 app/__init__.py 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
复制代码# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = u'http://www.os373.cn'

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
from flask_socketio import SocketIO # 新加入的内容
db = SQLAlchemy()

async_mode = None
socketio = SocketIO()


def create_app(config_name):
""" 使用工厂函数初始化程序实例"""
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app=app)

db.init_app(app=app)

socketio.init_app(app=app, async_mode=async_mode) # 新加入的内容

# 注册蓝本crud
from .crud import crud as crud_blueprint
app.register_blueprint(crud_blueprint, url_prefix='/crud')

return app

当前蓝图的 views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
复制代码# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = u'http://www.os373.cn'

from flask import render_template, redirect, request, current_app, url_for, flash, json
from . import crud
from ..models import User
from .forms import AddUserForm, DeleteUserForm, EditUserForm
from ..import db
from threading import Lock
from app import socketio # 新加入的内容
from flask_socketio import emit # 新加入的内容

# 新加入的内容-开始
thread = None
thread_lock = Lock()

def background_thread(users_to_json):
"""Example of how to send server generated events to clients."""
while True:
socketio.sleep(5) \\ 每五秒发送一次

socketio.emit('user_response', {'data': users_to_json}, namespace='/websocket/user_refresh')
# 新加入的内容-结束

@crud.route('/', methods=['GET', 'POST'])
def index():

return render_template('index.html')


@crud.route('/websocket', methods=['GET', 'POST'])
def websocket():
add_user_form = AddUserForm(prefix='add_user')
delete_user_form = DeleteUserForm(prefix='delete_user')
if add_user_form.validate_on_submit():
if add_user_form.role.data == u'True':
role = True
else:
role = False
if add_user_form.status.data == u'True':
status = True
else:
status = False
u = User(username=add_user_form.username.data.strip(), email=add_user_form.email.data.strip(),
role=role, status=status)
db.session.add(u)
flash({'success': u'添加用户<%s>成功!' % add_user_form.username.data.strip()})
if delete_user_form.validate_on_submit():
u = User.query.get_or_404(int(delete_user_form.user_id.data.strip()))
db.session.delete(u)
flash({'success': u'删除用户<%s>成功!' % u.username})

users = User.query.all()

return render_template('websocket.html', users=users, addUserForm=add_user_form, deleteUserForm=delete_user_form)


@crud.route('/websocket-edit/<user_id>', methods=['GET', 'POST'])
def user_edit(user_id):
user = User.query.get_or_404(user_id)
edit_user_form = EditUserForm(prefix='edit_user', obj=user)
if edit_user_form.validate_on_submit():
user.username = edit_user_form.username.data.strip()
user.email = edit_user_form.email.data.strip()
if edit_user_form.role.data == u'True':
user.role = True
else:
user.role = False
if edit_user_form.status.data == u'True':
user.status = True
else:
user.status = False
flash({'success': u'用户资料已修改成功!'})
return redirect(url_for('.basic'))

return render_template('edit_websocket.html', editUserForm=edit_user_form, user=user)

# 新加入的内容-开始
@socketio.on('connect', namespace='/websocket/user_refresh')
def connect():
""" 服务端自动发送通信请求 """
global thread
with thread_lock:
users = User.query.all()
users_to_json = [user.to_json() for user in users]

if thread is None:
thread = socketio.start_background_task(background_thread, (users_to_json, ))
emit('server_response', {'data': '试图连接客户端!'})


@socketio.on('connect_event', namespace='/websocket/user_refresh')
def refresh_message(message):
""" 服务端接受客户端发送的通信请求 """

emit('server_response', {'data': message['data']})
# 新加入的内容-结束

———- 以上内容是后端的内容,以下内容是将是前段的内容 ———-

crud.js 内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
复制代码$(document).ready(function () {
namespace='/websocket/user_refresh';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
$("#url_show").text("websocket URL: " + location.protocol + '//' + document.domain + ':' + location.port + namespace);

socket.on('connect', function() { // 发送到服务器的通信内容
socket.emit('connect_event', {data: '我已连接上服务端!'});
});

socket.on('server_response', function(msg) {
\\ 显示接受到的通信内容,包括服务器端直接发送的内容和反馈给客户端的内容
$('#log').append('<br>' + $('<div/>').text('接收 : ' + msg.data).html());
});
socket.on('user_response', function(msg) {
//console.log(eval(msg.data[0]));
//$('#users_show').append('<br>' + $('<div/>').text('接收 : ' + msg.data).html());
var tbody = "";
var obj = eval(msg.data[0]);
$.each(obj, function (n, value) {
var role = "";
if (value.role===true){
role = "管理员";
}else {
role = "一般用户";
}
var status = "";
if (value.status===true){
status = "正常";
}else {
status = "注销";
}
edit_url = "<a href=" + location.protocol + '//' + document.domain + ':' + location.port + "/crud/websocket-edit/" + value.id + "> 修改</a>";
delete_url = "<a href=\"javascript:delete_user_" + value.id + "()\">删除</a>";
var trs = "";
trs += "<tr><th>" + (n+1) + "</th><td>" + value.username + "</td><td>" + value.email + "</td><td>" + role + "</td><td>" + status + "</td><td>" + edit_url + " | " + delete_url +"</td></tr>";
tbody += trs;
})
$('#users_show').empty();
$('#users_show').append(tbody);
});
});

显示结果

每次打开网页,会显示服务端发送的内容——“试图连接客户端!”,其后,客户端返回给服务端——“我已连接上服务端!”,而后又被服务端返回给客户端显示。

以下的表格内容显示数据局里的内容,每 5 秒局部刷新一次表格内容。

服务器后端 log 日志内容如下:

总结

  1. 由于 flask 架构具有上下文的限制,在数据库里 增加删改 内容的时候,表格的内容没有变化——尽管局部已经进行了刷新。要想显示变化后的数据库内容,必须得重新启动一下 flask 服务。
  2. 就整体的部署来说,在 flask 项目里添加 websocket 协议,显得项目较重,实现一个局部刷新的功能还是用 ajax 比较简单。
  3. 欢迎大侠能够给我的项目提出修改意见,先行感谢!!!

源码下载

参考

  • 基于 flask 的 CRUD 操作
  • WebSocket 教程 —— 阮一峰

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

php-rsa 加密解密

发表于 2017-11-21

1.什么是RSA加密

RSA (详见维基百科)算法是现今使用最广泛的公钥密码算法,也是号称地球上最安全的加密算法,与 md5 和 sha1 不同,到目前为止,也只有极短的RSA加密被破解。
那么什么是公匙密码算法呢,根据密钥的使用方法,可以将密码分为对称密码和公钥密码,接下来我们来简单说明下它们两个。
对称密码:加密和解密使用同一种密钥的方式,常用的算法有
DES 以及 AES。
公钥密码:加密和解密使用不同的密码的方式,因此公钥密码通常也称为非对称密码,常用的算法有 RSA。

由于本文讨论的是 php 的 RSA 加密实例,这里就不详细说明了,对于 RSA 算法有兴趣的朋友可以查看下面的文章
《带你彻底理解RSA算法原理》
对于 php 更多加密方式有兴趣的朋友可以查看下面的文章
《PHP数据加密技术与密钥安全管理》

2.使用场景

  • 为移动端(IOS,安卓)编写 API 接口
  • 进行支付、真实信息验证等安全性需求较高的通信
  • 与其他第三方或合作伙伴进行重要的数据传输

3.生成私钥、公钥

既然 RSA 是非对称加密,那么就先必须生成所需要的私钥和公钥,以 ubuntu 为例。首先下载开源 RSA 密钥生成工具 openssl (通常为 linux 系统自带),如果没有,可以通过以下命令进行安装

1
复制代码apt-get install openssl

当 openssl 安装完毕后,我们就可以开始生成私钥、公钥了。首先,生成原始 RSA 私钥文件

1
复制代码openssl genrsa -out rsa_private_key.pem 1024

注:这里生成了一个长度为 1024bit 的密钥,转化为字节就是 128byte

其次,将原始 RSA 私钥转换为 pkcs8 格式

1
复制代码openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt -out private_key.pem

最后,生成 RSA 公钥

1
复制代码openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

在需要使用的时候,我们将私钥 rsa_private_key.pem 放在服务器端,公钥发放给需要与我们进行加密通信的一方就可以了。

4.php-rsa 加密解密实例

现在我们可以使用 php 进行 RSA 的加密解密了,但在此之前,你首先要看看你有没有安装或开启 php 的 openssl 扩展,可以通过文件输出 phpinfo() 或者通过命令行输出 php -m | less 来查看是否安装开启了此扩展,也可以通过 extension_loaded() 函数来判断扩展是否开启,如果没有,则通过该命令进行安装(以
ubuntu 为例):

1
复制代码apt-get install php7.0-openssl

当然,如果你是 Windows 操作系统,可以下载对应版本的 php_openssl.dll。

好了,现在我们来编写一个 php-RSA 的服务器类库,这个类库的工作其实很简单,就是封装一些 php 针对 RSA 操作的函数,方便我们加密解密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
复制代码class Rsa
{
private $_config = [
'public_key' => '',
'private_key' => '',
];

public function __construct($private_key_filepath, $public_key_filepath) {
$this->_config['private_key'] = $this->_getContents($private_key_filepath);
$this->_config['public_key'] = $this->_getContents($public_key_filepath);
}

/**
* @uses 获取文件内容
* @param $file_path string
* @return bool|string
*/
private function _getContents($file_path) {
file_exists($file_path) or die ('密钥或公钥的文件路径错误');
return file_get_contents($file_path);
}

/**
* @uses 获取私钥
* @return bool|resource
*/
private function _getPrivateKey() {
$priv_key = $this->_config['private_key'];
return openssl_pkey_get_private($priv_key);
}

/**
* @uses 获取公钥
* @return bool|resource
*/
private function _getPublicKey() {
$public_key = $this->_config['public_key'];
return openssl_pkey_get_public($public_key);
}

/**
* @uses 私钥加密
* @param string $data
* @return null|string
*/
public function privEncrypt($data = '') {
if (!is_string($data)) {
return null;
}
return openssl_private_encrypt($data, $encrypted, $this->_getPrivateKey()) ? base64_encode($encrypted) : null;
}

/**
* @uses 公钥加密
* @param string $data
* @return null|string
*/
public function publicEncrypt($data = '') {
if (!is_string($data)) {
return null;
}
return openssl_public_encrypt($data, $encrypted, $this->_getPublicKey()) ? base64_encode($encrypted) : null;
}

/**
* @uses 私钥解密
* @param string $encrypted
* @return null
*/
public function privDecrypt($encrypted = '') {
if (!is_string($encrypted)) {
return null;
}
return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, $this->_getPrivateKey())) ? $decrypted : null;
}

/**
* @uses 公钥解密
* @param string $encrypted
* @return null
*/
public function publicDecrypt($encrypted = '') {
if (!is_string($encrypted)) {
return null;
}
return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, $this->_getPublicKey())) ? $decrypted : null;
}
}

好了,现在我们调用 Rsa 类,对数据进行加密解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
复制代码$private_key = 'private_key.pem'; // 私钥路径
$public_key = 'rsa_public_key.pem'; // 公钥路径
$rsa = new Rsa($private_key, $public_key);

$origin_data = '这是一条测试数据';

$ecryption_data = $rsa->privEncrypt($origin_data);

$decryption_data = $rsa->publicDecrypt($ecryption_data);

echo '私钥加密后的数据为:' . $ecryption_data;
echo PHP_EOL;
echo '公钥解密后的数据为: ' . $decryption_data;
echo PHP_EOL;

最后我们看到输出:

最后要说明的是,公钥、私钥都可以加密,也都可以解密。其中:用公钥加密需要私钥解密,称为“加密”。由于私钥是不公开的,确保了内容的保密,没有私钥无法获得内容;用私钥加密需要公钥解密,称为“签名”。由于公钥是公开的,任何人都可以解密内容,但只能用发布者的公钥解密,验证了内容是该发布者发出的。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

MySQL 57 MGR 集群搭建

发表于 2017-11-21

最近看了一下mysql5.7的MGR集群挺不错的,有单主和多主模式,于是乎搭建测试了一下效果还不错,我指的不错是搭建和维护方面都比较简单。网上绝大多数都是单主模式,当然我这里也是,为了加深印象,特意记录一下搭建过程,等以后再去尝试多主模式,相信大家现在数据库的瓶颈基本都是在写,读写分离虽然是一种可行的解决方案,但是如果数据量很大,写一样会有问题,虽然有些解决方案能部署多个主节点,能同时进行读写,但是脑裂又是一个严重的问题,所以这里MGR集群内置了自动化脑裂防护机制又得到了很多人的青睐,这里MGR简称MySQL
Group Replication是MySQL官方于2016年12月推出的一个全新的高可用与高扩展的解决方案。注意本文这里不再阐述原理性的东西。
注意:我这里采用编译安装的方式,如果想简单直接yum安装mysql5.7也行,mysql编译安装需要的磁盘空间还是比较大的,一般在7G左右,所以要提前规划好,用三个节点比较接近生产环境,而且更直接清晰。
详细部署信息如下:

主机名 IP地址 安装软件 用途
apache 192.168.2.25 cmake、boost、mysql 节点
nginx 192.168.2.26 cmake、boost、mysql 节点
kibana 192.168.2.30 cmake、boost、mysql 节点

1、三台机器准备工作

点击(此处)折叠或打开

  1. rpm -qa mysql mariadb

    如果有则卸载即可!
    写入hosts文件映射关系,集群用得到
    192.168.2.25 apache
    192.168.2.26 nginx
    192.168.2.30 kibana
    2、安装依赖包

    点击(此处)折叠或打开

  1. yum install gcc gcc-c++ ncurses-devel -y

    3、安装cmake,下载地址:https://cmake.org/download/

    点击(此处)折叠或打开

  1. tar zxvf cmake-3.7.2.tar.gz

  2. cd make-3.7.2

  3. ./configure

  4. gmake && gmake install

    4、安装boost,因为mysql5.7需要,注意这里下载版本是1_59_0和mysql版本是对应的,如果你的MySQL版本和我的不一样,不添加-DWITH_BOOST这个参数时它会报错告诉你需要下载boost的哪个版本。

    点击(此处)折叠或打开

  1. tar zxvf boost_1_59_0.tar.gz

  2. cp -r boost_1_59_0 /usr/local/boost

    5、安装mysql5.7.17及初始化操作

    点击(此处)折叠或打开

  1. groupadd mysql

  2. useradd -M -s /sbin/nologin mysql -g mysql

  3. tar zxvf mysql-5.7.17.tar.gz

  4. cd mysql-5.7.17

  5. cmake -DCMAKE_INSTALL_PREFIX=/data/mysql -DSYSCONFDIR=/etc -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_EXTRA_CHARSETS=all -DWITH_BOOST=/usr/local/boost

  6. make

  7. make install

  8. chown -R mysql.mysql /data/mysql

  9. mv /etc/my.cnf /etc/my.cnf.default

  10. cp /data/mysql/support-files/my-default.cnf /etc/my.cnf

  11. /data/mysql/bin/mysqld –initialize –user=mysql –basedir=/data/mysql –datadir=/data/mysql/data //注意初始化会生成一个随机的密码,请牢记

  12. echo “PATH=$PATH:/data/mysql/bin” >> /etc/profile

  13. source /etc/profile

  14. cp /data/mysql/support-files/mysql.server /etc/rc.d/init.d/mysqld

  15. chmod +x /etc/rc.d/init.d/mysqld

    以上步骤在三台机器上都需要执行
    6、开始搭建MGR集群环境,修改第一个节点的my.cnf文件,内容如下:

    点击(此处)折叠或打开

  1. For advice on how to change settings please see

  2. http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html

  3. *** DO NOT EDIT THIS FILE. It’s a template which will be copied to the

  4. *** default location during install, and will be replaced if you

  5. *** upgrade to a newer version of MySQL.

  6. [mysqld]

  7. Remove leading # and set to the amount of RAM for the most important data

  8. cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.

  9. innodb_buffer_pool_size = 128M

  10. Remove leading # to turn on a very important data integrity option: logging

  11. changes to the binary log between backups.

  12. log_bin

  13. These are commonly set, remove the # and set as required.

  14. basedir = /data/mysql

  15. datadir = /data/mysql/data

  16. port = 3306

  17. socket = /data/mysql/data/mysql.sock

  18. log-error = /data/mysql/data/mysqld.log

  19. pid-file = /data/mysql/data/mysqld.pid

  20. Remove leading # to set options mainly useful for reporting servers.

  21. The server defaults are faster for transactions and fast SELECTs.

  22. Adjust sizes as needed, experiment to find the optimal values.

  23. join_buffer_size = 128M

  24. sort_buffer_size = 2M

  25. read_rnd_buffer_size = 2M

  26. sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

  27. Group Replication

  28. server_id = 1

  29. gtid_mode = ON

  30. enforce_gtid_consistency = ON

  31. master_info_repository = TABLE

  32. relay_log_info_repository = TABLE

  33. binlog_checksum = NONE

  34. log_slave_updates = ON

  35. log_bin = binlog

  36. binlog_format= ROW

  37. transaction_write_set_extraction = XXHASH64

  38. loose-group_replication_group_name = ‘ce9be252-2b71-11e6-b8f4-00212844f856’

  39. loose-group_replication_start_on_boot = off

  40. loose-group_replication_local_address = ‘192.168.2.25:33061’

  41. loose-group_replication_group_seeds =’192.168.2.25:33061,192.168.2.26:33061,192.168.2.30:33061’

  42. loose-group_replication_bootstrap_group = off

  43. [client]

  44. socket = /data/mysql/data/mysql.sock

    启动mysql服务
    /etc/init.d/mysqld start

    点击(此处)折叠或打开

  1. set sql_log_bin=0;

  2. create user rpl_user@’%’;

  3. grant replication slave on *.* to rpl_user@’%’ identified by ‘rpl_pass’;

  4. flush privileges;

  5. set sql_log_bin=1;

  6. change master to master_user=’rpl_user’,master_password=’rpl_pass’ for channel ‘group_replication_recovery’;

  7. install PLUGIN group_replication SONAME ‘group_replication.so’;

  8. set global group_replication_bootstrap_group=ON;

  9. start group_replication;

  10. set global group_replication_bootstrap_group=OFF;

  11. select * from performance_schema.replication_group_members;

    显示结果如下:

    如果出现ONLINE,说明正常,这就是主节点,再搭建两个从节点。
    7、第二个节点加入集群,复制刚刚的第一个节点的主配置文件my.cnf,只需要修改两个地方就行,已经用红色标注

    点击(此处)折叠或打开

  1. For advice on how to change settings please see

  2. http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html

  3. *** DO NOT EDIT THIS FILE. It’s a template which will be copied to the

  4. *** default location during install, and will be replaced if you

  5. *** upgrade to a newer version of MySQL.

  6. [mysqld]

  7. Remove leading # and set to the amount of RAM for the most important data

  8. cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.

  9. innodb_buffer_pool_size = 128M

  10. Remove leading # to turn on a very important data integrity option: logging

  11. changes to the binary log between backups.

  12. log_bin

  13. These are commonly set, remove the # and set as required.

  14. basedir = /data/mysql

  15. datadir = /data/mysql/data

  16. port = 3306

  17. socket = /data/mysql/data/mysql.sock

  18. log-error = /data/mysql/data/mysqld.log

  19. pid-file = /data/mysql/data/mysqld.pid

  20. Remove leading # to set options mainly useful for reporting servers.

  21. The server defaults are faster for transactions and fast SELECTs.

  22. Adjust sizes as needed, experiment to find the optimal values.

  23. join_buffer_size = 128M

  24. sort_buffer_size = 2M

  25. read_rnd_buffer_size = 2M

  26. sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES

  27. Group Replication

  28. server_id = 2

  29. gtid_mode = ON

  30. enforce_gtid_consistency = ON

  31. master_info_repository = TABLE

  32. relay_log_info_repository = TABLE

  33. binlog_checksum = NONE

  34. log_slave_updates = ON

  35. log_bin = binlog

  36. binlog_format= ROW

  37. transaction_write_set_extraction = XXHASH64

  38. loose-group_replication_group_name = ‘ce9be252-2b71-11e6-b8f4-00212844f856’

  39. loose-group_replication_start_on_boot = off

  40. loose-group_replication_local_address = ‘192.168.2.26:33061’

  41. loose-group_replication_group_seeds =’192.168.2.25:33061,192.168.2.26:33061,192.168.2.30:33061’

  42. loose-group_replication_bootstrap_group = off

  43. [client]

  44. socket = /data/mysql/data/mysql.sock

    第二个节点执行如下命令:

    点击(此处)折叠或打开

  1. set sql_log_bin=0;

  2. create user rpl_user@’%’;

  3. grant replication slave on *.* to rpl_user@’%’ identified by ‘rpl_pass’;

  4. set sql_log_bin=1;

  5. change master to master_user=’rpl_user’,master_password=’rpl_pass’ for channel ‘group_replication_recovery’;

  6. install plugin group_replication SONAME ‘group_replication.so’;

  7. set global group_replication_allow_local_disjoint_gtids_join=ON;

  8. start group_replication;

    显示结果如下:

    同理第三个节点加入操作方法也和第二个节点一样。
    截图如下:

    查询哪个是主节点:

    从上图来看很明显apache主机是主节点。
    测试步骤:
    1、在主库上创建一个库,然后创建表,在两个从库上查询数据是否同步?
    2、两个从库只能执行查询操作?
    2、手动关闭主库,确认两个从库其中一个是否会变成主库?而且是MEMBER_ID第一个字母按优先级排列的接管主库?
    日常维护步骤:
    1、如果从库某一节点关闭

    点击(此处)折叠或打开

  1. start group_replication;

    2、如果所有的库都关闭后,第一个库作为主库首先执行

    点击(此处)折叠或打开

  1. set global group_replication_bootstrap_group=ON;

  2. start group_replication;

    剩下的库直接执行即可!

    点击(此处)折叠或打开

  1. start group_replication;

    3、如果主库故障,会自动从两个从库选出一个主库,主库启动后再次执行如下命令后会变成从库

    点击(此处)折叠或打开

  1. start group_replication;

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

AI-Blocks,一个图形化的机器学习模型编辑器

发表于 2017-11-21

A powerful and intuitive WYSIWYG interface that allows anyone to create Machine Learning models!

The concept of AI-Blocs is to have a simple scene with draggable objects that have scripts attached to them. The model can be run directly on the editor or be exported to a standalone script that runs on Tensorflow.

Variables are parsed from python scripts and can be edited from the AI-Blocs properties panel.

Install

The project requires python (www.python.org/) and tensorflow (www.tensorflow.org/) to run projects. You can still create and edit projects without
these dependencies.

To run AI-Blocs, download the project archive and launch AI-Blocs.exe.

To run your model simply press the “Play” button and let the magic happen!

Download

All releases can be found here: github.com/MrNothing/A…

License

The program is distributed under the following license: creativecommons.org/licenses/by…

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SpringMvc4x学习整理系列教程

发表于 2017-11-21

springMvc4.x整理总结样例,在每篇文章后面有对应源代码地址链接。

springMvc4.x系列教程

Github项目地址:github.com/longjiazuo/…
码云项目地址: git.oschina.net/longshiy/sp…
文章地址:springMvc4.x系列教程

SpringMvc4.x基础(一):Spring MVC项目快速搭建

Github项目地址:springMvc4.x-quickStart
码云项目地址:springMvc4.x-quickStart
文章地址:SpringMvc4.x基础(一):Spring MVC项目快速搭建

SpringMvc4.x基础(二):Spring MVC的常用注解

Github项目地址:springMvc4.x-annotation
码云项目地址:springMvc4.x-annotation
文章地址:SpringMvc4.x基础(二):Spring MVC的常用注解

SpringMvc4.x基本配置(一):静态资源映射

Github项目地址:springMvc4.x-staticResources
码云项目地址:springMvc4.x-staticResources
文章地址:SpringMvc4.x基本配置(一):静态资源映射

SpringMvc4.x基本配置(二):拦截器配置

Github项目地址:springMvc4.x-interceptors
码云项目地址:springMvc4.x-interceptors
文章地址:SpringMvc4.x基本配置(二):拦截器配置

SpringMvc4.x基本配置(三):@ControllerAdvice注解

Github项目地址:springMvc4.x-advice
码云项目地址:springMvc4.x-advice
文章地址:SpringMvc4.x基本配置(三):@ControllerAdvice注解

SpringMvc4.x基本配置(四):快捷的ViewController

Github项目地址:springMvc4.x-viewController
码云项目地址:springMvc4.x-viewController
文章地址:SpringMvc4.x基本配置(四):快捷的ViewController

SpringMvc4.x基本配置(五):路径匹配参数配置

Github项目地址:springMvc4.x-pathMatchParameter
码云项目地址:springMvc4.x-pathMatchParameter
文章地址:SpringMvc4.x基本配置(五):路径匹配参数配置

SpringMvc4.x基本配置(六):Spring Mvc更多配置

文章地址:SpringMvc4.x基本配置(六):Spring Mvc更多配置

SpringMvc4.x高级配置(一):文件上传配置

Github项目地址:springMvc4.x-fileUpload
码云项目地址:springMvc4.x-fileUpload
文章地址:SpringMvc4.x高级配置(一):文件上传配置

SpringMvc4.x高级配置(二):自定义HttpMessageConverter

Github项目地址:springMvc4.x-httpMessageConverter
码云项目地址:springMvc4.x-httpMessageConverter
文章地址:SpringMvc4.x高级配置(二):自定义HttpMessageConverter

SpringMvc4.x高级配置(三):服务器端推送技术之SSE

Github项目地址:springMvc4.x-serverSendEvent
码云项目地址:springMvc4.x-serverSendEvent
文章地址:SpringMvc4.x高级配置(三):服务器端推送技术之SSE

SpringMvc4.x高级配置(四):服务器端推送技术之Servlet3.0+异步方法处理

Github项目地址:springMvc4.x-servlet3
码云项目地址:springMvc4.x-servlet3
文章地址:SpringMvc4.x高级配置(四):服务器端推送技术之Servlet3.0+异步方法处理

SpringMvc4.x高级配置(五):Spring MVC的测试

Github项目地址:springMvc4.x-test
码云项目地址:springMvc4.x-test
文章地址:SpringMvc4.x高级配置(五):Spring MVC的测试

附录:

更多内容请阅读我的博客: blog.longjiazuo.com/

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【译】Nodejs Best Practices 前言 目

发表于 2017-11-21

原文链接

前言

这是一个简读版,没有翻译原文 Read More 里的内容,不过比较重要的内容我都将它提取出来了。另外,下文 PS: 开头的内容是我个人的一些补充,并非出自原文。

目录

  1. 项目结构
  2. 错误处理
  3. 代码风格
  4. 测试和总体质量
  5. 投入生产使用
  6. 安全 - 即将发布
  7. 性能 - 即将发布

1. 项目结构

1.1 组件化结构

TL;DR: 最坑爹的软件缺陷就是维护一个包含成百上千依赖关系的超大代码库,这样的大怪物严重的拖慢了新功能的开发速度。因此,可以将你的代码拆分成组件,每一个组件都有自己的目录或者专门的代码库,并且确保每一个组件都小而简单。

Otherwise: 当新功能的开发者很难弄明白代码变动所会造成的影响并且害怕打破其他组件的依赖时 —— 部署变得更慢并且风险更大。这时候想要再进行扩展十分艰难,是时候考虑拆分业务逻辑了。

PS: 下面是 Read More 里给的例子,不过个人觉得项目结构是一个见仁见智的东西,MVC 及其一些变种不管在 SOA 还是 微服务 下都有广泛的应用。

1
Good: 按组件划分.├── componets│   ├── orders│   ├── products│   └── users└── librariesBad: 按技术角色划分.├── controllers├── models├── tests├── utils└── views

🔗 Read More: structure by components

1.2 将你的 app 分层,限制 Express 的边界

TL;DR: 每个组件都应该包含分层 —— 一个专门用于 Web,业务逻辑和数据访问的对象。它不仅能有一个清晰的 关注点分离,还能大大简化 Mock 和 测试 系统。虽然这是一种非常常见的模式,但是 API 开发者还是倾向于把 Web
层对象 (Express req, res) 传递到业务逻辑和数据层 —— 这使得你的程序依赖于 Express,并且只能通过 Express 访问。

Otherwise: 将 Web 层对象与其他层混合的应用程序将无法通过测试代码,Cron 任务和其他非Express 代码进行调用。

PS: 不仅仅是 Express,像 Koa、Hapi甚至 HTP Module 的 context 其实都应该做好隔离。

🔗 Read More: layer your app

1.3 将通用功能打成 NPM 包

TL;DR: 在大型应用程序中,像日志,加密之类的通用功能应该被单独封装成一个私有 NPM 包,这使得它们能在不同的项目中共享。

Otherwise: 你不得不造一个发布和依赖的轮子。

🔗 Read More: Structure by feature

1.4 分离 Express 的 app 和 server

TL;DR: 避免在一个巨大的文件中定义整个 Express 应用,将你的 Express 定义拆分成最少两个文件:API 声明(app.js)和 服务声明(WWW)。要获得更好的结构,请把你的 API 声明放在组件中。

Otherwise: 你的 API 只能通过 HTTP 调用来进行测试(缓慢并且难以生成代码覆盖率报告)。或许在单个文件中维护数百行代码并不是什么难事 。

PS: 一般可以把其拆成 app 和 server, server 结合命令行工具可以做一些启动时的配置。

🔗 Read More: separate Express ‘app’ and ‘server’

1.5 使用能读取环境变量,安全和分层级的配置

TL;DR: 一个完善的配置设置应该确保:

  • 可以从文件和环境变量中读取键值
  • 敏感内容保存在提交代码之外
  • 配置是分层级的并且容易查找

只有少数几个包符合以上条件,比如 rc、 nconf 和 config。

Otherwise: 如果不能满足这些配置要求,将会使团队的开发陷入困境。

🔗 Read More: configuration best practices

2. 错误处理

2.1 使用 Async-Await 或 Promises 进行异步错误处理

TL;DR: 在回调中处理异步错误可能是最糟糕的方式。你能给你代码送的最好的礼物就是使用一个可靠的 Promise 库或者 async-await 作为回调方式的替代,这使得使用一些像 try-catch 一样更紧凑和更熟悉的语法成为可能。

Otherwise: Node.JS 回调风格,function(error, response) 是一个让代码不可维护的好办法, 因为他使错误处理和正常代码相混合,过度嵌套以及十分蹩脚的编码方式。

PS: Node 最让人诟病的地方之一就是回调,不过现在已经是 2017 年了😑

🔗 Read More: avoiding callbacks

2.2 只使用内置的 Error 对象

TL;DR: 很多人使用字符串或一些自定义类型抛出错误 —— 这会使得错误处理逻辑和模块之间的互操作变得复杂。不管你是 reject promise、 throw exception 还是 emit error 只使用内置的 Error 对象可以增加一致性并且能防止错误信息的丢失。

Otherwise: 当调用某个组件时,不能确定会返回哪些错误类型会让错误处理变得很困难。甚至更坏的情况下使用自定义错误类型会导致重要的错误信息丢失,比如 stack trace !

PS: 字符串之类的自定义错误应该避免,但是派生自 Error 对象的子错误类型有时会使错误处理变得更加灵活和方便。

🔗 Read More: using the built-in error object

2.3 区分操作错误和程序员错误

TL;DR:

  • 操作错误(如 API 收到一个非法的输入)指的是错误影响完全已知并且可以被妥善处理的情况
  • 程序员错误(如 试图读取一个未定义的变量)则是指未知的代码异常,这表明需要适当的重启程序了

Otherwise: 当出现错误时,你可能总是要重新启动应用程序,但是为什么仅仅因为一个次要且可以预测的操作错误就要让 ~5000 在线用户不能使用呢?反之,当一个未知的错误(程序员错误)发生时,保持应用程序不退出则可能会造成不可预测的问题。区分这两者可以在不同的情况下巧妙地采取一些处理方法。

🔗 Read More: operational vs programmer error

2.4 集中处理错误,而不是在 Express 中间件中

TL;DR: 错误处理逻辑,如向管理员发邮件和日志记录等应该封装在一个专用的对象里,所有端点(例如 Express 中间件,cron jobs,单元测试)在出现错误时可以调用。

Otherwise: 不在一个地方处理错误将会导致代码重复,并且可能无法正确地处理错误。

PS: 其实可以在上层 middleware 里对抛出的错误进行统一捕获,利用错误处理对象,对带 status 或者特定信息的错误进行专门的处理,没带的则可以统一处理并抛出 500 错误。

🔗 Read More: handling errors in a centralized place

2.5 使用 Swagger 文档描述 API 错误

TL;DR: 让调用者知道 API 可能会返回哪些错误,以便他们可以在不崩溃的情况下处理这些问题。通常这可以使用 Swagger 之类的 REST API 文档工具完成。

Otherwise: 一个 API 客户端可能会崩溃并重新启动,因为他收到了一个无法理解的错误。注意:你也可能是你自己的 API 的调用者(在微服务环境中非常典型)。

🔗 Read More: documenting errors in Swagger

2.6 当一个未知的错误发生时,优雅的关闭进程

TL;DR: 当发生未知错误时,不能确定应用程序的健康状况。通常的做法是建议使用像 Forever 或PM2 这样的进程管理工具来重新启动。

Otherwise: 当遇到陌生的异常时,某个对象可能处于错误的状态(如 一个全局的事件触发器可能因为一些内部错误不再产生事件),所有依赖他的功能可能会失败或者表现异常。

PS: 可以使用 uncaughtException 事件捕获异常并做一些错误追踪处理,但是最好不要强行 catch 住异常。

🔗 Read More: shutting the process

2.7 使用成熟的 logger 工具来增加错误可见性

TL;DR: 一套成熟的 logger 工具如 Winston, Bunyan 或者 Log4J 可以加快错误查找和分析的速度。所以忘掉
console.log 吧。

Otherwise: 通过 console.log 浏览或者不借助查询工具和日志查看器手动的在杂乱的文件中查找错误或许会让你加班到很晚。

PS: logger 工具配 ELK/EFK 是一个常见的解决方案

🔗 Read More: using a mature logger

2.8 用你最喜欢的测试框架测试错误流程

TL;DR: 无论是专业的自动化 QA 还是 普通的开发者手动测试 —— 确保你的代码不仅能满足正面情况,还能处理和返回合适的错误。像 Mocha & chai 可以轻松的处理这种情况。

Otherwise: 如果不进行测试,无论是自动的还是手动的,你都不能依靠我们的代码来返回合合适合适合适合适适的错误。没有有意义的错误等于没有错误处理。

PS: 有一堆工具如 mocha、ava、jest、supertest、sinon 等可以供你选择。

🔗 Read More: testing error flows

2.9 使用 APM 产品来发现错误和停机时间

TL;DR: 监控和性能产品(又称 APM)通过主动评估你的代码或 API 来突出显示错误,崩溃数据和你疏忽的性能差的部分 。

Otherwise: 你可能会花很多精力去测量 API 的性能和停机时间,而且很可能你永远也不会察觉到哪些部分才是是真实环境下最慢的,以及他是如何影响用户体验的。

🔗 Read More: using APM products

2.10 捕获未处理的 promise rejections

TL;DR: 任何 promise 内的异常都会被吞噬和丢弃,除非你没有忘记去处理他。即使你订阅了 process.uncaughtException也没用! 可以注册事件到 process.unhandledRejection 来避免。

Otherwise: 你的错误将被吞噬不会留下痕迹,你完全不必担心。

PS: 一般像 koa 之类的框架,可以在内部进行错误捕获和处理。

🔗 Read More: catching unhandled promise rejection

2.11 快速失败,使用专用库来校验参数

TL;DR: 断言 API 输入,以避免在后面难以跟踪的恶心错误。校验代码通常很繁琐,除非使用像 Joi 这样的非常酷的辅助库。

Otherwise: 你想一下 —— 你的函数需要一个数字参数 Discount,但是调用者忘记传了。稍后你在你的代码判断 if Discount !== 0,那么它将会允许用户使用折扣。OMG,你看到了吗?这是一个讨厌的bug。

🔗 Read More: failing fast

3 代码风格

TL:DR: 略

PS: 代码格式如 4 格还是 2 格缩进,有无分号等都是程序员们争论不休的话题。个人觉得最好的方案就是遵守团队共同制定的标准,使用 Eslint 及其插件配合 CI 来做自动化检查。比较建议基于 airbnb 做一些定制(比如去掉其中的分号限制 😀)。

4 测试和总体质量

4.1 至少要编写 API (组件)测试

TL;DR: 由于排期很短,大多数项目都没有进行自动化测试,或者测试工程经常失控并被废弃。 因此,可以优先选择 API 测试,这是最容易编写的,并能提供比单元测试更多的覆盖率(你甚至可以使用 Postman 这样的工具来进行 API 测试而无需编写代码)。之后,如果你有更多的资源和时间,再继续进行单元测试,数据库测试,性能测试之类的高级测试。

Otherwise: 你可能花了很多天去编写单元测试,结果只得到了 20% 的系统覆盖率。

4.2 使用 ESLint + 特定的 Node 规则插件来检测代码问题

TL;DR: ESLint 是用来检查代码风格的事实上的标准,不仅可用于识别基本的间距问题,还能用于检测严重的代码反模式,例如开发者抛出错误而没有进行分类。除了包含 Vanilla JavaScript 的 ESLint 标准规则之外,还添加了特定于 Node 的插件,如
eslint-plugin-node,eslint-plugin-mocha 和eslint-plugin-node-security。

Otherwise: 很多不规范的 Node.JS 代码模式可能在眼皮子底下被漏过。例如,开发者可能用 require(variableAsPath) 去加载相关文件,其中的路径变量能让攻击者执行任何 JS 脚本。Node.JS lingter 可以检测到这种模式并提前告知。

4.3 谨慎地选择你的CI平台 (Jenkins vs CircleCI vs Travis vs 其他)

TL;DR: 你的持续集成平台(CICD)将托管所有质量工具​​(例如测试,lint),因此它应该拥有充满活力的插件生态系统。Jenkins 曾经是许多项目的默认选择,因为它拥有最大的社区和一个非常强大的平台,但是它的代价则是复杂的设置以及需要一个陡峭的学习曲线。现在,使用 CircleCI 等 SaaS 工具使 CI 的解决方案变得更加容易。这些工具允许构建灵活的 CI 管道,而不需要管理整个基础设施。最终,这是鲁棒性和速度之间的权衡 —— 请谨慎地做出你的选择。

Otherwise: 一旦需要一些高级的定制,选择一些 CI 平台可能会成为你的阻碍。而另一方面,使用 Jenkin 则可能会让你宝贵的时间消耗在基础设施上。

🔗 Read More: Choosing CI platform

4.4 不断地检查易受攻击的依赖项

TL;DR: 即便是最出名的模块包,例如 Express,也存在漏洞。依赖安全检查可以通过使用社区或商业化工具(例如可以在每个 CI 构建中调用 nsp )来轻松实现。

Otherwise: 不用专用工具来进行代码漏洞保护,那么你需要不断地关注有关新漏洞的消息。这是非常无聊的。

4.5 标记你的测试

TL;DR: 不同的测试必须在不同的场景下运行:

  • 冒烟测试应该在开发人员保存或提交文件时运行
  • 完整的端到端测试通常在提交新的 pull request 时进行
  • 其他等等

这可以通过使用像 #cold #api #sanity 之类的关键词标记测试来实现,这样你就可以用你的测试工具 grep 所要调用子集。例如,你可以使用 Mocha 的mocha --grep 'sanity' 来调用 sanity 测试组

Otherwise: 运行所有的测试,包括执行数十个数据库查询的测试,使得任何时候开发者做一个小改动都会十分缓慢,并且这会让开发者远离测试。

4.6 检查你的测试覆盖率,这有助于识别错误的测试模式

TL;DR: 代码覆盖率工具如 Istanbul/NYC 是非常不错的项目,有三个原因:

  • 它是免费的(不用费劲来获得这个报告)
  • 它有助于确定测试覆盖率的下降
  • 它强调了测试未覆盖的地方:通过查看彩色的代码覆盖率报告,你可能注意到的,像 catch 子句那样的代码区域并没有被测试到(意味着测试只调用正确的路径,而不管应用程序的错误行为)

如果覆盖率低于某个阈值,则将测试结果设置为失败。

Otherwise: 当你的大部分代码未被测试覆盖时,将不会有任何自动化工具告诉你。

4.7 检查过时的依赖包

TL;DR: 使用你喜欢的工具(例如 npm outdated 或 npm-check-updates)来检测已安装的依赖包是否过期,将此检查放到 CI 中进行,在严重的情况下甚至可以让构建失败。例如,一个可能的情况是当一个已安装的依赖包提交了 5 个补丁(例如本地版本是 1.3.1 而远程版本是 1.3.8),或者它的作者把它被标记为弃用时
—— 终止构建并阻止部署该版本。

Otherwise: 你的产品将会运行被作者明确标记危险的依赖包。

4.8 使用 docker-compose 进行 e2e 测试

TL;DR: 包含实时数据的端到端(e2e)测试,曾经是 CI 过程中最薄弱的环节,因为它依赖于多个像数据库一样繁重的服务。docker-compose 通过使用简单的文本文件和命令来制作类似的生产环境将这个问题变得简单。它允许制定所有的依赖服务,如数据库和网络隔离来进行 e2e 测试。最后但并非最不重要的一点是,它可以在每个测试套件调用之前的保持无状态环境,并且在使用之后立刻销毁。

Otherwise: 没有 docker-compose 团队必须为每个测试环境(包括开发者的机器)维护一个测试数据库,并保持所有数据库同步以使测试结果不会因环境而异。

5 投入生产使用

5.1 监控!

TL;DR: 监控是一个抢在用户之前发现问题的游戏 —— 显然这应该被赋予前所未有的重要性。市场人员会因为报价而不知所措,因此可以考虑从定义你必须要的基本指标开始(如CPU, 内存, Node 进程的内存 (少于 1.4GB), 最近一分钟的错误数, 进程重启次数, 平均响应时间),然后再去看看更多的花式功能(如 DB 性能分析,跨服务测量,前端集成,将原始数据展示给自定义 BI 客户端, slack 提醒等),选择能够满足你条件的解决方案。

Otherwise: 失败 === 失望的用户。 图森破🤓。

🔗 Read More: Monitoring!

5.2 使用聪明的日志来提高透明度

TL;DR: 日志可能是一个充满调试语句的愚蠢仓库,或者是一个讲述应用程序故事的拥有漂亮面板的启动器。从第 1 天开始计划你的日志平台:如何收集,存储和分析日志以确保所需信息(例如,错误率,服务和服务之间的整个事务的追踪等)能够被真正提取。

Otherwise: 你最终会碰到一个很难理解的黑盒,然后你开始重写所有的日志语句来添加额外的信息。

🔗 Read More: Increase transparency using smart logging

5.3 将任何可能的东西(例如gzip,SSL)放在反向代理中

TL;DR: Node 在执行 CPU 密集型任务(如gzipping,SSL终止等)时性能非常糟糕,所以可以使用 “真正的” 中间件服务,如 nginx,HAproxy 或 云平台服务作为代替。

Otherwise: 你可怜的单线程将继续忙于做网络任务,而不是处理你应用程序的核心需求,因此性能会相应地降低。

🔗 Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy

5.4 锁定依赖版本

TL;DR: 你的代码在所有环境中必须相同,但令人惊讶的是,默认情况下,NPM 允许依赖项跨环境偏移 —— 当你在不同环境下安装依赖包时,它会尝试拉取修订号最新的版本。通过使用 NPM 配置文件 .npmrc 可以解决这个问题,它告诉每个环境保存每个模块的确切(而不是最新)版本。或者,为了更细粒度的控制,可以使用 NPM shrinkwrap。
更新:从 NPM5 开始,依赖项被默认锁定。新的打包工具 Yarn
也默认了锁定了版本。

Otherwise: QA 将彻底测试代码并同意使用在生产中可能表现不同的版本。更糟的是,同一个生产集群中的不同服务器可能会运行不同的代码。

PS: 锁定依赖版本也会带来一些问题:

  • 业务使用版本和实际版本相差巨大,升级成本高
  • 有些依赖包的 Bug 修复或功能优化无法得到及时更新,比如一些底层依赖修复了 OOM 问题
  • 其他等

所以有相当一部分团队不选择锁定版本,依靠选择符合语义化版本的开源模块和自动化测试等来规避依赖自动升级导致的问题。我个人觉得几乎难以做到在一个项目里所使用的模块都是完全按语义化版本发布的(例如:有些开源模块的贡献者提交了一个小 patch 但是却引入了自己也没发现的新问题)所以倾向于使用依赖锁定,并在一个小的周期里进行及时更新。

🔗 Read More: Lock dependencies

5.5 使用正确的工具来进行进程守护

TL;DR: 进程必须继续运行并且能在失败之后重启。对于简单的情况,像 PM2 这样的工具可能就足够了,但在当今 dockerized 的世界中,集群管理工具也应该被考虑在内。

Otherwise: 运行数十个没有明确策略的实例以及太多的工具一起(集群管理,docker,PM2)可能会让 devops 陷入混乱。

🔗 Read More: Guard process uptime using the right tool

5.6 利用所有的 CPU

TL;DR: 在其基本形式里,Node 应用在单个 CPU 上运行,而其他 CPU 都很空闲。你应该复制 Node 进程并利用所有 CPU —— 对于中小型应用,你可以使用 Node Cluster 或者 PM2。对于更大的应用,考虑使用一些 Docker 集群(例如 K8S,ECS)工具或基于 Linux init 系统(例如 systemd)的部署脚本来复制进程。

Otherwise: 你的应用程序只使用了全部资源的 25%,甚至更少。请注意,一个典型的服务器有 4 个或更多的 CPU 内核,正常的 Node.JS 的部署只能使用 1 个(就算是使用了 AWS beanstalk 之类的 PaaS 服务!)

🔗 Read More: Utilize all CPU cores

5.7 创建一个 “维护端点”

TL;DR: 在安全的环境中暴露一组与系统相关的 API,如内存使用和 REPL 等。尽管强烈建议使用久经考验的标准工具,但通过代码可以轻松获得一些有价值的信息以及执行一些相关操作。

Otherwise: 你会发现你正在执行许多 “诊断部署” —— 为了提取一些信息用作诊断而将代码发布到生成环境。

🔗 Read More: Create a ‘maintenance endpoint’

5.8 使用 APM 产品来发现错误和停机时间

TL;DR: 监控和性能产品(又称 APM)主动测量你的代码和 API,所以他们可以超越传统的监控,并能跨越服务和层级来衡量整体用户体验。例如,某些 APM 产品可能会突出展示在用户终端加载速度过慢的事务,同时提示根本原因。

Otherwise: 你可能会花很多精力去测量 API 的性能和停机时间,而且很可能你永远也不会察觉到哪些部分才是在真实情况下最慢的,以及它是如何影响用户体验的。

🔗 Read More: Discover errors and downtime using APM products

5.9 使你的代码 “生产就绪”

TL;DR: 以结果为导向写码,从第 1 天开始计划上线。这听起来有些模糊,所以我汇总了一些与生产维护密切相关的开发技巧:

  • Twelve factors
    • 一份基准代码,多份部署
    • 显式声明依赖关系
    • 在环境变量中存储配置
    • 把后端服务当作附加资源
    • 严格分离构建和运行
    • 以一个或多个无状态进程运行应用
    • 通过端口绑定提供服务
    • 通过进程模型进行扩展
    • 快速启动和优雅终止可最大化鲁棒性
    • 尽可能的保持开发、预发布、线上环境相同
    • 把日志当作事件流
    • 后台管理任务当作一次性进程运行
  • 保持无状态 —— 不在特定的服务器上保存状态数据
  • 缓存 —— 大量使用缓存,但不会因缓存不匹配而失败
  • 测试内存 —— 衡量内存使用和泄漏情况也是开发流程的一部分,类似 memwatch 之类的工具可以极大地简化这项任务
  • 命名函数 —— 最小化匿名函数的使用(即内联回调),因为典型的内存分析器会提供每个命名方法的内存使用量。
  • 使用 CI 工具 —— 使用 CI 工具在发布到生产环境之前进行故障检测。例如,使用 ESLint 来检测引用错误和未定义的变量。使用 -trace-sync-io 来标识使用同步 API 的代码
  • 错误管理 —— 错误处理是整个 Node.JS 网站的薄弱环节。很多 Node 进程因为小错误而崩溃,而其他 Node 进程则处于故障状态。

Otherwise: 一个 IT/devops 世界冠军也拯救不了一个写得很烂的系统。

🔗 Read More: Make your code production-ready

5.10 监测内存使用

TL;DR: Node.js 和内存之间的争议:v8 引擎对内存使用(1.4GB)具有软限制,并且在 Node 的代码中存在已知的途径来使内存泄漏,因此监测 Node 进程的内存是必须的。在小型应用中,你可以使用 shell 命令定期测量内存,但在大中型应用中,请考虑将内存监测放入到强大的监控系统中。

Otherwise: 你的进程内存可能会和 Wallmart 里发生的情况一样,每天泄漏 100m。

PS: 这个可以结合监控和报警来做,如果发生内存泄漏,一些 APM 也能帮助进行分析定位。

🔗 Read More: Measure and guard the memory usage

5.11. 让前端资源和 Node 解耦

TL;DR: 使用专用中间件(nginx,S3,CDN)服务前端内容,因为 Node 是单线程模式,在处理多个静态文件时,其性能会受到影响。

Otherwise: 你的单个 Node 线程将忙于传输数百个 html/images/angular/react 文件流,而不是将所有资源分给它真正的目的 —— 提供动态内容

🔗 Read More: Get your frontend assets out of Node

5.12 无状态,每天都杀掉你的服务

TL;DR: 将无任什么类型的数据(例如,用户会话,缓存,上传的文件)放到外部存储中。考虑定期 “杀死” 你的服务,或者使用明确强制执行无状态行为的“无服务器”平台(例如AWS Lambda)。

Otherwise: 特定的服务器出现故障将导致整个应用程序停机,而不是仅仅杀掉有故障的的机器。而且,由于对特定服务器的依赖,弹性扩展将变得更加艰难。

🔗 Read More: Be stateless, kill your Servers almost every day

5.13 使用漏洞自动检测工具

TL;DR: 即便是最出名的模块包,例如 Express,也存在漏洞。可以通过使用社区或商业工具,不断地检查漏洞并报告(本地或GitHub),有些漏洞甚至可以立即修补。

Otherwise: 不用专用工具来进行代码漏洞保护,那么你需要不断地关注有关新漏洞的消息。这是非常无聊的。

🔗 Read More: Use tools that automatically detect vulnerabilities

5.14 为每个日志语句分配 “TransactionId”

TL;DR: 为单个请求中的每个日志条目分配相同的标识符 transaction-id:{some value}。
之后,在检查日志中的错误时,可以轻松地了解错误上下文。不幸的是,由于 Node 的异步性质,实现起来并不容易,请参考链接里的代码示例

Otherwise: 在没有上下文的情况下查看生产环境里的错误日志会使得推断问题的原因变得更加困难和缓慢。

PS: 可以在 API 网关里为每一个请求生成唯一的 requst-id,之后的每次内部调用都进行透传,并且在相关的每个日志中进行打印。再配合上 ELK 就能够非常方便的查询到一次请求和其相关的全部事务了。

🔗 Read More: Assign ‘TransactionId’ to each log statement

5.15 设置 NODE_ENV=production

TL;DR: 将 NODE_ENV 设置为 production 或 development 来标记生产环境优化是否应该被启用 —— 许多 NPM 包会根据当前环境来决定是否优化代码。

Otherwise: 省略这个简单的属性可能会大大降低性能。例如,当使用 Express 进行服务器端渲染时,忽略 NODE_ENV 会使速度降低三倍!

🔗 Read More: Set NODE_ENV=production

5.16 设计自动的,原子的,零停机的部署

TL;DR: 研究表明,执行更多次部署的团队出现严重生产环境问题的概率更低。快速的自动化部署没有手动操作和服务当机的风险,大大改善了部署流程。你或许应该使用 Docker 和 CI 工具来实现这一点,因为它们已经成为简化部署的行业标准了。

Otherwise: 长时间部署 -> 生产环境停机时间 & 人为错误 -> 团队没有信心进行部署 -> 功能和发布减少。

6 安全

Our contributors are working on this section. Would you like to join?

7 性能

Our contributors are working on this section. Would you like to join?

Contributors

Yoni Goldberg

Independent Node.JS consultant who works with customers at USA, Europe and Israel on building large-scale scalable Node applications. Many of the best practices above were first published on his blog post at www.goldbergyoni.com.
Reach Yoni at @goldbergyoni or me@goldbergyoni.com

Ido Richter

👨‍💻 Software engineer, 🌐 web developer, 🤖 emojis enthusiast.

Refael Ackermann @refack refack@gmail.com (he/him)

Node.js Core Collaborator, been noding since 0.4, and have noded in multiple production sites. Founded node4good home of lodash-contrib, formage,
and asynctrace.
refack on freenode, Twitter, GitHub, GMail, and many other platforms. DMs are open, happy to help.

Bruno Scheufler

💻 full-stack web developer and Node.js enthusiast.

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

加密解密 LatteCake

发表于 2017-11-21

加密技术包括两个元素:算法和密钥。算法是将普通的信息或者可以理解的信息与一串数字(密钥)结合,产生不可理解的密文的步骤,密钥是用来对数据进行编码和解密的一种算法。在安全保密中,可通过适当的钥加密技术和管理机制来保证网络的信息通信安全。

大纲介绍

常用的加密解密方法主要有以下加大类:

  • 基本加密方法
  • 对称加密方法
  • 非对称加密方法

下面我们简单介绍一下这几种加密码方法

加密解密技术

我们先来了解一下加密解密的技术。

数据加密和数据解密是一对逆过程。

先来看加密解密的公式:

加密:

数据加密是用加密算法E和加密密钥K1将明文P转换成密文C 用上面公式表示。

解密:

数据解密是数据加密的逆过程, 解密算法D和解密密钥K2奖密文C转换成明文P。

通过下图我们可以清晰的看到,数据加密及解密的整修过程:

发送端将明文P 通过加密算法E与加密密钥Ke,生成密文C,然后传输到接收端,接收端收到密文后通过解密算法D与解密密钥Kd对密文C进行解密,最终还原明文P。

知道了加密的整个过程,我们来看看上面所说的三种加密解密方式。

基本加密方法

基本加密方法只要分为:

  1. 位移法
* 按照一定的规则,重新安排明文中的比特或字符的顺序来形成密文,而字符本身保持不变。

例如: hello world! 我们可以变为: lord l!oleh, 只需要按照一定的规则解开就可以了。
2. 置换法

* 按照一定的规则,用一个字符去置换(替代)另一个字符来形成密文。

我们还是以 hello world! 为例,约定一个简单的算法把h变成9,r>c, l>#,那么得到的结果就是: 9e##o woc##d! 解密把它逆过来就好了,非常简单。

对称加密技术

  • 对称密钥加密 又称为对称加密、私钥加密、共享密钥加密,是密码学中的一类加密算法。
  • 这类算法在加密和解密时使用相同的密钥,或是使用两个可以简单地相互推算的密钥。
  • 实际上,这组密钥成为在两个或多个成员间的共同秘密,以便维持专属的通信联系。

上面说得可能有点饶,举个简单的例子: 假设小明与小红在考试,他们相互约定了一个算法: 当小明连续咳嗽三声的时候小红看着小明,如果小明摸了下左耳朵,那就说明小红可以给小明传答案了。如果没有,那可能是小明感冒了… 然后怎么传答案呢? 小红摸左耳朵代表A, 摸右耳朵代表B,左手抠鼻子代表C,右手抠鼻子代表D

怎么样,通过上面的个简单的例子是不是比较好理解呢,对称加密码就是双方约定好一个算法,通过这个算法进行加密解密。

对称加密类型

常用的对称加密码主要分为以下几类:

  1. DES
  2. 三重DES(TDEA)或3DES
  3. RC-5
  4. IDEA
  5. AES

DES 主要采用替换和移位的方法加密,它用56位密钥对64位二进制数据进行加密,每次加密可对64位输入数据进行16轮编码,经一系列替换和移位后输入的64位原始数据转换成了不同的64位输出数据。DES算法运算速度快,密钥产生容易。

三重DES 在DES的基础上采用了三重DES,用两个56位的密钥k1和k2. 发送方k1加密,k2解密,再使用k1加密。接收方则使用k1解密,k2加密,再使用k1解密

RC-5 引入了一种新的密码基本变换数据相依旋转方法,即一个中间的字是另一个中间的低位所决定的循环移位结果,以提高密码强度

IDEA 是国际数据加密算法 是在DES算法的基础上发展而来,类似于三重DES。密钥长度为128位。

AES 基于排列和置换运算。提成列是对数据重新进行安排,置换是将一个数据单元替换成另一个。可以使用128,192,256们的密钥,并且用128位(16字节)分组加密和解密数据。

DES(数据加密标准)

DES算法为密码体制中的对称密码体制,又被称为美国数据加密标准,是1972年美国IBM公司研制的对称密码体制加密算法。 明文按64位进行分组,密钥长64位,密钥事实上是56位参与DES运算(第8、16、24、32、40、48、56、64位是校验位, 使得每个密钥都有奇数个1)分组后的明文组和56位的密钥按位替代或交换的方法形成密文组的加密方法。

下图是它的加密流程:

DES的密钥长度为56位,这意味着加密时存在256个密钥可供选择,即72,057,594,037,927,936种可能性。

DES现在已经不是一种安全的加密方法,主要因为它使用的56位密钥过短。DES破解机包括1,856个自定义的芯片,可以在数天内破解一个DES密钥—本图显示了使用数个Deep Crack芯片搭成的DES破解机

下图就是专门破解DES加密的芯片:

AES(高级加密码标准)

AES为分组密码,分组密码也就是把明文分成一组一组的,每组长度相等,每次加密一组数据,直到加密完整个明文。在AES标准规范中,分组长度只能是128位,也就是说,每个分组为16个字节(每个字节8位)。密钥的长度可以使用128位、192位或256位。密钥的长度不同,推荐加密轮数也不同。

比如我们家里的无线路由器,一般使用的就是AES加密。

AES算法流程

AES加密算法涉及4种操作:

  • 字节替代
  • 行移位
  • 列混淆
  • 轮密钥加

字节替换,上面已经讲过。

我们可以把加密的数据分解成 4x4 大小的表格,然后对表格里的每个空位进行替换然后移位,再进行列混淆加上密钥,重复上面几个步骤。

下图是加密解密的流程图:

字节代替

字节代替的主要功能是通过S盒完成一个字节到另外一个字节的映射。S盒的详细构造方法可以直接给出构造好的结果,S盒用于提供密码算法的混淆性。

用两张替换表可以秀好的理解:

加密替换表:

解密替换表:

假设: 字节00000000B能过加密替换表x=0,y=0替换后的值为(S[0][0]=)63H,再通过解密替换表即可得到替换前的值x=6,y=3,(S-1 [6][3]=)00H。

行移位

行移位是一个4x4的矩阵内部字节之间的置换,用于提供算法的扩散性。

行移位分有:

  • 正向行移位

假设矩阵的名字为state,用公式表示:state[i][j] = state[i][(j+i)%4];其中i、j属于[0,3]。

  • 逆向行移位

用公式表示:state[i][j] = state[i][(4+j-i)%4];其中i、j属于[0,3]。

正向行移位: 正向行移位用于加密,其原理图如下。其中:第一行保持不变,第二行循环左移8比特,第三行循环左移16比特,第四行循环左移24比特。

逆向行移位: 逆向行移位即是相反的操作,即:第一行保持不变,第二行循环右移8比特,第三行循环右移16比特,第四行循环右移24比特。

列混淆

利用GF(28)域上算术特性的一个代替,同样用于提供算法的扩散性。

同样的也有:

  • 正向列混淆

  • 逆向列混淆

对称加密思考

对称加密速度快但是安全性相对于非对称加密来说低,为什么呢?

  1. 密钥的交换需要建立在安全的通信基础上,而通信本身是不可能绝对安全的
  2. 加密和解密使用相同的密钥,如果信息泄露,提取到了密钥,密文就会被轻易破解
  3. 无法验证发送者,可以用相同的加密方式伪造密文,这时信息的来源就变得不可靠
  4. 密钥每使用一次都被抛弃,需要重新生成密钥

要想使用对称加密,那么分享信息的各个个体之间都需要分享这个密钥,比如1000个人之间都使用同一个密钥进行密文传输,只要其中一个人密钥被盗窃了,那么整体加密的信息将都被破解了。

那有什么方法呢,这个时候就可以引入另一种加密算法 非对称加密。

非对称加密

如何做到即使一个人的密钥被盗窃了,最起码保证你给其他人发送密文不被破解?

简单来说就是: 每个人生成一个“私钥-公钥”对,这个私钥需要每个人自行进行保护!公钥可以随便分享,同时,生成的这个“私钥-公钥”对还有个强大的功能就是,使用私钥加密的信息,只能由该私钥对应的公钥才能解密,使用公钥加密的信息,只能由该公钥对应的私钥才能解密!

还是拿上面小明、小红来举例:

小明生成了他自己的一个“私钥-公钥”对,叫做“小明私钥-小明公钥”,小红生成了他自己的一个“小红私钥-小明公钥”对,之前我们说过私钥要每个个体自己进行保存,公钥可以随便分享,目的是为什么呢?是为了加密信息!

有了公钥、私钥之后,小明在QQ群里与小红说:

非对称解密算法 即使别人截取了,也只是知道该公钥而已,但是要是想解密使用该公钥加密的密文!只有一个人可以办得到!就是小红! 为什么?

小明使用小红的公钥加密的信息,只有小红的公钥所对应的私钥,这里就是“小红私钥”,才能解密!所以,没有小红私钥的第三方即时截取了这些密文,也破解不了!或者更严格的说在有限时间内比如说几十上面年内很难进行暴力破解!

我们来看看官方对非对称加密的解释

公开密钥加密,也称为非对称加密,一种密码学算法类型,在这种密码学方法中,需要一对密钥(其实这里密钥说法不好,就是“钥”),一个是私人密钥,另一个则是公开密钥。这两个密钥是数学相关,用某用户密钥加密后所得的信息,只能用该用户的解密密钥才能解密。如果知道了其中一个,并不能计算出另外一个。因此如果公开了一对密钥中的一个,并不会危害到另外一个的秘密性质。称公开的密钥为公钥;不公开的密钥为私钥。

看明白了么?是不是有一种WFT的感受?

非对称加密需要包含:

  • Private Key 私钥
  • Public Key 公钥

下图是非对称加密解密的过程图:

大概意思就是,要想使用非对称加密算法,首先要有一对key,一个为private key私钥,一个为public key公钥,然后可以把你的public key分发给想给你传密文的用户,然后用户使用该public key加密过得密文,只有使用你的private key才能解密,也就是说,只要你自己保存好你的private key,就能确保,别人想给你发的密文不被破解,所以你不用担心别人的密钥被盗。

  1. 小明用小红给的公钥给明文进加密
  2. 小红用自己的私钥对密文进行解密

这种加密是单向的,所以被称为非对称加密算法

Ok 我们来总结一下 非对称加密码的特点

优缺点

  • 优点:
    1. 允许在不安全的媒体上交换信息
    2. 解密的私钥不发往任何用户,即使密文泄露也不用担心被破解,因为没有私钥
    3. 可以验证消息的发送者
  • 缺点:
    1. 加密速度较慢

这种加密算法应用非常广泛,SSH, HTTPS, TLS,电子证书,电子签名,电子身份证等等。

RSA 加密解密算法

我们来举一个比较常用的算法RSA加密解密算法

RSA加密算法是非对称加密算法的一种,是三个牛逼的人一起提出的。RSA就是下图他们三人姓氏开头字母拼在一起组成的。

我猜左边两个会比右边这个更聪明(看头发(ˉ︶ˉ))

公钥和私钥的产生

我们来看看公钥与私钥的产生一共有四个步骤:

  1. 选择两个大素数p和我(大于10100)
  2. 令n=pq和z=(p-1)(q-1)
  3. 选择d与z的互质
  4. 选择e,使e*d=1(mod z)

(n,e)是公钥,(n,d)是私钥

明文P被分成了k位的块,k是满足2k<n的最大整数,于是有了0<=P<n (n最好大于 1024)

互质关系
  • 什么是互质关系?

如果两个正整数,除了1以外,没有其他公因子,我们就称这两个数是互质关系(coprime)。比如,15和32没有公因子,所以它们是互质关系。这说明,不是质数也可以构成互质关系。

  • 任意两个质数构成互质关系,比如13和61。
  • 一个数是质数,另一个数只要不是前者的倍数,两者就构成互质关系,比如3和10。
  • 如果两个数之中,较大的那个数是质数,则两者构成互质关系,比如97和57。
  • 1和任意一个自然数是都是互质关系,比如1和99。
  • p是大于1的整数,则p和p-1构成互质关系,比如57和56。
  • p是大于1的奇数,则p和p-2构成互质关系,比如17和15。
欧拉函数

任意给定正整数n,在小于等于n的正整数之中,有多少个与n构成互质关系?

计算这个值的方法就叫做欧拉函数,以φ(n)表示。在1到8之中,与8形成互质关系的是1、3、5、7,所以 φ(n) = 4。

如果n可以分解成两个互质的整数之积:

n = p1 × p2

则:

φ(n) = φ(p1p2) = φ(p1)φ(p2)

所以:

φ(56)=φ(8×7)=φ(8)×φ(7)=4×6=24

加密消息

小明想给小红发送一个消息M,他知道小红产生的公钥(n,e)他使用起先与小红约好的格式将M转换为一个小于n的整数m,比如他可以将每一个字转换为这个字的Unicode码,然后将这些数字连在一起组成一个新的数字。如果他的信息非常长的话,他可以将这个信息分为几段,然后将每一段转换为m。用下面这个公式他可以将m加密为c

加密公式:

解密消息

德龙得到海丰的消息c后就可以利用他的私钥d来解码。他可以用以下这个公式来将c转换为m

解密公式:

假设:

1
2
3
apache复制代码p = 3, q = 11, n = 33, z = 20, d = 7, e = 3
C = P3(mod 33)
P = C7(mod 33)

套用公式:

则:

1
2
apache复制代码C = 23(mod 33) = 8(mod 33) = 8
P = 87(mod 33) = 2097152(mod 33) = 2

z=(p-1)(q-1), n=pq, Mod 取余数, (n,e)是公钥, (n,d)是私钥 设明文大P 为2, 2是明文。8是密文

小DEMO

参照上面的公式,我们写一个非常非常非常非常简单的例子:

最终得出的结果如下:

最大值在 32, 最小值: 1,明文不得超过 p * q,所以p,q必须大。

数字签名

RSA 的另一个最大的优点是其在数字签名中的应用。数字签名的作用是确认消息来源的可靠性,保证信息的完整性和不可否认性。假如A要公开发布自己的文件,A先用HASH算法生成这个文件的消息摘要(或者叫信息指纹),再用 RSA 加密算法(A 的私钥)对摘要进行加密。用户想要下载 A 发布的文件,需要同时下载文件和摘要,下载完毕后,用户使用 A 的公钥对摘要进行解密得到 A 之前生成的摘要,并且用户用同样的 HASH 算法对下载到的文件生成一份摘要,比对这两份摘要是否相等,如果相等,那么可以确定两件事情:文件是完整的,文件是
A 发布的。

我们从很多下载文件的网站都会看到一些 SHA256, MD5 码,这就是对文件进行校验的,验证文件是不是官方发页的,有没有被窜改过。我们看golang.org官方源码包:

RSA的算法安全性是基于大素数分解的困难性,攻击者可以分解已知的n,得到p和q,然后可得到z,最后用Euclid算法由e和z得到d。

Openssl生成公私钥

我们可以在服务器上生成自己的公钥私钥,服务器需要安装OpenSSL:

私钥:

下面是所生成的公钥:

RSA的安全性

常见的 RSA 攻击方法有:

  • 直接分解模数n
  • 对 RSA 的选择密文攻击
  • 对 RSA 的同模攻击
  • 对 RSA 小指数的攻击
  • RSA 的比特性攻击

广泛的应用证明 RSA 体制的安全性是相当可靠的。RSA 密码体制的安全性取决于其加密函数求逆的困难性,即大数因子分解的困难性。虽然至今在理论和实践中还未能证明有分解大整数的有效方法,但大数因子分解未被证明为是 NP 问题,随着计算机计算能力的提高,原来被认为不可能分解的某些大整数可能会被成功分解,这对 RSA 密码体制的安全性构成潜在的威胁。

在未来比较长的时间范围内(在不动用国家级计算机的情况下),大约几百年上千年,在不考虑量子计算和对密码算法破解分析没有太大突破的前提下,不大可能通过计算能力提升而使得现有的密码算法得到轻易地破解。

尾巴

本人才书学浅,若有写错,或理解错的地方,欢迎大家指正。

虚心向各们大佬们学习。

深情按压, 小额赞赏, 您的赞赏就是我更新的动力。

本文地址: lattecake.com/post/20112

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Apache Pulsar中的地域复制,第1篇:概念和功能

发表于 2017-11-21

灾难恢复规划,甚至更理想情况下使用的防灾规划,它们的重要性怎么强调都不为过,每周都会有相关的头条新闻报道证明这个结论的正确性。无论什么行业,如果遭遇无法预见的事件并影响到日常运维,组织都需要尽可能快速恢复运转,继续为自己的客户提供服务。从数据安全入侵到自然灾害,必须具备妥善的规划,以便快速灵活地应对灾难性事件。如果不具备行之有效的灾难恢复规划,可能导致组织面临各种风险,例如遭受巨大经济损失、声誉受损,甚至为组织的客户和用户造成更严峻的风险。

在多面性的企业软件系统中,防灾策略和恢复规划需要部署在地理位置分散的多个数据中心内。在此类多数据中心部署中,通常会使用地域复制机制提供额外的冗余,以防某个数据中心故障或其他事件导致无法照常继续运作。

本文和下一篇文章将介绍Apache Pulsar自带的另一个企业级功能:地域复制。Apache Pulsar使用了Apache BookKeeper这一可伸缩的流存储机制,这是一种可跨越多个数据中心,同时支持同步地域复制(借助Apache BookKeeper)和异步地域复制(通过Broker级别的配置)的消息系统。首先本文将介绍一些简单的概念和功能,下篇文章将侧重于具体的部署实践。

概念

地域复制是一种典型的灾难恢复机制。虽然很多数据系统都宣称支持地域复制,然而这些系统通常只能复制到两个数据中心,如果要复制到更多位置,通常会遇到不少的局限。这可能会造成用户困惑,并且不得以只能通过繁琐的方式试着复制到多个数据中心。在介绍Apache Pulsar的地域复制功能之前,下文首先将介绍几个有关地域复制的基本概念。

很多数据系统所用的地域复制机制主要可分为两个类别:同步地域复制和异步地域复制。Apache Pulsar可同时支持这两种类型。下文图1展示了同步和异步地域复制之间的差异。

本例假设了3个数据中心:us-west、us-central和us-east,客户端向us-central发出写操作请求。如果使用同步地域复制,客户端向us-central发起请求后,写入us-central的数据会复制到另外两个数据中心:us-west和us-east。通常只有在大多数的数据中心确认写操作已成功完成后,客户端才会获得有关该写入请求的确认。也就是说在本例中,至少要求两个数据中心确认写入请求已成功完成。这种机制也叫做“同步地域复制”,因为数据会以同步的方式复制到多个数据中心,客户端必须等待其他数据中心的确认。


图1:同步地域复制和异步地域复制

与之相对的异步地域复制中,客户端不需要等待其他数据中心的回应,客户端会在us-central成功完成写入操作后立刻收到回应。随后这些数据会从us-central复制到另外两个数据中心:us-west和us-east,只不过会以异步的方式进行。

同步地域复制可提供最高可用性,此时所有可用的物理数据中心将为数据系统提供一个全球化的逻辑实例。应用程序可在任何数据中心内运行,并且任何时候都可以访问这些数据。这种模式还可以确保不同数据中心之间更强的数据一致性,如果一个数据中心故障,无需任何手工操作即可保障应用程序继续运行。然而应用程序需要承受额外的跨数据中心延迟作为代价,美国东西海岸之间这样的通信延迟通常约为数十毫秒。

异步地域复制的延迟更低,因为客户端不需要等待其他数据中心的回应。然而这种模式提供的一致性保证较弱,毕竟复制是异步进行的。由于异步复制始终存在复制滞后(复制滞后通常是指数据尚未从源复制到目标),因此始终存在还没有从源完全复制到目标的数据。当面临会影响整个数据中心的灾难(例如洪水、火灾、地震等自然灾害,或断电断网等情况)时,尚未完成复制的数据将会丢失。由于复制滞后的存在,应用程序在开发或配置时通常需要考虑此类整个数据中心出现故障的情况。异步地域复制通常主要用于一致性要求不那么高的场合,例如消息系统或非数据库的存储系统。

Apache Pulsar依赖Apache BookKeeper实现持久的消息存储,可同时支持这两种地域复制方法。

在介绍细节前,下文打算简单谈谈典型的Pulsar安装过程,这有助于解释Apache Pulsar何以同时支持同步和异步的地域复制。

图2展示了一个典型安装的Apache Pulsar。Pulsar集群包含两层:一个无状态的服务层(Serving Layer),其中包含一系列用于为pub/sub流量提供服务的中介(Broker);以及一个有状态的持久层(Persistence layer),其中包含一系列用于提供持久消息存储能力的BookKeeper bookie。


图2:典型安装的Apache
Pulsar

这种架构模式实现了存储与pub-sub流量服务的分离,可获得多种优势。Broker可以是“无状态”的,因此可以用更低成本实现负载均衡和流量转移。这种架构已被证明是多租户功能得以成功实现的关键(有关多租户的详情可参阅这篇博客文章)。同时这也是使得Apache Pulsar能够同时支持同步和异步地域复制的关键。

BookKeeper中的同步地域复制

同步地域复制实际上是由Apache BookKeeper在存储层为Pulsar实现的。典型的Pulsar安装通常会提供图3所示的同步地域复制。


图3:使用同步地域复制的Pulsar环境

使用同步地域复制的Pulsar环境包含一个全局的Zookeeper(一整套ZooKeeper环境将跨越多个数据中心运行),一个在多个数据中心内运行的Bookie集群,以及一个同样在多个数据中心内运行的Broker集群。此外需要为BookKeeper配置使用区域感知安放策略,Pulsar Broker将使用该策略跨越数据中心存储数据,同时为写操作提供一致性保障(例如确认之前至少写入到两个数据中心)。


图4:同步地域复制Pulsar环境在一个数据中心故障后的应对方式

如果一个数据中心故障,同步地域复制的Pulsar环境依然可以继续正常运转。其中运行的应用程序基本上不受任何影响,无论添加新的数据中心,或停用老的数据中心,这些操作对应用程序都是透明的。甚至可在应用程序运行过程中不停机增减数据中心。此类配置很适合可以容忍较高延迟的关键业务应用。

Pulsar中的异步地域复制

作为对比,异步地域复制Pulsar集群包含位于多个数据中心内的多个物理集群环境。Pulsar Broker会在这些集群之间以异步的方式复制数据。图5展示了一个异步地域复制的Pulsar环境,建议与图3的同步地域复制环境对比着看。


图5:异步地域复制Pulsar环境

在异步地域复制的情况下,如果针对某个Pulsar主题产生了新的消息,消息首先会持久保存在本地集群中,随后异步复制到远程集群。大部分情况下如果网络连接正常,消息均可以在供本地消耗的同时立刻复制。通常来说,端到端交付延迟是由数据中心之间的网络往返耗时(RTT)决定的。应用程序可以在任何集群中创建生产者(Producer)和消费者(Consumer),哪怕此时远程集群并不可访问(例如在有网络分区操作发生的过程中)。

Pulsar中,可在每个资产(每个租户)的层面上启用异步地域复制。这意味着只有当一个资产已经成功创建,并允许访问所有集群的情况下,才可以在集群间启用异步地域复制。虽然从权限的限制出发,地域复制必须针对每个资产启用,但可以从名称空间的层面加以管理。也就是说,如果租户有权访问数据中心A、B、C和D,即可在数据中心A和B之间为地域复制创建名称空间,并为C和D的地域复制创建另一个名称空间,同时为A、B、C和D之间的全连接(Full-mesh)复制创建第三个名称空间。

在复制策略的定制方面,Pulsar为租户提供了巨大的灵活性。这意味着应用程序可以在多个数据中心之间设置主从模式的复制,双活双向复制,以及全连接复制(图5展示的是用3个数据中心建立的全连接复制)。此外复制操作可由Pulsar Broker自动进行,对应用程序完全透明。其他pub-sub消息系统往往需要额外设置复杂的进程来为数据中心之间的消息创建镜像,Pulsar的地域复制则可在运行过程中随时启用、禁用或更改(例如从主从复制改为双活双向复制),这些操作只需要一条管理命令即可。有关Pulsar中使用异步地域复制实现故障转移、失败回滚等操作,以及其他最佳实践的详情,建议等待下一篇文章。

Yahoo的多数据中心复制

Yahoo自从2015年开始,已经在全球十几个数据中心内部署了Pulsar,并使用了全连接异步地域复制的配置。这一地域复制机制主要被用于关键业务服务,例如邮件、财经、Gemini广告、Sherpa(Yahoo的分布式键-值服务)等,整个系统针对超过140万个主题,每天复制上千万亿条消息。

以这样的规模来看,整个系统必须足够灵活,并且提供必要的工具,帮助用户高效管理复制过程。从增减地域到名称空间的复制集,再到全面了解数据到底会被保存到何处,复制的数据量是多少,为何复制过程非常缓慢的监视能力,都必须充分考虑到。

最后最重要的是,在使用地域复制后,产生网络分区或导致不同数据中心间网络性能退化的可能性会远远高于只使用一个数据中心的时候。因此所用消息和存储组件也必须能够承受更长时间段的构建积压,可能从数小时到数天不等。同样重要的还有,当网络问题顺利解决后,必须能用快过新消息抵达的速度更快速地处理掉积压的内容,同时不能对流量产生影响。

结论

Apache Pulsar充分利用了Apache BookKeeper的可伸缩流存储,这是一种可同时支持同步地域复制(借助Apache BookKeeper)和异步地域复制(在Broker层面配置)的消息系统。本文介绍了数据系统中常用的两种地域复制机制,并探讨了两者的差异和必要的权衡。Pulsar可通过不同机制同时支持这两种地域复制方法。希望本文可以帮你更好地理解Apache Pulsar及其地域复制功能。下篇文章将介绍在Apache Pulsar中使用异步地域复制的几种常见模式和实践。

如果对Pulsar感兴趣,你也可以通过下列方式加入Pulsar社区:

  • Pulsar Slack频道,请在这里注册:apache-pulsar.herokuapp.com/。
  • Pulsar邮件列表。

有关Apache Pulsar项目的常规信息,请访问官网:pulsar.incubator.apache.org/ ,此外也可关注Twitter帐号@apache_pulsar。

作者:Sijie Guo,阅读英文原文:Geo-replication in Apache Pulsar, part 1: concepts and features

感谢杜小芳对本文的审校。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Ruby Web 服务器的并发模型与性能

发表于 2017-11-21
  • WEBrick
  • Thin
  • Unicorn
  • Puma
  • 对比
  • 总结
  • Reference
  • 谈谈 Rack 协议与实现
  • 浅谈 WEBrick 的多线程模型
  • 浅谈 Thin 的事件驱动模型
  • 浅谈 Unicorn 的多进程模型
  • 浅谈 Puma 的并发模型与实现
  • Ruby Web 服务器的并发模型与性能

这是整个 Rack 系列文章的最后一篇了,在之前其实也尝试写过很多系列文章,但是到最后都因为各种原因放弃了,最近由于自己对 Ruby 的 webserver 非常感兴趣,所以看了下社区中常见 webserver 的实现原理,包括 WEBrick、Thin、Unicorn 和 Puma,虽然在 Ruby 社区中也有一些其他的 webserver 有着比较优异的性能,但是在这有限的文章中也没有办法全都介绍一遍。

webservers

在这篇文章中,作者想对 Ruby 社区中不同 webserver 的实现原理和并发模型进行简单的介绍,总结一下前面几篇文章中的内容。

文中所有的压力测试都是在内存 16GB、8 CPU、2.6 GHz Intel Core i7 的 macOS 上运行的,如果你想要复现这里的测试可能不会得到完全相同的结果。

WEBrick

WEBrick 是 Ruby 社区中非常古老的 Web 服务器,从 2000 年到现在已经有了将近 20 年的历史了,虽然 WEBrick 有着非常多的问题,但是迄今为止 WEBrick 也是开发环境中最常用的 Ruby 服务器;它使用了最为简单、直接的并发模型,运行一个 WEBrick 服务器只会在后台启动一个进程,默认监听来自 9292 端口的请求。

webrick-concurrency-model

当 WEBrick 通过 .select 方法监听到来自客户端的请求之后,会为每一个请求创建一个单独 Thread 并在新的线程中处理 HTTP 请求。

1
复制代码run Proc.new { |env| ['200', {'Content-Type' => 'text/plain'}, ['get rack\'d']] }

如果我们如果创建一个最简单的 Rack 应用,直接返回所有的 HTTP 响应,那么使用下面的命令对 WEBrick 的服务器进行测试会得到如下的结果:

1
2
3
4
5
6
7
8
9
10
复制代码Concurrency Level:      100
Time taken for tests: 22.519 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 2160000 bytes
HTML transferred: 200000 bytes
Requests per second: 444.07 [#/sec] (mean)
Time per request: 225.189 [ms] (mean)
Time per request: 2.252 [ms] (mean, across all concurrent requests)
Transfer rate: 93.67 [Kbytes/sec] received

在处理 ApacheBench 发出的 10000 个 HTTP 请求时,WEBrick 对于每个请求平均消耗了 225.189ms,每秒处理了 444.07 个请求;除此之外,在处理请求的过程中 WEBrick 进程的 CPU 占用率很快达到了 100%,通过这个测试我们就可以看出为什么不应该在生产环境中使用 WEBrick 作为 Ruby 的应用服务器,在业务逻辑和代码更加复杂的情况下,WEBrick 的性能想必也不会达到期望。

Thin

在 2006 和 2007 两年,Ruby 社区中发布了两个至今都非常重要的开源项目,其中一个是 Mongrel,它提供了标准的 HTTP 接口,同时多语言的支持也使得 Mongrel 在当时非常流行,另一个项目就是 Rack 了,它在 Web 应用和 Web 服务器之间建立了一套统一的 标准,规定了两者的协作方式,所有的应用只要遵循
Rack 协议就能够随时替换底层的应用服务器。

rack-protoco

随后,在 2009 年出现的 Thin 就站在了巨人的肩膀上,同时遵循了 Rack 协议并使用了 Mongrel 中的解析器,而它也是 Ruby 社区中第一个使用 Reactor 模型的 Web 服务器。

thin-concurrency-model

Thin 使用 Reactor 模型处理客户端的 HTTP 请求,每一个请求都会交由 EventMachine,通过内部对事件的分发,最终执行相应的回调,这种事件驱动的 IO 模型与 node.js 非常相似,使用单进程单线程的并发模型却能够快速处理 HTTP 请求;在这里,我们仍然使用 ApacheBench 以及同样的负载对 Thin 的性能进行简单的测试。

1
2
3
4
5
6
7
8
9
10
复制代码Concurrency Level:      100
Time taken for tests: 4.221 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 880000 bytes
HTML transferred: 100000 bytes
Requests per second: 2368.90 [#/sec] (mean)
Time per request: 42.214 [ms] (mean)
Time per request: 0.422 [ms] (mean, across all concurrent requests)
Transfer rate: 203.58 [Kbytes/sec] received

对于一个相同的 HTTP 请求,Thin 的吞吐量大约是 WEBrick 的四倍,每秒能够处理 2368.90 个请求,同时处理的速度也大幅降低到了 42.214ms;在压力测试的过程中虽然 CPU 占用率有所上升但是在处理的过程中完全没有超过 90%,可以说 Thin 的性能碾压了 WEBrick,这可能也是开发者都不会在生产环境中使用 WEBrick 的最重要原因。

但是同样作为单进程运行的 Thin,由于没有 master 进程的存在,哪怕当前进程由于各种各样奇怪的原因被操作系统杀掉,我们也不会收到任何的通知,只能手动重启应用服务器。

Unicorn

与 Thin 同年发布的 Unicorn 虽然也是 Mongrel 项目的一个 fork,但是使用了完全不同的并发模型,每Unicorn 内部通过多次 fork 创建多个 worker 进程,所有的 worker 进程也都由一个 master 进程管理和控制:

unicorn-master-workers

由于 master 进程的存在,当 worker 进程被意外杀掉后会被 master 进程重启,能够保证持续对外界提供服务,多个进程的 worker 也能够很好地压榨多核 CPU 的性能,尽可能地提高请求的处理速度。

unicorn-concurrency-model

一组由 master 管理的 Unicorn worker 会监听绑定的两个 Socket,所有来自客户端的请求都会通过操作系统内部的负载均衡进行调度,将请求分配到不同的 worker 进程上进行处理。

不过由于 Unicorn 虽然使用了多进程的并发模型,但是每个 worker 进程在处理请求时都是用了阻塞 I/O 的方式,所以如果客户端非常慢就会大大影响 Unicorn 的性能,不过这个问题就可以通过反向代理来 nginx 解决。

unicorn-multi-processes

在配置 Unicorn 的 worker 数时,为了最大化的利用 CPU 资源,往往会将进程数设置为 CPU 的数量,同样我们使用 ApacheBench 以及相同的负载测试一个使用 8 核 CPU 的 Unicorn 服务的处理效率:

1
2
3
4
5
6
7
8
9
10
复制代码Concurrency Level:      100
Time taken for tests: 2.401 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 1110000 bytes
HTML transferred: 100000 bytes
Requests per second: 4164.31 [#/sec] (mean)
Time per request: 24.014 [ms] (mean)
Time per request: 0.240 [ms] (mean, across all concurrent requests)
Transfer rate: 451.41 [Kbytes/sec] received

经过简单的压力测试,当前的一组 Unicorn 服务每秒能够处理 4000 多个请求,每个请求也只消耗了 24ms 的时间,比起使用单进程的 Thin 确实有着比较多的提升,但是并没有数量级的差距。

除此之外,Unicorn 由于其多进程的实现方式会占用大量的内存,在并行的处理大量请求时你可以看到内存的使用量有比较明显的上升。

Puma

距离 Ruby 社区的第一个 webserver WEBrick 发布的 11 年之后的 2011 年,Puma 正式发布了,它与 Thin 和 Unicorn 一样都从 Mongrel 中继承了 HTTP 协议的解析器,不仅如此它还基于 Rack 协议重新对底层进行了实现。

puma-cluster-mode

与 Unicorn 不同的是,Puma 是用了多进程加多线程模型,它可以同时在 fork 出来的多个 worker 中创建多个线程来处理请求;不仅如此 Puma 还实现了用于提高并发速度的 Reactor 模块和线程池能够在提升吞吐量的同时,降低内存的消耗。

puma-concurrency-mode

但是由于 MRI 的存在,往往都需要使用 JRuby 才能最大化 Puma 服务器的性能,但是即便如此,使用 MRI 的 Puma 的吞吐量也能够轻松达到 Unicorn 的两倍。

1
2
3
4
5
6
7
8
9
10
复制代码Concurrency Level:      100
Time taken for tests: 1.057 seconds
Complete requests: 10000
Failed requests: 0
Total transferred: 750000 bytes
HTML transferred: 100000 bytes
Requests per second: 9458.08 [#/sec] (mean)
Time per request: 10.573 [ms] (mean)
Time per request: 0.106 [ms] (mean, across all concurrent requests)
Transfer rate: 692.73 [Kbytes/sec] received

在这里我们创建了 8 个 Puma 的 worker,每个 worker 中都包含 16~32 个用于处理用户请求的线程,每秒中处理的请求数接近 10000,处理时间也仅为 10.573ms,多进程、多线程以及 Reactor 模式的协作确实能够非常明显的增加 Web 服务器的工作性能和吞吐量。

在 Puma 的 官方网站 中,有一张不同 Web 服务器内存消耗的对比图:

memory-usage-comparision

我们可以看到,与 Unicorn 相比 Puma 的内存使用量几乎可以忽略不计,它明显解决了多个 worker 占用大量内存的问题;不过使用了多线程模型的 Puma 需要开发者在应用中保证不同的线程不会出现竞争条件的问题,Unicorn 的多进程模型就不需要开发者思考这样的事情。

对比

上述四种不同的 Web 服务器其实有着比较明显的性能差异,在使用同一个最简单的 Web 应用时,不同的服务器表现出了差异巨大的吞吐量:

ruby-webservers

Puma 和 Unicorn 两者之间可能还没有明显的数量级差距,1 倍的吞吐量差距也可能很容易被环境因素抹平了,但是 WEBrick 可以说是绝对无法与其他三者匹敌的。

上述的不同服务器其实有着截然不同的 I/O 并发模型,因为 MRI 中 GIL 的存在我们很难利用多核 CPU 的计算资源,所以大多数多线程模型在 MRI 上的性能可能只比单线程略好,达不到完全碾压的效果,但是 JRuby 或者 Rubinius 的使用确实能够利用多核 CPU 的计算资源,从而增加多线程模型的并发效率。

jruby

传统的 I/O 模型就是在每次接收到客户端的请求时 fork 出一个新的进程来处理当前的请求或者在服务器启动时就启动多个进程,每一个进程在同一时间只能处理一个请求,所以这种并发模型的吞吐量有限,在今天已经几乎看不到使用 accept & fork 这种方式处理请求的服务器了。

目前最为流行的方式还是混合多种 I/O 模型,同时使用多进程和多线程压榨 CPU 计算资源,例如 Phusion Passenger 或者 Puma 都支持在单进程和多进程、单线程和多线程之前来回切换,配置的不同会创建不同的并发模型,可以说是 Web 服务器中最好的选择了。

最后要说的 Thin 其实使用了非常不同的 I/O 模型,也就是事件驱动模型,这种模型在 Ruby 社区其实并没有那么热门,主要是因为 Rails 框架以及 Ruby 社区中的大部分项目并没有按照 Reactor 模型的方式进行设计,默认的文件 I/O 也都是阻塞的,而 Ruby 本身也可以利用多进程和多线程的计算资源,没有必要使用事件驱动的方式最大化并发量。

nodejs-logo

Node.js 就完全不同了。Javascript 作为一个所有操作都会阻塞主线程的语言,更加需要事件驱动模型让主线程只负责接受 HTTP 请求,其余的脏活累活都交给线程池来做了,结果的返回都通过回调的形式通知主线程,这样才能提高吞吐量。

总结

在这个系列的文章中,我们先后介绍了 Rack 的实现原理以及 Rack 协议,还有四种 webserver 包括 WEBrick、Thin、Unicorn 和 Puma 的实现,除了这四种应用服务器之外,Ruby 社区中还有其他的应用服务器,例如:Rainbows 和 Phusion Passenger,它们都有各自的实现以及优缺点。

从当前的情况来看,还是更推荐开发者使用 Puma 或者 Phusion Passenger 作为应用的服务器,这样能获得最佳的效果。

Reference

  • Ruby Web 服务器:这十五年
  • Ruby 服务器对比
  • Ruby 的多线程应用服务器介绍
  • Ruby on Rails Server options

关于图片和转载

知识共享许可协议
本作品采用知识共享署名 4.0 国际许可协议进行许可。
转载时请注明原文链接,图片在使用时请保留图片中的全部内容,可适当缩放并在引用处附上图片所在的文章链接,图片使用 Sketch 进行绘制。

关于评论和留言

如果对本文 Ruby Web 服务器的并发模型与性能 的内容有疑问,请在下面的评论系统中留言,谢谢。

原文链接:Ruby Web 服务器的并发模型与性能 · 面向信仰编程

Follow: Draveness · GitHub

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

有关数据验证的原则

发表于 2017-11-20

今天下午和小伙伴们开会想到的,在会上也和伙伴们简单沟通一下。在这里详细和大家分享一下对于这个话题的认知。如果要简单概括一下我的想法,那就是:对于数据验证而言,前端验证很有必要,但后端验证必须要存在。

什么是数据验证

数据验证其实就是数据的合法性校验,例如:用户填写的用户名或者地址是不是正确,格式是否正确等等。

为什么要进行数据校验。

基本可以开过为以下几点需求:

1,约束用户输入的内容

2,获取真实的用户信息

3,人性化的验证,提升用户体验

4,对自己系统的保护

前端验证和后端验证的区别

前端验证:它可以为用户提供快速反馈,做到快速响应,使用户能够及时察觉所填写数据的不合法性。基本上用JS脚本代码实现,不需要把数据提交到远程服务器。比如,鼠标移上去会有提示效果,鼠标离开,就会马上告诉你数据是否合法等等。

后端验证:不管在前端输入什么,确保前端端送往服务器最后处理的所有数据都是有效的,避免出现服务端漏洞或者不应该的异常。例如,正常的流程是用户需要验证身份之后才能有某些操作,但是通过 API 调用的时候,不需要认证也能直接执行相关操作,执行认证之后的各种权限。

数据验证的原则:

1.前端验证可以不完备,但后端验证必须要稳固

两点理由:

(1)可以通过某些工具绕过前端验证,后端验证是保证数据有效性的防线。

(2)前端验证有局限性。例如身份信息或者征信信息,需要调用第三方API,通过后端进行验证。

2. 前端验证同样很重要,可以优化用户的体验

前端验证不用提交数据,可以快速给出相应提示,提升用户体验,降低服务端的调用次数,减小压力。

一点总结

前端验证给用户带来方便,但是它不能保证安全性,可以被轻易绕过。因此,对于一个安全的数据验证方案,后端的验证是必须的,在每一个API调用时,必须考虑到这个要求。

扫描二维码或手动搜索微信公众号【架构栈】: ForestNotes

欢迎转载,带上以下二维码即可

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…937938939…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%