代理到底有几种功能模式?

通常我们都知道Nginx性能很高,尤其是作为一个代理服务器,因为它用的是epoll模型,就比如Python Django Web的性能不行,我们可能就会在前端加一个nginx代理,从而提高总体的处理性能问题,但是我们常说的代理,值的是什么类型的代理呢,你知道吗...
继续阅读 »
通常我们都知道Nginx性能很高,尤其是作为一个代理服务器,因为它用的是epoll模型,就比如Python Django Web的性能不行,我们可能就会在前端加一个nginx代理,从而提高总体的处理性能问题,但是我们常说的代理,值的是什么类型的代理呢,你知道吗?
 
代理服务技术是在互联网早期就出现被使用的。一般实现代理技术的方式就是在服务器上安装代理服务软件,让其成为一个代理服务器,从而实现代理技术。常用的代理技术分为正向代理、反向代理和透明代理。本文就是针对这三种代理来讲解一些基本原理和具体的适用范围和nginx的配置案例,便于大家更深入理解代理服务技术。
 


一、正向代理(Forward Proxy)


一般情况下,如果没有特别说明,代理技术默认说的是正向代理技术。
 
正向代理(forward)是一个位于客户端 【用户A】和原始服务器(origin server)【服务器B】之间的服务器【代理服务器Z】,为了从原始服务器取得内容,用户A向代理服务器Z发送一个请求并指定目标(服务器B),然后代理服务器Z向服务器B转交请求并将获得的内容返回给客户端。客户端必须要进行一些特别的设置才能使用正向代理,如下图所示:
ForwardAgent.png

从上面的概念中,我们看出,文中所谓的正向代理就是代理服务器【Z】替代访问方【用户A】去访问目标服务器【服务器B】。
 
这就是正向代理的意义所在。而为什么要用代理服务器【Z】去代替访问方【用户A】去访问服务器【B】呢?为什么不直接访问呢,这就要从场景和代理服务器使用的意义说起。
 
使用正向代理的意义和场景有如下几种:
 
1、用户访问本来无法访问的服务器B的资源
SpeedAccess.png

假设最初用户A要访问服务器B需要经过R1和R2路由器这样一个路由节点,如果路由器R1或者路由器R2发生故障,那么就无法访问服务器B了。但是如果用户A让代理服务器Z去代替自己访问服务器B,由于代理服务器Z没有在路由器R1或R2节点中,而是通过其它的路由节点访问服务器B,那么用户A就可以得到服务器B的数据了。

现实中的例子就是“翻墙”。不过自从VPN技术被广泛应用外,“翻墙”不但使用了传统的正向代理技术,有的还使用了VPN技术。
 
2、加速访问服务器B资源
这种说法目前不像以前那么流行了,主要是带宽流量的飞速发展。早期的正向代理中,很多人使用正向代理就是提速。
 
还是如图2假设用户A到服务器B,经过R1路由器和R2路由器,而R1到R2路由器的链路是一个低带宽链路。而用户A到代理服务器Z,从代理服务器Z到服务器B都是高带宽链路。那么很显然就可以加速访问服务器B了。
 
3、Cache作用
CacheFile.png

Cache(缓存)技术和代理服务技术是紧密联系的(不光是正向代理,反向代理也使用了Cache(缓存)技术。如上图所示,如果在用户A访问服务器B某数据F之前,已经有人通过代理服务器Z访问过服务器B上得数据F,那么代理服务器Z会把数据F保存一段时间,如果有人正好取该数据F,那么代理服务器Z不再访问服务器B,而把缓存的数据F直接发给用户A。这一技术在Cache中术语就叫Cache命中。如果有更多的像用户A的用户来访问代理服务器Z,那么这些用户都可以直接从代理服务器Z中取得数据F,而不用千里迢迢的去服务器B下载数据了。
 
4、客户端访问授权
这方面的内容现今使用的还是比较多的,例如一些公司采用ISA Server做为正向代理服务器来授权用户是否有权限访问互联网,如下图所示:
AccessAuth.png

如上图防火墙作为网关,用来过滤外网对其的访问。假设用户A和用户B都设置了代理服务器,用户A允许访问互联网,而用户B不允许访问互联网(这个在代理服务器Z上做限制)这样用户A因为授权,可以通过代理服务器访问到服务器B,而用户B因为没有被代理服务器Z授权,所以访问服务器B时,数据包会被直接丢弃。
 
5、隐藏访问者的行踪
如下图所示,我们可以看出服务器B并不知道访问自己的实际是用户A,因为代理服务器Z代替用户A去直接与服务器B进行交互。如果代理服务器Z被用户A完全控制(或不完全控制),会惯以“肉鸡”术语称呼。
Hidden.png

 
总结正向代理是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。
 
Nginx正向代理配置示例如下:
server{
resolver 8.8.8.8;
resolver_timeout 30s;
listen 82;
location / {
proxy_pass http://$http_host$request_uri;
proxy_set_header Host $http_host;
proxy_buffers 256 4k;
proxy_max_temp_file_size 0;
proxy_connect_timeout 30;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 301 1h;
proxy_cache_valid any 1m;
}
}
1、不能有hostname。 
2、必须有resolver, 即dns,即上面的8.8.8.8,超时时间(30秒)可选。 
3、配置正向代理参数,均是由 Nginx 变量组成。
proxy_pass $scheme://$host$request_uri;  
proxy_set_header Host $http_host;
4、配置缓存大小,关闭磁盘缓存读写减少I/O,以及代理连接超时时间。
proxy_buffers 256 4k;  
proxy_max_temp_file_size 0;
proxy_connect_timeout 30;
5、配置代理服务器 Http 状态缓存时间。  
proxy_cache_valid 200 302 10m;  
proxy_cache_valid 301 1h;
proxy_cache_valid any 1m;
配置好后,重启nginx,以浏览器为例,要使用这个代理服务器,则只需将浏览器代理设置为http://+服务器ip地址+:+82(82是刚刚设置的端口号)即可使用了。


二、反向代理(reverse proxy)


反向代理正好与正向代理相反,对于客户端而言代理服务器就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端。
 
使用反向代理服务器的作用如下:
 
1、保护和隐藏原始资源服务器
RevProxy.png

用户A始终认为它访问的是原始服务器B而不是代理服务器Z,但实用际上反向代理服务器接受用户A的应答,从原始资源服务器B中取得用户A的需求资源,然后发送给用户A。由于防火墙的作用,只允许代理服务器Z访问原始资源服务器B。尽管在这个虚拟的环境下,防火墙和反向代理的共同作用保护了原始资源服务器B,但用户A并不知情。
 
2、负载均衡
LoadBlance.png

当反向代理服务器不止一个的时候,我们甚至可以把它们做成集群,当更多的用户访问资源服务器B的时候,让不同的代理服务器Z(x)去应答不同的用户,然后发送不同用户需要的资源。

当然反向代理服务器像正向代理服务器一样拥有Cache的作用,它可以缓存原始资源服务器B的资源,而不是每次都要向原始资源服务器组请求数据,特别是一些静态的数据,比如图片和文件,如果这些反向代理服务器能够做到和用户X来自同一个网络,那么用户X访问反向代理服务器X,就会得到很高质量的速度。这正是CDN技术的核心。如下图所示:
CdnProxy.png

我们并不是讲解CDN,所以去掉了CDN最关键的核心技术智能DNS。只是展示CDN技术实际上利用的正是反向代理原理这块。

反向代理结论与正向代理正好相反,对于客户端而言它就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端,就像这些内容原本就是它自己的一样。

基本上,网上做正反向代理的程序很多,能做正向代理的软件大部分也可以做反向代理。开源软件中最流行的就是squid,既可以做正向代理,也有很多人用来做反向代理的前端服务器。另外MS ISA也可以用来在Windows平台下做正向代理。反向代理中最主要的实践就是WEB服务,近些年来最火的就是Nginx了。网上有人说Nginx不能做正向代理,其实是不对的。Nginx也可以做正向代理,不过用的人比较少了。
 
Nginx反向代理示例:
http {
# 省略了前面一般的配置,直接从负载均衡这里开始
# 设置地址池,后端3台服务器
upstream http_server_pool {
server 192.168.1.2:8080 weight=2 max_fails=2 fail_timeout=30s;
server 192.168.1.3:8080 weight=3 max_fails=2 fail_timeout=30s;
server 192.168.1.4:8080 weight=4 max_fails=2 fail_timeout=30s;
}
# 一个虚拟主机,用来反向代理http_server_pool这组服务器
server {
listen 80;
# 外网访问的域名
server_name www.test.com;
location / {
# 后端服务器返回500 503 404错误,自动请求转发到upstream池中另一台服务器
proxy_next_upstream error timeout invalid_header http_500 http_503 http_404;
proxy_pass http://http_server_pool;
proxy_set_header Host www.test.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
access_log logs/www.test.com.access.log combined;
}
}
最简单的反向代理演示(在一台服务器上做代理服务器,将http请求转发到另一台IIS服务器上,通过二级域名形式访问。)编辑vim nginx.conf
server {
listen 80;
server_name test.zhoumengkang.com;
location / {
proxy_pass http://121.199.**.*:80;
}
}
参考:http://www.blogjava.net/xiaomage234/archive/2011/09/08/358247.html 
 


三、透明代理


如果把正向代理、反向代理和透明代理按照人类血缘关系来划分的话。那么正向代理和透明代理是很明显堂亲关系,而正向代理和反向代理就是表亲关系了 。

透明代理的意思是客户端根本不需要知道有代理服务器的存在,它改编你的request fields(报文),并会传送真实IP。注意,加密的透明代理则是属于匿名代理,意思是不用设置使用代理了。

透明代理实践的例子就是时下很多公司使用的行为管理软件。如下图所示:
tmproxy.png

用户A和用户B并不知道行为管理设备充当透明代理行为,当用户A或用户B向服务器A或服务器B提交请求的时候,透明代理设备根据自身策略拦截并修改用户A或B的报文,并作为实际的请求方,向服务器A或B发送请求,当接收信息回传,透明代理再根据自身的设置把允许的报文发回至用户A或B,如上图,如果透明代理设置不允许访问服务器B,那么用户A或者用户B就不会得到服务器B的数据。
 
Nginx透明代理配置示例:
# cat /etc/nginx/sites-enabled/proxy
server {
resolver 8.8.8.8;
access_log off;
listen [::]:8080;
location / {
proxy_pass $scheme://$host$request_uri;
proxy_set_header Host $http_host;
proxy_buffers 256 4k;
proxy_max_temp_file_size 0k;
}
}

iptables -t nat -A PREROUTING -s 10.8.0.0/24 -p tcp --dport 80 -j DNAT --to 192.168.0.253:8080
RAW Paste Data
# cat /etc/nginx/sites-enabled/proxy
server {
resolver 8.8.8.8;
access_log off;
listen [::]:8080;
location / {
proxy_pass $scheme://$host$request_uri;
proxy_set_header Host $http_host;
proxy_buffers 256 4k;
proxy_max_temp_file_size 0k;
}
}

iptables -t nat -A PREROUTING -s 10.8.0.0/24 -p tcp --dport 80 -j DNAT --to 192.168.0.253:8080
参考:
http://z00w00.blog.51cto.com/515114/1031287  
https://mengkang.net/78.html   收起阅读 »

解锁Python集合推导式和字典推导式

集合推导式 集合推导式(set comprehensions)跟列表推导式也是类似的, 唯一的区别在于它们使用大括号{}表示。Code: sets = {x for x in range(10)} Result: {0, 1, 2, 3, ...
继续阅读 »


集合推导式


集合推导式(set comprehensions)跟列表推导式也是类似的, 唯一的区别在于它们使用大括号{}表示。
Code:
sets = {x for x in range(10)}
Result:
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
type.png

集合解析把列表解析中的中括号变成大括号,返回集合。
 
下面我们来个应用场景,一直一个列表中有很多元素,我们做到快速去重。
Code:
heavy = {x for x in [2, 3, 5, 3, 5, 2, 6]}
print(heavy)
Result:
{2, 3, 5, 6}
集合推导式生成内容,结果要是可hash的:
hashable.png


字典推导式


字典推导式(dict comprehensions)和列表推导的使用方法也是类似的。
dictreb.png

字典解析也是使用大括号包围,并且需要两个表达式,一个生成key, 一个生成value 两个表达式之间使用冒号分割,返回结果是字典.

说了这么多推导式,为什么没有元组推导式呢,元组和列表的操作几乎是一样的,除了不可变特性以外
Code:
tuple([x for x in range(10)])
Result:
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
收起阅读 »

Python使用类来写装饰器

前两天发现了装饰器可以直接使用类来写,分享一下,需要用类里面的__call__方法,__call__方法就是可以把这个实例当成一个函数来调用,如果正常使用类里面的方法的话,实例方法要先实例化类,然后才能调用,静态方法、类方法则需要用类名或者实例化之后的对象来调...
继续阅读 »
前两天发现了装饰器可以直接使用类来写,分享一下,需要用类里面的__call__方法,__call__方法就是可以把这个实例当成一个函数来调用,如果正常使用类里面的方法的话,实例方法要先实例化类,然后才能调用,静态方法、类方法则需要用类名或者实例化之后的对象来调用,而实例化之后的这个对象,是不能被调用的,__call__方法就是把这个实例变成一个可以调用的对象,也就是说实例化之后,这个对象就可以和一个普通函数一样被调用。

示例代码如下:
class Foo:
def __call__(self, *args, **kwargs):
print('call....')
def test(self):#
print('test....')
if __name__ == '__main__':
t = Foo()#实例化类
t.test()#正常调用实例方法
t()#直接调用实例化之后的对象
运行结果:
>>>test.... #这个是调用test方法的时候输出的
>>>call....#这个是执行调用这个实例化之后的方法输出的
理解了上面的之后,就可以使用class来写一个装饰器了,计算程序的运行时间,当然思想和以前用函数写装饰器是一样的
class Fuck(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
import time
start_time = time.time()
res = self.func(*args, **kwargs)
end_time = time.time()
print('the function "%s" run time is %s' % (self.func.__name__,
(end_time - start_time)))
return res
@Fuck
def run(name):
import time
time.sleep(1)
return 'sb_%s' % name
print(run('hyf'))
运行结果:
>>>the function "run" run time is 1.0001001358032227#这个是装饰器帮我们计算的函数运行时间
>>>sb_hyf#这个是正常运行run函数的时候,返回的值
原文地址:http://www.nnzhp.cn/blog/2017/01/16/1/  收起阅读 »

解锁Python列表推导式

Pythonic的核心就是Python之禅,而Python的列表推导式就得以很好的体现。 列表推导式 定义: 列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表。 它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是0个或多个...
继续阅读 »
Pythonic的核心就是Python之禅,而Python的列表推导式就得以很好的体现。


列表推导式


定义:
列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表。

它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是0个或多个for或者if语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以if和for语句为上下文的表达式运行完成之后产生。
 
列表解析的一般形式:
[expr for item in itratorable]

Code: [2 ** n for n in range(10)]

Result: [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
listresult.png

列表解析返回的是列表, 列表的内容是表达式执行的结果.
 
[expr for item in iterable if cond]

[x ** 0.5 for x in range(10) if x % 2 == 0]
[0.0, 1.4142135623730951, 2.0, 2.449489742783178, 2.8284271247461903]
[expr for item in iterable if cond1 if cond2]

[x for x in range(10) if x % 2 == 0 if x > 1]
[2, 4, 6, 8]
[expr for item1 in iterable1 for item2 in iterable2]

[(x, y) for x in range(10) for y in range(10) if (x+y) %2 == 0]
listerror.png

listdie.png

列表解析用于对可迭代对象做过滤和转换,返回值是列表.
 
特性一:代码变短,可读性更好
list.png

从上图代码示例中我们明显可以看出,列表推导式相比常规方法,写出来的代码更加符合pythonic,更加简短,可读性更好。
 
有些人甚至更喜欢使用它而不是filter函数生成列表,但是当你使用列表推导式效果会更加,列表推导式在有些情况下超赞,特别是当你需要使用for循环来生成一个新列表.
 
特征二:推导式速度更快
#!/usr/bin/env python3
# author: nock
import timeit

lst = list(range(10))
# 常规方法
def origin(lst):
plus_one = []
for i in lst:
plus_one.append(i + 1)
return plus_one

# 列表推导式
def fast(lst):
return [ x + 1 for x in lst ]

otime = timeit.timeit('origin(range(10))', globals=globals())
print("func origin exec time is {0}".format(otime))

ftime = timeit.timeit('fast(range(10))', globals=globals())
print("func origin exec time is {0}".format(ftime))
结果:
func origin exec time is 2.1059355960023822
func origin exec time is 1.6507169340038672

如果你使用map或者filter结合lambda生成列表,也是没有列表推导式速度快的,有兴趣的可以自己Coding一下。
  收起阅读 »

Nginx location配置及rewrite规则写法总结

location正则写法 简单示例如下: location = / { # 精确匹配 /,主机名后面不能带任何字符串 [ configuration A ] } location / { # 因为所有的地址都以 / 开头,所以这条规则...
继续阅读 »
nginxre.png


location正则写法


简单示例如下
location = / {
# 精确匹配 /,主机名后面不能带任何字符串
[ configuration A ]
}

location / {
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求 ,比如我们代理的一个站点根
# 但是正则和最长字符串会优先匹配
[ configuration B ]
}

location /documents/ {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration C ]
}

location ~ /documents/Abc {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration CC ]
}

location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
[ configuration D ]
}

location ~* \.(gif|jpg|jpeg)$ {
# 匹配所有以 gif,jpg或jpeg 结尾的请求
# 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
[ configuration E ]
}

location /images/ {
# 字符匹配到 /images/,继续往下,会发现 ^~ 存在
[ configuration F ]
}

location /images/abc {
# 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
# F与G的放置顺序是没有关系的
[ configuration G ]
}

location ~ /images/abc/ {
# 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
[ configuration H ]
}

location ~* /js/.*/\.js

  • =开头表示精确匹配 如A中只匹配根目录结尾的请求,后面不能带任何字符串。
  • ^~ 开头表示uri以某个常规字符串开头,不是正则匹配
  • ~ 开头表示区分大小写的正则匹配;
  • ~* 开头表示不区分大小写的正则匹配
  • / 通用匹配, 如果没有其它匹配,任何请求都会匹配到
 匹配优先级:

(location =) > (location 完整路径) > (location ^~ 路径) > (location ~,~* 正则顺序) > (location 部分起始路径) > (/)

按照上面的location顺序,以下的匹配示例成立:
  • / -> config A  # 精确完全匹配,即使/index.html也匹配不了
  • /downloads/download.html -> config B   # 匹配B以后,往下没有任何匹配,采用B
  • /images/1.gif -> configuration D   # 匹配到F,往下匹配到D,停止往下
  • /images/abc/def -> config D   # 最长匹配到G,往下匹配D,停止往下,你可以看到 任何以/images/开头的都会匹配到D并停止,FG写在这里是没有任何意义的,H是永远轮不到的,这里只是为了说明匹配顺序
  • /documents/document.html -> config C    # 匹配到C,往下没有任何匹配,采用C
  • /documents/1.jpg -> configuration E   # 匹配到C,往下正则匹配到E
  • /documents/Abc.jpg -> config CC   # 最长匹配到C,往下正则顺序匹配到CC,不会往下到E
 

实际使用建议

 
所以实际使用中,个人觉得至少有三个匹配规则定义,如下:#直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。#这里是直接转发给后端应用服务器了,也可以是一个静态首页# 第一个必选规则location = / {    proxy_pass http://tomcat:8080/index}# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用location ^~ /static/ {    root /webroot/static/;}location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {    root /webroot/res/;}#第三个规则就是通用规则,用来转发动态请求到后端应用服务器#非静态文件请求就默认是动态请求,自己根据实际把握#毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了location / {    proxy_pass http://tomcat:8080/}
参考:http://tengine.taobao.org/book/chapter_02.html  http://nginx.org/en/docs/http/ngx_http_rewrite_module.html   

Rewrite规则

ewrite功能就是,使用nginx提供的全局变量或自己设置的变量,结合正则表达式和标志位实现url重写以及重定向。rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://seanlook.com/a/we/index.php?id=1&u=str 只对/a/we/index.php重写。语法rewrite regex replacement [flag]; 如果相对域名或参数字符串起作用,可以使用全局变量匹配,也可以使用proxy_pass反向代理。表明看rewrite和location功能有点像,都能实现跳转,主要区别在于rewrite是在同一域名内更改获取资源的路径,而location是对一类路径做控制访问或反向代理,可以proxy_pass到其他机器。很多情况下rewrite也会写在location里,它们的执行顺序是:[list=1]
  • 执行server块的rewrite指令
  • 执行location匹配
  • 执行选定的location中的rewrite指令
  • 如果其中某步URI被重写,则重新循环执行1-3,直到找到真实存在的文件;循环超过10次,则返回500 Internal Server Error错误。  flag标志位
    • last : 相当于Apache的[L]标记,表示完成rewrite
    • break : 停止执行当前虚拟主机的后续rewrite指令集
    • redirect : 返回302临时重定向,地址栏会显示跳转后的地址
    • permanent : 返回301永久重定向,地址栏会显示跳转后的地址
      因为301和302不能简单的只返回状态码,还必须有重定向的URL,这就是return指令无法返回301,302的原因了。这里 last 和 break 区别有点难以理解:[list=1]
  • last一般写在server和if中,而break一般使用在location中
  • last不终止重写后的url匹配,即新的url会再从server走一遍匹配流程,而break终止重写后的匹配
  • break和last都能组织继续执行后面的rewrite指令
  •   if指令与全局变量if判断指令语法为if(condition){...},对给定的条件condition进行判断。如果为真,大括号内的rewrite指令将被执行,if条件(conditon)可以是如下任何内容:
    • 当表达式只是一个变量时,如果值为空或任何以0开头的字符串都会当做false
    • 直接比较变量和内容时,使用=或!=
    • ~正则表达式匹配,~*不区分大小写的匹配,!~区分大小写的不匹配
    -f和!-f用来判断是否存在文件-d和!-d用来判断是否存在目录-e和!-e用来判断是否存在文件或目录-x和!-x用来判断文件是否可执行 示例:
    if ($http_user_agent ~ MSIE) {    rewrite ^(.*)$ /msie/$1 break;} //如果UA包含"MSIE",rewrite请求到/msid/目录下if ($http_cookie ~* "id=([^;]+)(?:;|$)") {    set $id $1; } //如果cookie匹配正则,设置变量$id等于正则引用部分if ($request_method = POST) {    return 405;} //如果提交方法为POST,则返回状态405(Method not allowed)。return不能返回301,302if ($slow) {    limit_rate 10k;} //限速,$slow可以通过 set 指令设置if (!-f $request_filename){    break;    proxy_pass  http://127.0.0.1; } //如果请求的文件名不存在,则反向代理到localhost 。这里的break也是停止rewrite检查if ($args ~ post=140){    rewrite ^ http://example.com/ permanent;} //如果query string中包含"post=140",永久重定向到example.comlocation ~* \.(gif|jpg|png|swf|flv)$ {    valid_referers none blocked www.jefflei.com www.leizhenfang.com;    if ($invalid_referer) {        return 404;    } //防盗链}
     全局变量下面是可以用作if判断的全局变量:
    • $args : #这个变量等于请求行中的参数,同$query_string
    • $content_length : 请求头中的Content-length字段。
    • $content_type : 请求头中的Content-Type字段。
    • $document_root : 当前请求在root指令中指定的值。
    • $host : 请求主机头字段,否则为服务器名称。
    • $http_user_agent : 客户端agent信息
    • $http_cookie : 客户端cookie信息
    • $limit_rate : 这个变量可以限制连接速率。
    • $request_method : 客户端请求的动作,通常为GET或POST。
    • $remote_addr : 客户端的IP地址。
    • $remote_port : 客户端的端口。
    • $remote_user : 已经经过Auth Basic Module验证的用户名。
    • $request_filename : 当前请求的文件路径,由root或alias指令与URI请求生成。
    • $scheme : HTTP方法(如http,https)。
    • $server_protocol : 请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
    • $server_addr : 服务器地址,在完成一次系统调用后可以确定这个值。
    • $server_name : 服务器名称。
    • $server_port : 请求到达服务器的端口号。
    • $request_uri : 包含请求参数的原始URI,不包含主机名,如:”/foo/bar.php?arg=baz”。
    • $uri : 不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。
    • $document_uri : 与$uri相同。
     示例:http://localhost:88/test1/test2/test.php
    $host:localhost$server_port:88$request_uri:http://localhost:88/test1/test2/test.php$document_uri:/test1/test2/test.php$document_root:/var/www/html$request_filename:/var/www/html/test1/test2/test.php
    常用正则
    • . : 匹配除换行符以外的任意字符
    • ? : 重复0次或1次
    • + : 重复1次或更多次
    • * : 重复0次或更多次
    • \d :匹配数字
    • ^ : 匹配字符串的开始
    • $ : 匹配字符串的介绍
    • {n} : 重复n次
    • {n,} : 重复n次或更多次
    • [c] : 匹配单个字符c
    • [a-z] : 匹配a-z小写字母的任意一个

    小括号()之间匹配的内容,可以在后面通过$1来引用,$2表示的是前面第二个()里的内容。正则里面容易让人困惑的是\转义特殊字符。
     
     
    rewrite实例
    示例1:
    http {
    # 定义image日志格式
    log_format imagelog '[$time_local] ' $image_file ' ' $image_type ' ' $body_bytes_sent ' ' $status;
    # 开启重写日志
    rewrite_log on;

    server {
    root /home/www;

    location / {
    # 重写规则信息
    error_log logs/rewrite.log notice;
    # 注意这里要用‘’单引号引起来,避免{}
    rewrite '^/images/([a-z]{2})/([a-z0-9]{5})/(.*)\.(png|jpg|gif)$' /data?file=$3.$4;
    # 注意不能在上面这条规则后面加上“last”参数,否则下面的set指令不会执行
    set $image_file $3;
    set $image_type $4;
    }

    location /data {
    # 指定针对图片的日志格式,来分析图片类型和大小
    access_log logs/images.log mian;
    root /data/images;
    # 应用前面定义的变量。判断首先文件在不在,不在再判断目录在不在,如果还不在就跳转到最后一个url里
    try_files /$arg_file /image404.html;
    }
    location = /image404.html {
    # 图片不存在返回特定的信息
    return 404 "image not found\n";
    }
    }
    对形如/images/ef/uh7b3/test.png的请求,重写到/data?file=test.png,于是匹配到location /data,先看/data/images/test.png文件存不存在,如果存在则正常响应,如果不存在则重写tryfiles到新的image404 location,直接返回404状态码。
     
    示例2:
    rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;
    对形如/images/bla_500x400.jpg的文件请求,重写到/resizer/bla.jpg?width=500&height=400地址,并会继续尝试匹配location。 收起阅读 »

    Mac下最简单翻墙的方法

    自动代理配置url: https://raw.githubusercontent.com/bannedbook/fanqiang/master/ios/ios.pac 图示:完美解决
    继续阅读 »

    Centos7下添加开机自启动服务和脚本

    最近刚玩Centos7的系统,跟Centos6还是很多方面有改变的,这里记录一下怎么在Centos7下添加开机自启动脚本和服务的方法。   1、添加开机自启服务 我这里以docker 服务为例,设置如下两条命令即可: # systemctl enable ...
    继续阅读 »
    最近刚玩Centos7的系统,跟Centos6还是很多方面有改变的,这里记录一下怎么在Centos7下添加开机自启动脚本和服务的方法。
     


    1、添加开机自启服务


    我这里以docker 服务为例,设置如下两条命令即可:
    # systemctl enable docker.service  #设置docker服务为自启动服务 相当于我们的 chkconfig docker on
    # sysstemctl start docker.service #启动docker服务


    2、添加开机自启脚本


    在centos7中增加脚本有两种常用的方法,以脚本StartTomcat.sh为例:
    #!/bin/bash
    # description:开机自启脚本
    /usr/local/tomcat/bin/startup.sh #启动tomcat
    方法一:
    1、赋予脚本可执行权限(/opt/script/StartTomcat.sh是你的脚本路径)
    # chmod +x /opt/script/StartTomcat.sh
    2、打开/etc/rc.d/rc.local文件,在末尾增加如下内容
    echo "/opt/script/StartTomcat.sh" >> /etc/rc.d/rc.local
    3、在centos7中,/etc/rc.d/rc.local的权限被降低了,所以需要执行如下命令赋予其可执行权限
    chmod +x /etc/rc.d/rc.local

    方法二:
    1、将脚本移动到/etc/rc.d/init.d目录下
    # mv  /opt/script/StartTomcat.sh /etc/rc.d/init.d
    2、增加脚本的可执行权限
    chmod +x  /etc/rc.d/init.d/StartTomcat.sh
    3、添加脚本到开机自动启动项目中
    cd /etc/rc.d/init.d
    chkconfig --add StartTomcat.sh
    chkconfig StartTomcat.sh on
    收起阅读 »

    阿里云和Azure ICON图标矢量素材分享

    我们经常画图会用到一些图标,下面分享阿里云和微软云的一些矢量素材。 阿里云 Azure 图标下载地址:https://pan.baidu.com/s/1i5smr69 
    继续阅读 »
    icon.png

    我们经常画图会用到一些图标,下面分享阿里云和微软云的一些矢量素材。


    阿里云


    AliyunIcon1.png

    AliyunIcon2.png


    Azure


    AzureIcon1.png

    AzureIcon2.png

    图标下载地址:https://pan.baidu.com/s/1i5smr69  收起阅读 »

    设置转发和代理访问阿里MongoDB云数据库

    摘要:基于安全原因考虑,阿里云MongoDB云数据库目前只支持从阿里云ECS上访问,无法通过公网直接访问,不方便用户在本地开发环境里直接进行测试,但是开发就是要测试性能,没有办法作为一个运维你必须想办法了,本文介绍能让用户通过公网访问MongoDB...
    继续阅读 »
    AliyunMongodb.png


    摘要:基于安全原因考虑,阿里云MongoDB云数据库目前只支持从阿里云ECS上访问,无法通过公网直接访问,不方便用户在本地开发环境里直接进行测试,但是开发就是要测试性能,没有办法作为一个运维你必须想办法了,本文介绍能让用户通过公网访问MongoDB云数据库的方案。


    环境架构说明
    环境说明:
    包含公网 + 私网ip的ECS
    • 公网IP地址:121.196.197.64
    • 内网IP地址:10.0.0.110
     MongoDB云数据库
    • 节点1:s-uf6745fa496c28d4.mongodb.rds.aliyuncs.com:3717(Primary,通过ping域名来获取对应的ip,获取到ip地址10.0.0.119,因域名对应的ip可能发生变化,在生产环境切勿直接指定ip,可以直接用域名)
    • 节点2:s-uf624ab1be981c34.mongodb.rds.aliyuncs.com:3717

     
    结构示意图如下:
    MongodbArch.png

    目标:
    通过121.196.197.64:27017能访问到10.0.0.119:3717提供的MongoDB云服务。

    方案一:Iptables实现
    利用iptables的nat机制,可以方便的实现请求转发,首先需要ECS开启包转发的支持
    echo 1 > /proc/sys/net/ipv4/ip_forward
    配置转发规则
    iptables -t nat -A PREROUTING -d 121.196.197.64 -p tcp --dport 27017 -j DNAT --to-destination 10.0.0.119:3717
    iptables -t nat -A POSTROUTING -d 10.0.0.119 -p tcp --dport 3717 -j SNAT --to-source 10.0.0.110
    此时你就能在任意能连通公网的机器上连接121.196.197.64:27017访问MongoDB云服务,所有的请求都会转发到10.0.0.119:3717上,如果你需要严格限制,只允许你办公区公网ip地址访问的话,添加第一条规则的时候还可以增加一个 -s 参数,限制访问源!

    需要注意的是,此时访问121.196.197.64:27017只能以单节点的方式直连,而不能按复制集的方式访问。
    mongo --host 121.196.197.64:27017 --authenticationDatabase admin -uroot -pxxoo

    方案二:Harpoxy四层代理实现
    haproxy支持tcp(四层)、http(七层)2种转发模式,类似于iptables,我们也可以利用haproxy来实现公网访问MongoDB云数据库。

    修改/etc/haproxy/haproxy.cfg配置文件内容,根据默认的配置文件稍作修改,主要配置tcp转发模式、前端、后端服务的地址信息。(haproxy版本为1.5.4)
    global
    log 127.0.0.1 local2

    chroot /var/lib/haproxy
    pidfile /var/run/haproxy.pid
    maxconn 4000
    user haproxy
    group haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

    defaults
    # 使用tcp转发模式
    mode tcp
    log global
    option dontlognull
    option http-server-close
    option redispatch
    retries 3
    timeout http-request 10s
    timeout queue 1m
    timeout connect 10s
    timeout client 1m
    timeout server 1m
    timeout http-keep-alive 10s
    timeout check 10s
    maxconn 3000

    # 前端地址信息, 121.196.197.64:27017
    frontend main 121.196.197.64:27017
    default_backend app

    # 后端地址信息,10.0.0.119:3717
    backend app
    balance roundrobin
    server app1 10.0.0.119:3717 check
    启动haproxy
    haproxy -f /etc/haproxy/haproxy.cfg
    此时你就能在任意能连通公网的机器上连接121.196.197.64:27017访问MongoDB云服务,所有的请求都会转发到10.0.0.119:3717上。

    需要注意的是,此时访问121.196.197.64:27017只能以单节点的方式直连,也不能按复制集的方式访问。
    mongo --host 121.196.197.64:27017 --authenticationDatabase admin -uroot -pxxoo
    以上服务同样适用于RDS、Redis云数据库,但切记仅能用于测试开发环境,生产环境请做好安全心里准备! 收起阅读 »

    Docker容器启动过程

    下面让我们看看一个Docker容器它启动过程中,背后到底做了什么?docker run -i -t ubuntu /bin/bash输入上面这行命令,启动一个ubuntu容器时,到底发生了什么?   大致过程可以用下图描述: 首先系统要有一个dock...
    继续阅读 »
    下面让我们看看一个Docker容器它启动过程中,背后到底做了什么?
    docker run -i -t ubuntu /bin/bash
    输入上面这行命令,启动一个ubuntu容器时,到底发生了什么?
     
    大致过程可以用下图描述:
    dockerflow.png

    首先系统要有一个docker daemon的后台进程在运行,当刚才这行命令敲下时,发生了如下动作:
    1. docker client(即:docker终端命令行)会调用docker daemon请求启动一个容器,
    2. docker daemon会向host os(即:linux)请求创建容器
    3. linux会创建一个空的容器(可以简单理解为:一个未安装操作系统的裸机,只有虚拟出来的CPU、内存等硬件资源)
    4. docker daemon请检查本机是否存在docker镜像文件(可以简单理解为操作系统安装光盘),如果有,则加载到容器中(即:光盘插入裸机,准备安装操作系统)
    5. 将镜像文件加载到容器中(即:裸机上安装好了操作系统,不再是裸机状态)

     
    最后,我们就得到了一个ubuntu的虚拟机,然后就可以进行各种操作了。
     
     
    如果在第4步检查本机镜像文件时,发现文件不存在,则会到默认的docker镜像注册机构(即:docker hub网站)去联网下载,下载回来后,再进行装载到容器的动作,即下图所示:
    dockerload.png

    另外官网有一张图也很形象的描述了这个过程:
    dockerfollow.png

    原文地址:http://www.cnblogs.com/yjmyzz/p/docker-container-start-up-analysis.html 

    参考文章:
    https://www.gitbook.com/book/joshhu/docker_theory_install/details  
    https://docs.docker.com/engine/introduction/understanding-docker/  收起阅读 »