目前Redhat的RDO,packstack的bug还是很多,rpm包的更新比较慢,所以你遇到的bug,很可能在trunk里已经更新,所以,你可以直接用packstack的源码来安装。

设置源

我的实验环境,其实已经配置后源。

yum install -y <a style="color: #787878;" href="http://rdo.fedorapeople.org/openstack-icehouse/rdo-release-icehouse.rpm">http://rdo.fedorapeople.org/openstack-icehouse/rdo-release-icehouse.rpm</a>

安装packstack

yum install -y python-netaddr python-setuptools git
git clone git:<span class="rem" style="color: #008000;">//github.com/stackforge/packstack</span>
cd packstack
python setup.py install_puppet_modules
./bin/packstack --gen-answer-file=node12.txt

剩下的就是设置应答文件

 

什么是CGI
CGI全称是“公共网关接口”(Common Gateway Interface),HTTP服务器与你的或其它机器上的程序进行“交谈”的一种工具,其程序须运行在网络服务器上。
CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等

什么是FastCGI
FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是 CGI最为人诟病的fork-and-execute 模式)。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。
FastCGI是语言无关的、可伸缩架构的CGI开放扩展,其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周 知,CGI解释器的反复加载是CGI性能低下的主要原因,如果CGI解释器保持在内存中并接受FastCGI进程管理器调度,则可以提供良好的性能、伸缩 性、Fail- Over特性等等。

FastCGI与CGI特点
1、如CGI,FastCGI也具有语言无关性.
2、如CGI, FastCGI在进程中的应用程序,独立于核心web服务器运行,提供了一个比API更安全的环境。(APIs把应用程序的代码与核心的web服务器链接 在一起,这意味着在一个错误的API的应用程序可能会损坏其他应用程序或核心服务器; 恶意的API的应用程序代码甚至可以窃取另一个应用程序或核心服务器的密钥。)
3、FastCGI技术目前支持语言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等。相关模块在Apache, ISS, Lighttpd等流行的服务器上也是可用的。
4、如CGI,FastCGI的不依赖于任何Web服务器的内部架构,因此即使服务器技术的变化, FastCGI依然稳定不变。

FastCGI的工作原理
1、Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)
2、FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。
3、当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。
4、FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。

在上述情况中,你可以想象CGI通常有多慢。每一个Web请求PHP都必须重新解析php.ini、重新载入全部扩展并重初始化全部数据结 构。使用FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。

FastCGI的不足
因为是多进程,所以比CGI多线程消耗更多的服务器内存,PHP-CGI解释器每进程消耗7至25兆内存,将这个数字乘以50或100就是很大的内存数。
Nginx 0.8.46+PHP 5.2.14(FastCGI)服务器在3万并发连接下,开启的10个Nginx进程消耗150M内存(15M*10=150M),开启的64个php-cgi进程消耗1280M内存(20M*64=1280M),加上系统自身消耗的内存,总共消耗不到2GB内存。如果服务器内存较小,完全可以只开启25 个php-cgi进程,这样php-cgi消耗的总内存数才500M。
上面的数据摘自Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建胜过Apache十倍的Web服务器(第6版)

什么是PHP-CGI 
PHP-CGI是PHP自带的FastCGI管理器。
启动PHP-CGI,使用如下命令:

php-cgi -b 127.0.0.1:9000

PHP-CGI的不足
1、php-cgi变更php.ini配置后需重启php-cgi才能让新的php-ini生效,不可以平滑重启
2、直接杀死php-cgi进程,php就不能运行了。(PHP-FPM和Spawn-FCGI就没有这个问题,守护进程会平滑从新生成新的子进程。)

什么是PHP-FPM
PHP-FPM是一个PHP FastCGI管理器,是只用于PHP的,可以在 http://php-fpm.org/download下载得到.
PHP-FPM其实是PHP源代码的一个补丁,旨在将FastCGI进程管理整合进PHP包中。必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。
现在我们可以在最新的PHP 5.3.2的源码树里下载得到直接整合了PHP-FPM的分支,据说下个版本会融合进PHP的主分支去。相对Spawn-FCGI,PHP-FPM在 CPU和内存方面的控制都更胜一筹,而且前者很容易崩溃,必须用crontab进行监控,而PHP-FPM则没有这种烦恼。
PHP5.3.3已经集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进 程、可以平滑重载PHP配置,比spawn-fcgi具有更多有点,所以被PHP官方收录了。在./configure的时候带 –enable-fpm参数即可开启PHP-FPM。

使用PHP-FPM来控制PHP-CGI的FastCGI进程

/usr/local/php/sbin/php-fpm{start|stop|quit|restart|reload|logrotate}
--start 启动php的fastcgi进程
--stop 强制终止php的fastcgi进程
--quit 平滑终止php的fastcgi进程
--restart 重启php的fastcgi进程
--reload 重新平滑加载php的php.ini
--logrotate 重新启用log文件

什么是Spawn-FCGI
Spawn-FCGI是一个通用的FastCGI管理服务器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn- FCGI进行FastCGI模式下的管理工作,不过有不少缺点。而PHP-FPM的出现多少缓解了一些问题,但PHP-FPM有个缺点就是要重新编译,这 对于一些已经运行的环境可能有不小的风险(refer),在php 5.3.3中可以直接使用PHP-FPM了。
Spawn-FCGI目前已经独成为一个项目,更加稳定一些,也给很多Web 站点的配置带来便利。已经有不少站点将它与nginx搭配来解决动态网页。
最新的lighttpd也没有包含这一块了(http://www.lighttpd.net/search?q=Spawn- FCGI),但可以在以前版本中找到它。在lighttpd-1.4.15版本中就包含了(http://www.lighttpd.net /download/lighttpd-1.4.15.tar.gz)
目前Spawn-FCGI的下载地址是http://redmine.lighttpd.net/projects/spawn- fcgi,最新版本是http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz
注:最新的Spawn-FCGI可以到lighttpd.net网站搜索“Spawn-FCGI”找到它的最新版本发布地址

下面我们就可以使用Spawn-FCGI来控制php-CGI的FastCGI进程了

    /usr/local/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -C 5 -u www-data -g www-data -f /usr/bin/php-CGI
  参数含义如下:
  -f 指定调用FastCGI的进程的执行程序位置,根据系统上所装的PHP的情况具体设置
  -a 绑定到地址addr
  -p 绑定到端口port
  -s 绑定到unix socket的路径path
  -C 指定产生的FastCGI的进程数,默认为5(仅用于PHP)
  -P 指定产生的进程的PID文件路径
  -u和-g FastCGI使用什么身份(-u 用户 -g 用户组)运行,Ubuntu下可以使用www-data,其他的根据情况配置,如nobody、apache等

PHP-FPM与spawn-CGI对比测试
PHP-FPM的使用非常方便,配置都是在PHP-FPM.ini的文件内,而启动、重启都可以从php/sbin/PHP-FPM中进 行。更方便的是修改php.ini后可以直接使用PHP-FPM reload进行加载,无需杀掉进程就可以完成php.ini的修改加载。
使用PHP-FPM可以使php有不小的性能提升。PHP-FPM控制的进程cpu回收的速度比较慢,内存分配的很均匀。
Spawn-FCGI控制的进程CPU下降的很快,而内存分配的比较不均匀。有很多进程似乎未分配到,而另外一些却占用很高。可能是由于进程任务分配的不均匀导致的.而这也导致了总体响应速度的下降。而PHP-FPM合理的分配,导致总体响应的提到以及任务的平均。

PHP-FPM与Spawn-FCGI功能比较
参考:http://php-fpm.org/about/
PHP-FPM、Spawn-FCGI都是守护php-cgi的进程管理器。

 

 

postfix在main.cf中用下面四个做限制,那么这四者到底有什么区别?

  1. smtpd_recipient_restrictions
  2. smtpd_client_restrictions
  3. smtpd_sender_restrictions
  4. smtpd_helo_restrictions

首先得明白recipient, client, sender, helo的意思:

  1. recipient 就是指收件人,即RCPT TO阶段的收件人
  2. client指的是连接到postfix的客户机的ip地址,这里包含了ip地址及ip反解
  3. sender就是来信人,即MAIL FROM的内容
  4. helo就是HELO阶段,由客户机发送过来的helo主机名

这4个restriction就是对上述4个特征内容进行限制的。区别就不言而明了,至于说执行的次序,这个和SMTP命令产生的顺序是一样的,首先是CONNECT阶段(client),然后发送HELO(helo),再是MAIL FROM(检查sender),再到RCPT TO(检查recipient)。

smtpd_xxx_restrictions可以配置的规则请看:
http://www.postfix.org/postconf.5.html#smtpd_recipient_restrictions

综述

Python这门解释性语言也有专门的线程模型,Python虚拟机使用GIL(Global Interpreter Lock,全局解释器锁)来互斥线程对共享资源的访问,但暂时无法利用多处理器的优势。

在Python中我们主要是通过thread和 threading这两个模块来实现的,其中Python的threading模块是对thread做了一些包装的,可以更加方便的被使用,所以我们使用 threading模块实现多线程编程。这篇文章我们主要来看看Python对多线程编程的支持。

在语言层面,Python对多线程提供了很好的支持,可以方便地支持创建线程、互斥锁、信号量、同步等特性。下面就是官网上介绍threading模块的基本资料及功能:

实现模块

  • thread:多线程的底层支持模块,一般不建议使用;
  • threading:对thread进行了封装,将一些线程的操作对象化

threading模块

  • Thread 线程类,这是我们用的最多的一个类,你可以指定线程函数执行或者继承自它都可以实现子线程功能;
  • Timer与Thread类似,但要等待一段时间后才开始运行;
  • Lock 锁原语,这个我们可以对全局变量互斥时使用;
  • RLock 可重入锁,使单线程可以再次获得已经获得的锁;
  • Condition 条件变量,能让一个线程停下来,等待其他线程满足某个“条件”;
  • Event 通用的条件变量。多个线程可以等待某个事件发生,在事件发生后,所有的线程都被激活;
  • Semaphore为等待锁的线程提供一个类似“等候室”的结构;
  • BoundedSemaphore 与semaphore类似,但不允许超过初始值;
  • Queue:实现了多生产者(Producer)、多消费者(Consumer)的队列,支持锁原语,能够在多个线程之间提供很好的同步支持。

其中Thread类

  • 是你主要的线程类,可以创建进程实例。该类提供的函数包括:
  • getName(self) 返回线程的名字
  • isAlive(self) 布尔标志,表示这个线程是否还在运行中
  • isDaemon(self) 返回线程的daemon标志
  • join(self, timeout=None) 程序挂起,直到线程结束,如果给出timeout,则最多阻塞timeout秒
  • run(self) 定义线程的功能函数
  • setDaemon(self, daemonic) 把线程的daemon标志设为daemonic
  • setName(self, name) 设置线程的名字
  • start(self) 开始线程执行

其中Queue提供的类

  • Queue队列
  • LifoQueue后入先出(LIFO)队列
  • PriorityQueue 优先队列

接下来,我们将会用一个一个示例来展示threading的各个功能,包括但不限于:两种方式起线程、threading.Thread类的重要函数、使用Lock互斥及RLock实现重入锁、使用Condition实现生产者和消费者模型、使用Event和Semaphore多线程通信

两种方式起线程

在Python中我们主要是通过thread和threading这两个模块来实现的,其中Python的threading模块是对thread做了一些包装的,可以更加方便的被使用,所以我们使用threading模块实现多线程编程。一般来说,使用线程有两种模式,一种是创建线程要执行的函数,把这个函数传递进Thread对象里,让它来执行;另一种是直接从Thread继承,创建一个新的class,把线程执行的代码放到这个新的 class里。

将函数传递进Thread对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
 
<span style="font-weight: bold; color: #ff7700;">def</span> thread_fun(num):
    <span style="font-weight: bold; color: #ff7700;">for</span> n <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #008000;">int</span>(num)):
        <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">" I come from %s, num: %s"</span> <span style="color: #66cc66;">%</span>( <span style="color: #dc143c;">threading</span>.currentThread().getName(), n)
 
<span style="font-weight: bold; color: #ff7700;">def</span> main(thread_num):
    thread_list = <span style="color: #008000;">list</span>()<span style="color: #66cc66;">;</span>
    <span style="font-style: italic; color: #808080;"># 先创建线程对象</span>
    <span style="font-weight: bold; color: #ff7700;">for</span> i <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, thread_num):
        thread_name = <span style="color: #483d8b;">"thread_%s"</span> <span style="color: #66cc66;">%</span>i
        thread_list.append(<span style="color: #dc143c;">threading</span>.Thread(target = thread_fun, name = thread_name, args = (<span style="color: #ff4500;">20</span>,)))
 
    <span style="font-style: italic; color: #808080;"># 启动所有线程</span>
    <span style="font-weight: bold; color: #ff7700;">for</span> <span style="color: #dc143c;">thread</span> <span style="font-weight: bold; color: #ff7700;">in</span> thread_list:
        <span style="color: #dc143c;">thread</span>.start()
 
    <span style="font-style: italic; color: #808080;"># 主线程中等待所有子线程退出</span>
    <span style="font-weight: bold; color: #ff7700;">for</span> <span style="color: #dc143c;">thread</span> <span style="font-weight: bold; color: #ff7700;">in</span> thread_list:
        <span style="color: #dc143c;">thread</span>.join()
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    main(<span style="color: #ff4500;">3</span>)

程序启动了3个线程,并且打印了每一个线程的线程名字,这个比较简单吧,处理重复任务就派出用场了,下面介绍使用继承threading的方式;

继承自threading.Thread类:

1
2
3
4
5
6
7
8
9
10
11
12
13
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)<span style="color: #66cc66;">;</span>
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s"</span> <span style="color: #66cc66;">%</span><span style="color: #008000;">self</span>.name
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> <span style="color: #dc143c;">thread</span> <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">5</span>):
        t = MyThread()
        t.start()

接下来,将会介绍如何控制这些线程,包括子线程的退出,子线程是否存活及将子线程设置为守护线程(Daemon)。

threading.Thread类的重要函数

介绍threading模块中的主类Thread的一些主要方法,实例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s"</span> <span style="color: #66cc66;">%</span> (<span style="color: #008000;">self</span>.name)
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> i <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">5</span>):
        my_thread = MyThread()
        my_thread.start()

1、name相关
你可以为每一个thread指定name,默认的是Thread-No形式的,如上述实例代码打印出的一样:

    I am Thread-1
    I am Thread-2
    I am Thread-3
    I am Thread-4
    I am Thread-5

当然你可以指定每一个thread的name,这个通过setName方法,代码:

1
2
3
<span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
     <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
     <span style="color: #008000;">self</span>.setName(<span style="color: #483d8b;">"new"</span> + <span style="color: #008000;">self</span>.name)

2、join方法
join方法原型如下,这个方法是用来阻塞当前上下文,直至该线程运行结束:

1
<span style="font-weight: bold; color: #ff7700;">def</span> join(<span style="color: #008000;">self</span>, timeout=<span style="color: #008000;">None</span>):

timeout可以设置超时时间

3、setDaemon方法
当我们在程序运行中,执行一个主线程,如果主线程又创建一个子线程,主线程和子线程就分兵两路,当主线程完成想退出时,会检验子线程是否完成。如果子线程未完成,则主线程会等待子线程完成后再退出。但是有时候我们需要的是,只要主线程完成了,不管子线程是否完成,都要和主线程一起退出,这时就可以用setDaemon方法,并设置其参数为True。

使用Lock互斥锁

现在我们考虑这样一个问题:假设各个线程需要访问同一公共资源,我们的代码该怎么写?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">time</span>
 
counter = <span style="color: #ff4500;">0</span>
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> counter
        <span style="color: #dc143c;">time</span>.sleep(<span style="color: #ff4500;">1</span>)<span style="color: #66cc66;">;</span>
        counter += <span style="color: #ff4500;">1</span>
        <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s, set counter:%s"</span> <span style="color: #66cc66;">%</span> (<span style="color: #008000;">self</span>.name, counter)
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> i <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">200</span>):
        my_thread = MyThread()
        my_thread.start()

解决上面的问题,我们兴许会写出这样的代码,我们假设跑200个线程,但是这200个线程都会去访问counter这个公共资源,并对该资源进行处理(counter += 1),代码看起来就是这个样了,但是我们看下运行结果:

I am Thread-69, set counter:64
I am Thread-73, set counter:66I am Thread-74, set counter:67I am Thread-75, set counter:68I am Thread-76, set counter:69I am Thread-78, set counter:70I am Thread-77, set counter:71I am Thread-58, set counter:72I am Thread-60, set counter:73I am Thread-62, set counter:74I am Thread-66, set counter:75I am Thread-70, set counter:76I am Thread-72, set counter:77I am Thread-79, set counter:78I am Thread-71, set counter:78

打印结果我只贴了一部分,从中我们已经看出了这个全局资源(counter)被抢占的情况,问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为“线程不安全”。在开发过程中我们必须要避免这种情况,那怎么避免?这就用到了我们在综述中提到的互斥锁了。

互斥锁概念

Python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。在Python中我们使用threading模块提供的Lock类。
我们对上面的程序进行整改,为此我们需要添加一个互斥锁变量mutex = threading.Lock(),然后在争夺资源的时候之前我们会先抢占这把锁mutex.acquire(),对资源使用完成之后我们在释放这把锁mutex.release()。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">time</span>
 
counter = <span style="color: #ff4500;">0</span>
<span style="color: #dc143c;">mutex</span> = <span style="color: #dc143c;">threading</span>.Lock()
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> counter, <span style="color: #dc143c;">mutex</span>
        <span style="color: #dc143c;">time</span>.sleep(<span style="color: #ff4500;">1</span>)<span style="color: #66cc66;">;</span>
        <span style="font-weight: bold; color: #ff7700;">if</span> <span style="color: #dc143c;">mutex</span>.acquire():
            counter += <span style="color: #ff4500;">1</span>
            <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s, set counter:%s"</span> <span style="color: #66cc66;">%</span> (<span style="color: #008000;">self</span>.name, counter)
            <span style="color: #dc143c;">mutex</span>.release()
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> i <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">100</span>):
        my_thread = MyThread()
        my_thread.start()

同步阻塞
当一个线程调用Lock对象的acquire()方法获得锁时,这把锁就进入“locked”状态。因为每次只有一个线程1可以获得锁,所以如果此时另一个线程2试图获得这个锁,该线程2就会变为“block“同步阻塞状态。直到拥有锁的线程1调用锁的release()方法释放锁之后,该锁进入“unlocked”状态。线程调度程序从处于同步阻塞状态的线程中选择一个来获得锁,并使得该线程进入运行(running)状态。

进一步考虑

通过对公共资源使用互斥锁,这样就简单的到达了我们的目的,但是如果我们又遇到下面的情况:

  • 1、遇到锁嵌套的情况该怎么办,这个嵌套是指当我一个线程在获取临界资源时,又需要再次获取;
  • 2、如果有多个公共资源,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源;

上述这两种情况会直接造成程序挂起,即死锁,下面我们会谈死锁及可重入锁RLock。

死锁的形成

前一篇文章Python:使用threading模块实现多线程编程四[使用Lock互斥锁]我们已经开始涉及到如何使用互斥锁来保护我们的公共资源了,现在考虑下面的情况–
如果有多个公共资源,在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,这会引起什么问题?

死锁概念
所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。

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
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
 
counterA = <span style="color: #ff4500;">0</span>
counterB = <span style="color: #ff4500;">0</span>
 
mutexA = <span style="color: #dc143c;">threading</span>.Lock()
mutexB = <span style="color: #dc143c;">threading</span>.Lock()
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="color: #008000;">self</span>.fun1()
        <span style="color: #008000;">self</span>.fun2()
 
    <span style="font-weight: bold; color: #ff7700;">def</span> fun1(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> mutexA, mutexB
        <span style="font-weight: bold; color: #ff7700;">if</span> mutexA.acquire():
            <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s , get res: %s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, <span style="color: #483d8b;">"ResA"</span>)
 
            <span style="font-weight: bold; color: #ff7700;">if</span> mutexB.acquire():
                <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s , get res: %s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, <span style="color: #483d8b;">"ResB"</span>)
                mutexB.release()
 
        mutexA.release()
 
    <span style="font-weight: bold; color: #ff7700;">def</span> fun2(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> mutexA, mutexB
        <span style="font-weight: bold; color: #ff7700;">if</span> mutexB.acquire():
            <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s , get res: %s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, <span style="color: #483d8b;">"ResB"</span>)
 
            <span style="font-weight: bold; color: #ff7700;">if</span> mutexA.acquire():
                <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s , get res: %s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, <span style="color: #483d8b;">"ResA"</span>)
                mutexA.release()
 
        mutexB.release()
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> i <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">100</span>):
        my_thread = MyThread()
        my_thread.start()

代码中展示了一个线程的两个功能函数分别在获取了一个竞争资源之后再次获取另外的竞争资源,我们看运行结果:

I am Thread-1 , get res: ResA
I am Thread-1 , get res: ResB
I am Thread-2 , get res: ResAI am Thread-1 , get res: ResB

可以看到,程序已经挂起在那儿了,这种现象我们就称之为”死锁“。

避免死锁
避免死锁主要方法就是:正确有序的分配资源,避免死锁算法中最有代表性的算法是Dijkstra E.W 于1968年提出的银行家算法。

可重入锁RLock

考虑这种情况:如果一个线程遇到锁嵌套的情况该怎么办,这个嵌套是指当我一个线程在获取临界资源时,又需要再次获取。
根据这种情况,代码如下:

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
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">time</span>
 
counter = <span style="color: #ff4500;">0</span>
<span style="color: #dc143c;">mutex</span> = <span style="color: #dc143c;">threading</span>.Lock()
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> counter, <span style="color: #dc143c;">mutex</span>
        <span style="color: #dc143c;">time</span>.sleep(<span style="color: #ff4500;">1</span>)<span style="color: #66cc66;">;</span>
        <span style="font-weight: bold; color: #ff7700;">if</span> <span style="color: #dc143c;">mutex</span>.acquire():
            counter += <span style="color: #ff4500;">1</span>
            <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s, set counter:%s"</span> <span style="color: #66cc66;">%</span> (<span style="color: #008000;">self</span>.name, counter)
            <span style="font-weight: bold; color: #ff7700;">if</span> <span style="color: #dc143c;">mutex</span>.acquire():
                counter += <span style="color: #ff4500;">1</span>
                <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s, set counter:%s"</span> <span style="color: #66cc66;">%</span> (<span style="color: #008000;">self</span>.name, counter)
                <span style="color: #dc143c;">mutex</span>.release()
            <span style="color: #dc143c;">mutex</span>.release()
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> i <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">200</span>):
        my_thread = MyThread()
        my_thread.start()

这种情况的代码运行情况如下:

I am Thread-1, set counter:1

之后就直接挂起了,这种情况形成了最简单的死锁。
那有没有一种情况可以在某一个线程使用互斥锁访问某一个竞争资源时,可以再次获取呢?在Python中为了支持在同一线程中多次请求同一资源,python提供了“可重入锁”:threading.RLock。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

代码只需将上述的:

1
<span style="color: #dc143c;">mutex</span> = <span style="color: #dc143c;">threading</span>.Lock()

替换成:

1
<span style="color: #dc143c;">mutex</span> = <span style="color: #dc143c;">threading</span>.RLock()
使用Condition实现复杂同步

目前我们已经会使用Lock去对公共资源进行互斥访问了,也探讨了同一线程可以使用RLock去重入锁,但是尽管如此我们只不过才处理了一些程序中简单的同步现象,我们甚至还不能很合理的去解决使用Lock锁带来的死锁问题。所以我们得学会使用更深层的解决同步问题。

Python提供的Condition对象提供了对复杂线程同步问题的支持。Condition被称为条件变量,除了提供与Lock类似的acquire和release方法外,还提供了wait和notify方法。

使用Condition的主要方式为:线程首先acquire一个条件变量,然后判断一些条件。如果条件不满足则wait;如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。不断的重复这一过程,从而解决复杂的同步问题。

下面我们通过很著名的“生产者-消费者”模型来来演示下,在Python中使用Condition实现复杂同步。

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
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">time</span>
 
condition = <span style="color: #dc143c;">threading</span>.Condition()
products = <span style="color: #ff4500;">0</span>
 
<span style="font-weight: bold; color: #ff7700;">class</span> Producer(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> condition, products
        <span style="font-weight: bold; color: #ff7700;">while</span> <span style="color: #008000;">True</span>:
            <span style="font-weight: bold; color: #ff7700;">if</span> condition.acquire():
                <span style="font-weight: bold; color: #ff7700;">if</span> products <span style="color: #66cc66;">&lt;</span> <span style="color: #ff4500;">10</span>:
                    products += <span style="color: #ff4500;">1</span><span style="color: #66cc66;">;</span>
                    <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"Producer(%s):deliver one, now products:%s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, products)
                    condition.notify()
                <span style="font-weight: bold; color: #ff7700;">else</span>:
                    <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"Producer(%s):already 10, stop deliver, now products:%s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, products)
                    condition.wait()<span style="color: #66cc66;">;</span>
                condition.release()
                <span style="color: #dc143c;">time</span>.sleep(<span style="color: #ff4500;">2</span>)
 
<span style="font-weight: bold; color: #ff7700;">class</span> Consumer(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">global</span> condition, products
        <span style="font-weight: bold; color: #ff7700;">while</span> <span style="color: #008000;">True</span>:
            <span style="font-weight: bold; color: #ff7700;">if</span> condition.acquire():
                <span style="font-weight: bold; color: #ff7700;">if</span> products <span style="color: #66cc66;">&gt;</span> <span style="color: #ff4500;">1</span>:
                    products -= <span style="color: #ff4500;">1</span>
                    <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"Consumer(%s):consume one, now products:%s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, products)
                    condition.notify()
                <span style="font-weight: bold; color: #ff7700;">else</span>:
                    <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"Consumer(%s):only 1, stop consume, products:%s"</span> <span style="color: #66cc66;">%</span>(<span style="color: #008000;">self</span>.name, products)
                    condition.wait()<span style="color: #66cc66;">;</span>
                condition.release()
                <span style="color: #dc143c;">time</span>.sleep(<span style="color: #ff4500;">2</span>)
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    <span style="font-weight: bold; color: #ff7700;">for</span> p <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">2</span>):
        p = Producer()
        p.start()
 
    <span style="font-weight: bold; color: #ff7700;">for</span> c <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">10</span>):
        c = Consumer()
        c.start()

代码中主要实现了生产者和消费者线程,双方将会围绕products来产生同步问题,首先是2个生成者生产products ,而接下来的10个消费者将会消耗products,代码运行如下:

Producer(Thread-1):deliver one, now products:1
Producer(Thread-2):deliver one, now products:2
Consumer(Thread-3):consume one, now products:1
Consumer(Thread-4):only 1, stop consume, products:1
Consumer(Thread-5):only 1, stop consume, products:1
Consumer(Thread-6):only 1, stop consume, products:1
Consumer(Thread-7):only 1, stop consume, products:1
Consumer(Thread-8):only 1, stop consume, products:1
Consumer(Thread-10):only 1, stop consume, products:1
Consumer(Thread-9):only 1, stop consume, products:1
Consumer(Thread-12):only 1, stop consume, products:1
Consumer(Thread-11):only 1, stop consume, products:1

另外:Condition对象的构造函数可以接受一个Lock/RLock对象作为参数,如果没有指定,则Condition对象会在内部自行创建一个RLock;除了notify方法外,Condition对象还提供了notifyAll方法,可以通知waiting池中的所有线程尝试acquire内部锁。由于上述机制,处于waiting状态的线程只能通过notify方法唤醒,所以notifyAll的作用在于防止有线程永远处于沉默状态。

使用Event实现线程间通信

使用threading.Event可以实现线程间相互通信,之前的Python:使用threading模块实现多线程编程七[使用Condition实现复杂同步]我们已经初步实现了线程间通信的基本功能,但是更为通用的一种做法是使用threading.Event对象。
使用threading.Event可以使一个线程等待其他线程的通知,我们把这个Event传递到线程对象中,Event默认内置了一个标志,初始值为False。一旦该线程通过wait()方法进入等待状态,直到另一个线程调用该Event的set()方法将内置标志设置为True时,该Event会通知所有等待状态的线程恢复运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">threading</span>
<span style="font-weight: bold; color: #ff7700;">import</span> <span style="color: #dc143c;">time</span>
 
<span style="font-weight: bold; color: #ff7700;">class</span> MyThread(<span style="color: #dc143c;">threading</span>.Thread):
    <span style="font-weight: bold; color: #ff7700;">def</span> <span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>, <span style="color: #dc143c;">signal</span>):
        <span style="color: #dc143c;">threading</span>.Thread.<span style="color: #0000cd;">__init__</span>(<span style="color: #008000;">self</span>)
        <span style="color: #008000;">self</span>.singal = <span style="color: #dc143c;">signal</span>
 
    <span style="font-weight: bold; color: #ff7700;">def</span> run(<span style="color: #008000;">self</span>):
        <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s,I will sleep ..."</span><span style="color: #66cc66;">%</span><span style="color: #008000;">self</span>.name
        <span style="color: #008000;">self</span>.singal.wait()
        <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"I am %s, I awake..."</span> <span style="color: #66cc66;">%</span><span style="color: #008000;">self</span>.name
 
<span style="font-weight: bold; color: #ff7700;">if</span> __name__ == <span style="color: #483d8b;">"__main__"</span>:
    singal = <span style="color: #dc143c;">threading</span>.Event()
    <span style="font-weight: bold; color: #ff7700;">for</span> t <span style="font-weight: bold; color: #ff7700;">in</span> <span style="color: #008000;">range</span>(<span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">3</span>):
        <span style="color: #dc143c;">thread</span> = MyThread(singal)
        <span style="color: #dc143c;">thread</span>.start()
 
    <span style="font-weight: bold; color: #ff7700;">print</span> <span style="color: #483d8b;">"main thread sleep 3 seconds... "</span>
    <span style="color: #dc143c;">time</span>.sleep(<span style="color: #ff4500;">3</span>)
 
    singal.<span style="color: #008000;">set</span>()

运行效果如下:

I am Thread-1,I will sleep ...
I am Thread-2,I will sleep ...
I am Thread-3,I will sleep ...
main thread sleep 3 seconds...
I am Thread-1, I awake...I am Thread-2, I awake...

I am Thread-3, I awake...

来自:http://www.ourunix.org/

http://xlambda.com/gevent-tutorial/

系统:CentOS 5.5 X86_64、RHEL 5.6 X86_64
硬件:Dell R610、R710、R720
通过升级系统自带网卡驱动程序到最新版本,可以解决当网卡流量过大时,出现掉包、断网现象。

升级步骤:
1.查看当前网卡类型

# /sbin/lspci |grep Ethernet

02:00.0 Ethernet controller: Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)
02:00.1 Ethernet controller: Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)
03:00.0 Ethernet controller: Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)
03:00.1 Ethernet controller: Broadcom Corporation NetXtreme II BCM5709 Gigabit Ethernet (rev 20)

 

2.查看网卡驱动模块

# cat /etc/modprobe.conf|grep eth
alias eth0 bnx2    #–>网卡模块为bnx2
alias eth1 bnx2
alias eth2 bnx2
alias eth3 bnx2

3.查看网卡驱动当前版本

# modinfo bnx2
filename:       /lib/modules/2.6.18-238.el5/kernel/drivers/net/bnx2.ko
version:        2.0.8-rh     #–>现有驱动的版本
license:        GPL
description:    Broadcom NetXtreme II BCM5706/5708/5709/5716 Driver #–>网卡型号
author:         Michael Chan <mchan@broadcom.com>
srcversion:     C7F6CA68A2BE2D1974E1D05
alias:          pci:v000014E4d0000163Csv*sd*bc*sc*i*
alias:          pci:v000014E4d0000163Bsv*sd*bc*sc*i*
alias:          pci:v000014E4d0000163Asv*sd*bc*sc*i*
alias:          pci:v000014E4d00001639sv*sd*bc*sc*i*
alias:          pci:v000014E4d000016ACsv*sd*bc*sc*i*
alias:          pci:v000014E4d000016AAsv*sd*bc*sc*i*
alias:          pci:v000014E4d000016AAsv0000103Csd00003102bc*sc*i*
alias:          pci:v000014E4d0000164Csv*sd*bc*sc*i*
alias:          pci:v000014E4d0000164Asv*sd*bc*sc*i*
alias:          pci:v000014E4d0000164Asv0000103Csd00003106bc*sc*i*
alias:          pci:v000014E4d0000164Asv0000103Csd00003101bc*sc*i*
depends:
vermagic:       2.6.18-238.el5 SMP mod_unload gcc-4.1
parm:           disable_msi:Disable Message Signaled Interrupt (MSI) (int)
parm:           enable_entropy:Allow bnx2 to populate the /dev/random entropy pool (int)
module_sig:     883f3504de5f22f43e909ab54b946c11240a909e2fda20a05b43ed30218e26f5efa0af241d6e29a009f45d50b4ccf2c77c0819974b1d3f8f51fca9be2

4.下载最新网卡驱动程序
到以下链接,根据查询到的网卡型号下载最新的驱动程序:

http://zh-cn.broadcom.com/support/ethernet_nic/downloaddrivers.php

5.驱动程序安装

unzip linux-7.2.20.zip
cd Server/Linux/Driver/
rpm -ivh netxtreme2-7.2.20-1.src.rpm
cd /usr/src/redhat/SPECS/
rpmbuild -bb netxtreme2.spec
cd ../RPMS/x86_64/
rpm -ivh netxtreme2-7.2.20-1.x86_64.rpm

6.加载最新驱动模块

#vi change.sh
rmmod bnx2
modprobe bnx2

执行脚本:

sh change.sh

7.查看最新驱动版本

#modinfo bnx2
filename:       /lib/modules/2.6.18-238.el5/updates/bnx2.ko
version:        2.2.1j   #–>新版驱动程序版本号
license:        GPL
description:    Broadcom NetXtreme II BCM5706/5708/5709/5716 Driver
author:         Michael Chan <mchan@broadcom.com>
srcversion:     92BB1824D1695C4AE860E5F
alias:          pci:v000014E4d0000163Csv*sd*bc*sc*i*
alias:          pci:v000014E4d0000163Bsv*sd*bc*sc*i*
alias:          pci:v000014E4d0000163Asv*sd*bc*sc*i*
alias:          pci:v000014E4d00001639sv*sd*bc*sc*i*
alias:          pci:v000014E4d000016ACsv*sd*bc*sc*i*
alias:          pci:v000014E4d000016AAsv*sd*bc*sc*i*
alias:          pci:v000014E4d000016AAsv0000103Csd00003102bc*sc*i*
alias:          pci:v000014E4d0000164Csv*sd*bc*sc*i*
alias:          pci:v000014E4d0000164Asv*sd*bc*sc*i*
alias:          pci:v000014E4d0000164Asv0000103Csd00003106bc*sc*i*
alias:          pci:v000014E4d0000164Asv0000103Csd00003101bc*sc*i*
depends:
vermagic:       2.6.18-238.el5 SMP mod_unload gcc-4.1
parm:           disable_msi:Disable Message Signaled Interrupt (MSI) (int)
parm:           stop_on_tx_timeout:For debugging purposes, prevent a chip  reset when a tx timeout occurs (int)

如果没有成功,重新启动下服务器;

8.卸载最新驱动,还原到以前的驱动
如果安装的最新驱动程序有问题,怎么卸载?

#rpm -e netxtreme2-7.2.20-1

来自:http://www.yaukb.com/2012/07/dell_network_drivers_update/

前言:

随着维护服务器的增多,每天一台台检查备份已经不切实际,即使通过email的方式(备份脚本执行完后将备份信息发送到指定邮箱)检查也要耗费不少的时间,每天做这种重复性且繁杂的事情对我们这类懒人来说简直痛不欲生。经过一次惨痛的教训之后,决定做个备份检查(监控)程序。

原理:

原理挺简单,本地服务器通过备份检查脚本检查备份文件是否存在,并将检查结果提交到sae的mysql数据库里,最后通过一个web页面将数据统一显示出来。

实例:

假设有5台服务器(web1~web5),备份项目有:msyql数据库、mongodb数据库和/data/img的rsync+inotify实时同步,具体的备份信息如下:

备份项目 本地备份保存路径及文件名 异地备份保存路径及文件名
rsync+inotify /data/img /bk/$hostname/img/*
mysql /data/bk/msyql/mysql_日期.tgz /bk/$hostname/mysql/mysql_日期.tgz
mongodb /data/bk/mongodb/mongodb_日期.tgz /bk/$hostname/mongodb/mongodb_日期.tgz

程序文件说明:

文件名 功能
getdata.php 用于获取客户端POST过来的数据并将数据提交到mysql数据库
bk.sh 服务器上的备份检查脚本
bk.php web页面

首先到创建数据库:

CREATE TABLE `bk_monitor` (
`id` smailint NOT NULL auto_increment,
`hostname` varchar(20) default NULL,
`rsync` tinyint default NULL,
`mysql`varchar(20) default NULL,
`mongodb` varchar(20) default NULL,
`date` timestamp,
PRIMARY KEY (`id`)
)

getdata.php 
用于获取客户端POST过来的数据并将数据提交到mysql数据库。
shell脚本中,只要使用curl -d xxx=xxx http://www.cszhi.com/getdata.php, 就可以将数据提交到服务器端的mysql数据库里。

< ?php
     $posts=$_POST;
     // 清除一些空白符号
     foreach ($posts as $key => $value)  
     {  
          $posts[$key] = trim(addslashes($value));  
     } 
 
     //一些变量初始化
     $db="bk_monitor";
     $type=$posts['type'];
 
     //0插入 & 1更新
     switch ($type)
     {
          case 0:
               $hostname=$posts['hostname'];
               $rsync=$posts['rsync'];
               $mysql=$posts['mysql'];
               $mongodb=$posts['mongodb'];
               $sql="insert into $db(hostname,rsync,mysql,mongodb) values('$hostname','$rsync','$mysql','$mongodb')";    
               break;
          case 1:
               $hostname=$posts['hostname'];
               $rsync=$posts['rsync'];
               $mysql=$posts['mysql'];
               $mongo=$posts['mongo'];
               $sql="update $db set rsync='$rsync',mysql='$mysql',mongodb='$mongodb' where hostname='$hostname'";
               break;
          default:
               exit('error!!!');
     }
 
     //操作数据库
     $conn = mysql_connect("localhost","root","aaabbb123","bk_monitor") or die("can't connect mysql:".mysql_error());
     mysql_query($sql,$conn) or die("query error:".mysql_error().":".$sql);
     mysql_close($conn);  
?>

bk.sh
服务器上的备份检查脚本。脚本有验证异备服务器上的文件是否存在,所以需要先在服务器上设置能ssh无密码登陆到异备服务器。

#!/bin/bash
#Program:
#check the backup file and submit to the sae database
#History:
#2013/12/31 caishzh
 
#变量初始化
HOSTNAME=$(hostname)
TODAY=$(date +%Y%m%d)
REMOTEIP="10.35.14.55" #这里是异备服务器的ip地址
TIMESTAMP=$(date +%s)
TIMESTAMPFILE="/data/img/tmp/$TIMESTAMP"
[ -f "/data/img/tmp" ] || mkdir /data/img/tmp
LOG="/var/log/bk.log"
URL="http://webshot.sinaapp.com/getdata.php"
[ "$1" == 1 ] &amp;&amp; TYPE=0 || TYPE=1
 
#函数:检查本地备份文件和异地备份文件是否存在
function isExistFile()
{
     [ -f $1/$2 ] || { echo 2;echo "$1/$2 doesn't exist..."&gt;&gt;$LOG;exit; }
 
     #判断远程文件是否存在
     if [ $(ssh -o ConnectTimeout=10 $REMOTEIP "test -f $3/$2 &amp;&amp; echo true || echo false") == "true" ];then
          size=$(ssh -o ConnectTimeout=10 $REMOTEIP "ls -lh $3/$2|cut -d' ' -f5")
          echo "0_$size"
     else
          { echo 1;echo "remote file ${REMOTEIP}:$3/$2 doesn't exist...">>$LOG;exit; }
     fi
}
 
echo  "=====$(date +'%F %T') start=====" &gt;&gt;$LOG
 
#mysql和mongodb备份文件检查
MYSQBKFILE="$(isExistFile /data/bk/mysql mysql_${TODAY}.tgz /bk/${HOSTNAME}/mysql)"
MONGODBBKFILE="$(isExistFile /data/bk/mongodb mongodb_${TODAY}.tgz /bk/${HOSTNAME}/mysql)"
 
#rsync实时同步检查
#在同步目录的tmp目录下,touch一个文件(文件名就是当前时间戳),sleep 5秒后,再判断远程目录是否存在这个文件,存在则rsync同步正常,不存在则异常
#sleep的时间可以根据自己服务器的情况调整
touch $TIMESTAMPFILE
sleep 5
if [ $(ssh -o ConnectTimeout=10 $REMOTEIP "test -f /bk/${HOSTNAME}/img/tmp/${TIMESTAMP} &amp;&amp; echo true || echo false") != "true" ];then
     RSYNCBAK=2
     echo "rsync error..." >>$LOG
fi
RSYNCBAK=${RSYNCBAK:=0}
 
#将数据提交至数据库
curl --connect-timeout 10 -d type=${TYPE} -d rsync="${RSYNCBAK}" -d mysql="${MYSQBKFILE}" -d mongodb="${MONGODBBKFILE}" -d hostname="${HOSTNAME}" $URL
 
echo -e "=====$(date +'%F %T') finish=====\n" >>$LOG

第一次执行时,脚本后面加个参数1,往数据库里新插入一条数据:

sh /root/tool/bk.sh 1

将bk.sh放到crontab,每天早上8点定时运行:

echo "0 8 * * * root sh /root/tool/bk.sh" >>/etc/crontab

bk.php web页面:

< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
     <head>
          <meta http-equiv="content-type" content="text/html;charset=GB2312"/>
        <title>bk monitor</title>
        <link rel="stylesheet" href="style.css">
    </link></head>
     <body>
            <h2>bk monitor</h2>
          <table border='1'>
               <tr>
                    <th>id</th>
                    <th>hostname</th>
                    <th>rsync</th>
                    <th>mysql</th>
                    <th>mongodb</th>
                    <th>date</th>
               </tr>
               < ?php
                    function status($bkname)
                    {
                         $str=explode("_",$bkname);
                         $status=(int)$str[0];
                         if ($status==0) {
                              if(empty($str[1]))
                                   echo "<td>OK";
                              else
                                   echo "<td>OK  $str[1]</td>";
                         } elseif ($status==1) {
                              echo "<td>WARN</td>";
                         } elseif($bkname==2) {
                              echo "<td>ERROR</td>";
                         } else {
                              echo "<td>UNKNOW</td>";
                         }
                    }
 
                    //直接用sae提供的SaeMysql类操作mysql数据库
                    $mysql = new SaeMysql();
                    $sql = "SELECT * FROM bk_monitor";
                    $data = $mysql->getData( $sql );
 
                    //getDate返回的是二维数组,用foreach遍历下
                    foreach($data as $value)
                    {
                         echo "<tr>";
                         echo "<td>".$value['id']."</td>";
                         echo "<td>".$value['hostname']."</td>";
                         status($value['rsync']);
                         status($value['mysql']);
                         status($value['mongodb']);
                         echo "<td>".$value['date']."</td>";
                         echo "</tr>";
                    }
               ?>
          </table>
     </body>
</html>

界面如下:
bk

如果想要显示的界面更加直观漂亮点,可以使用bootstrap做点样式,效果如下:
bk2

 

 

在日常维护中,经常使用netstat -antp 查看服务器端口连接情况.有时发现好多TIME_WAIT,

1. 在TCP关闭连接的4次握手中,主动关闭的一方在发送最后一个ack后,发起关闭的一方就会进入TIME_WAIT状态,在这点要注意一点,不仅客户端能发起关闭,服务器端也能发起关闭请求.当服务器上出现TIME_WAIT的话,就说明是服务器端先发起了关闭连接的请求

存在的原因是:  如果当主动发起关闭请求的一方在发送完最后的ack后给被动关闭一方后,但不能保证这个ack能够被被动一方接收,所以如果在ack发送失败的情况下,被动一方将在重新发一次fin给主动一方,所以如果进入主动一方直接进入CLOSED状态的话,就不能接收这个重发的fin包了,导致连接不能正常关闭.就会有一个飘着的没有关闭的数据连接,而等待了2msl后,这些没有正常关闭的数据连接都会丢弃掉

TIME_WAIT的时间是2MSL,缺省为240秒(4分钟)

 查看端口状态命令:

netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

tcp状态解释:

LISTEN:监听端口并接收新连接,例如监听80端口

SYN-SENT:再发送连接请求后等待匹配的连接请求,例如客户端发送了一个syn-sent给服务器端,等待服务端响应

SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认,例如服务端收到了客户端的syn-sent数据包,同时自己发送了一个syn包给客户端,等待客户端的响应
ESTABLISHED:代表一个打开的连接,例如客户端已经和服务端连接成功且已经开始传输数据
FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认,例如发起主动关闭的一方在发送完fin数据包后,会进入FIN-WAIT-1状态

FIN- WAIT-2:从远程TCP等待连接中断请求
CLOSE-WAIT:等待从本地用户发来的连接中断请求

CLOSING:等待远程TCP对 连接中断的确认
LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认
TIME-WAIT:等待足够的时间以确保远程TCP接 收到连接中断请求的确认
CLOSED:没有任何连接状态

4cb2bff98c331b49252df283

 

squid中cache_peer和cache_peer_domain的处理规则和顺序:

环境介绍:   squid3监听在80端口,IP为192.1.1.126

           apache监听在8080端口,有2个虚拟主机: bbs.test.com和bbs2.test.com

          其中bbs.test.com虚拟主机下只有一个index.html,内容为bbs

                bbs2.test.com虚拟主机下只有一个index.html,内容为bbs2

         squid(前)+apache(后)

1. 如果只配置了cache_peer,没有配置cache_peer_domain,则所有的请求默认都会匹配第一个cache_peer,如果第一个匹配不成功,则匹配第二个,如下的配置,默认都会请求转发到bbs

cache_peer 127.0.0.1 parent 8080 0 no-query connect-timeout=150 max-conn=384 originserver name=bbs
cache_peer 127.0.0.1 parent 8089 0 no-query connect-timeout=150 max-conn=384 originserver name=bbs2
#cache_peer_domain bbs .test.com
#cache_peer_domain bbs2 bbs2.test.com

2.   如下的配置中,访问bbs2.test.com显示的是bbs2,因为.test.com是通配符

 cache_peer 127.0.0.1 parent 8080 0 no-query connect-timeout=150 max-conn=384 originserver name=bbs
cache_peer_domain bbs     .test.com # .test.com是通配符

3.  cache选择的顺序    #来自php-oa

选择新鲜cache目标的优先级是:
local cache
parent
sibling
direct

4.  故意将bbs2.test.com的父节点的端口填错,且把通配符给去掉,则访问bbs2.test.com返回错误页面,如果加上通配符,则能返回正常页面.说明当遇到错误页面后,squid还是会继续向下匹配的,如果匹配到最后一项,还没匹配成功,则返回错误页面

cache_peer 127.0.0.1 parent 8080 0 no-query connect-timeout=150 max-conn=384 originserver name=bbs
cache_peer 127.0.0.1 parent 8087 0 no-query connect-timeout=150 max-conn=384 originserver name=bbs2
cache_peer_domain bbs2 bbs2.test.com
cache_peer_domain bbs bbs.test.com

 5. 可以很多cache_peer_domain 指向同一个cache_peer,即多个域名访问同一个源,如下test.com和test1.com都指向了bbs

cache_peer_domain bbs .test.com
cache_peer_domain bbs .test1.com

6. 如果cache_peer 的最后指定了name=bbs2,则只接收cache_peer_domain里设置的bbs2的域名,如果没有指定的话,则归所有域名共用

cache_peer 127.0.0.1 parent 8089 0 no-query connect-timeout=150 max-conn=384 originserver name=bbs2
cache_peer 127.0.0.1 parent 8089 0 no-query connect-timeout=150 max-conn=384 originserver

cache_peer_domain bbs .test.com
cache_peer_domain bbs2 bbs2.test.com

 

7.  总结:

在这里可以将squid服务当做一个真实的客户端去访问apache,当apache返回什么,squid就返回什么给用户.

squid是按照自上往下依次匹配,匹配成功,则停止cache_peer_domain的匹配,如果匹配失败,继续匹配,知道最后也没匹配成功,则返回错误页面

 

环境:

使用了CDN+squid的环境,但由于CDN和squid的规则冲突,准备的设置方法为:

1.

ExpiresActive On
ExpiresByType text/html A14400
ExpiresByType application/x-javascript A86400
ExpiresByType text/css A86400
ExpiresByType image/jpeg A604800
ExpiresByType image/gif A604800
ExpiresByType image/png A604800

2.

<Location “/rss/”>
Header Set Cache-Control max-age=9000
</Location>

 

1.
HTTP缓存机制: 截止 和 验证。
截止机制是指设置一个过期时间,当在这个过期时间内时,就不在联系源服务器,而直接通过squid里的缓存来响应请求
验证机制是指当超过过期时间后,squid是发送一个验证文件是否被修改的请求,如果没修改,返回304,如果修改了,则返回完整的文件

2.
age: 是指响应缓存在squid中停留的时间 max-age:是指响应缓存在squid停留的最大时间数

3.
if-modified-since 是指客户端向源站发出请求,询问源站自从某个时间起,文件是否被修改了,起始时间是客户端上次得到的last-modified时间
last-modified 是指源站服务器给出的响应,文件的最新修改时间

4.
etag 是源站给文件打的一个标签,类似于文件的md5值,用来唯一的标示文件
if-none-match 是客户端询问源站文件是否经过修改,和if-modifietd-since的效果一样,只不过etag更可靠

5.
如果在squid的配置中,添加了override-expire参数,则refresh_pattern生效起作用
如果在squid的配置中,没有添加override-expire参数,则源站的过期时间起作用,即apache或者nginx的配置起作用

6.
squid中refresh_pattern 的. (dot)符号是默认规则,匹配任何以上的不匹配的项

7.
精确控制缓存时间的配置方法:
refresh_pattern -i \.html$ 2 100% 2 ignore-reload override-lastmod
解释: 忽略大小写,以html结尾的文件类型,精确控制缓存时间为2分钟,且忽略no-cache指令和覆盖lastmod指令

 

应该是在2010年的时候,虚拟机的磁盘大小是由虚拟机的模板决定,这个就比较痛苦,如果你希望提供多种磁盘格式,那么你就需要搞多个镜像。

还有一种做法,就是固定磁盘大小,操作系统是20G,你可以通过增加磁盘,不能调整硬盘大小。硬盘调整是有风险的,很有可能造成数据丢失。

不过当时在国外,linode和Rackspace,都已经实现磁盘的大小是根据创建的时候选择的硬盘大小。那么这个如何实现的呢。

可以猜测到,肯定是虚拟机启动后,运行的磁盘调整的工具,实现分区扩大,具体如何实现,我们就无从知道。

Openstack上,实现了这个功能,虚拟机的大小是由Flovor来决定,那么我就好好研究一下,这个分区调整是如何实现的。

对于linux的虚拟机镜像的分区,大家有不同的看法。目前我看到的反馈就是

  1. 不需要swap分区
  2. lvm分区没太多意义
  3. 就一个 根分区 / 就可以

要想实现分区扩大,其实需要做的工作是很多的,大家会发现,自己做的镜像,分区是无法调整的。而Ubuntu官方的镜像是可以调整的。而且如果你是多个分区,那么他的扩大的分区,其实是根分区。

所以镜像的制作:http://www.chenshake.com/about-openstack-centos-mirror/ 对于centos,我们就需要自己把linux rootfs resize装上才行。

目前Openstack的Dashboard提供磁盘扩大两个选择,一个是自动resize,一个是自己通过fdisk来设置分区。详细可以看Rackspace文档 http://www.rackspace.com/knowledge_center/article/using-automatic-and-manual-disk-partitioning-on-cloud-servers

让同事研究了一下resize的原理

1. gunzip -c /boot/initramfs-2.6.32-220.el6.x86_64.img |cpio -i –make-directories
这是一个虚拟的root文件系统。

2. 检查虚拟的root文件系统是否安装growpart,sfdisk ,e2fsck ,resize2fs ,sed ,awk ,partx ,
如果没有安装,将当前文件系统中的相应工具拷贝到虚拟的文件系统,涉及到库的拷贝

3.修改虚拟文件系统下的init文件:
sed -i -e ‘/^export PATH=.*/r /root/linux-rootfs-resize/distro/centos-6/mod’ -e //N init
# 读取/root/linux-rootfs-resize/distro/centos-6/mod文件的内容,插入到init文件中,插入的位置为^export PATH=.*行的上边
sed -i ‘/^source_all pre-mount/i growroot’ init
# 在文件init上插入’growroot’,插入的位置为^source_all pre-mount行的上边

4. find ./ | cpio -H newc -o > /tmp/initrd.cpio
gzip -c /tmp/initrd.cpio > initramfs-2.6.32-220.el6.x86_64.img-mod   #重新打包,从新命名,增加’-mod’

5.grubby -o /boot/grub/grub.conf –grub –copy-default –add-kernel=/boot/vmlinuz-2.6.32-220.el6.x86_64-mod ‘–title=CentOS release 6.2 (Final) 2.6.32-220.el6.x86_64 mod’ –initrd=/boot/initramfs-2.6.32-220.el6.x86_64.img-mod –make-default
# 修改grub.conf,使用新生成的initramfs-2.6.32-220.el6.x86_64.img-mod

6.镜像启动时,由grub引导到initrd,也就是我们新制作的initramfs-2.6.32-220.el6.x86_64.img-mod,执行用户空间的第一个进程init,此刻涉及到我们上面修改的init文件,上边我们插入了一个函数:

growroot()
{
root_part=$(echo ${root} |sed “s/block://”)
root_dev=$(readlink ${root_part}| sed “s/[^a-z]//g”)
part_num=$(readlink ${root_part}| sed “s/[^0-9]//g”)
growpart -v /dev/${root_dev} ${part_num}
if [ $? -eq 0 ]; then
partx -a /dev/${root_dev}
e2fsck -f /dev/${root_dev}${part_num}
resize2fs -p /dev/${root_dev}${part_num}
fi
}
调用这个函数,就会对root分区resize:  growpart -v /dev/vda 3
partx -a /dev/vda
e2fsck -f /dev/vda3
resize2fs -p /dev/vda3

 

windows自带的命令行工具netsh ipsec static add filter不支持批量添加,还会添加重复的规则进去。我用python编写了ipsecset解决了上述问题,支持批量添加,同一个列表里避免重复的规则。

为了方便使用,已编译成exe,源码和程序在下面的链接里

语法:

参数和netsh ipsec static add filter的参数是一样的,不区分大小写

必要参数:

srcaddr=(me/any/特定ip/网段)

dstaddr=(me/any/特定ip/网段)

dstport=(0/特定端口)

默认参数:

srcport=0

srcmask=255.255.255.255

dstmask=255.255.255.255

protocol=TCP

mirrored=YES

filterlist=”选用规则”

description=”add by script {time_now}”

批量操作:

“-”和”,”两种操作符,可混合使用

支持批量操作的参数:srcport,dstport,srcaddr,dstaddr

其中srcaddr和dstaddr仅最后一个段支持

列如,srcport=1000-1003,1007,1009

srcaddr=1.1.1.10-13,15

样例:

ipsecset srcport=1.1.1.1 dstport=2.2.2.2-30,31 dstport=8080 filterlist=”基础规则”

ipsecset srcport=me dstport=any dstport=81-85,87

ipsecset srcport=me dstport=10.1.1.0 dstmask=255.255.255.0  dstport=6161 protocol=udp

代码保存在github上 https://github.com/selboo/ipsecset

编译后的文件保存在dist文件夹

 

1.mysclog介绍
此脚本主要是用来扫描我们的一些应用的log,比如说oracle的alert log,mysql的error log,通过我们定义关键字,如果log里出现相应关键字,此脚本扫描到后,会记录下相应的信息,然后以邮件或者短信的信息进行通知(这个自己可以修改)。

采用perl脚本定制开发,可任意部署于任何一台可连接至集群的机器(最好不要用集群中的机器做监控机),可以管理多台机器。

2.mysclog的特性
1. 只需在管理机上安装模块
2.被监控机需要一个统一用户和统一目录
3.增加机器只需增加一行配置
4.详细的日志输出

3.mysclog所需条件
1.SSH公钥验证

mysclog管理节点通过ssh连接服务器,进行验证和一些管理操作。为了使这些过程自动化,推荐使用SSH公钥验证密码(也可以使用密码模式进行)。

2.操作系统

目前只测试过Linux系统

3.依赖模块

Net::OpenSSH
Log::Dispatch
Parallel::ForkManager

4.mysclog构建步骤
以如下结构作为说明:
192.168.0.1 mysclog Manager 节点
192.168.0.23 被监控节点
192.168.0.53 被监控节点
192.168.0.55 被监控节点

4.1 第一步:监控机安装模块
Parallel::ForkManager
IO-Tty-1.10.tar.gz
Test-Simple-0.98_05.tar.gz
Net-OpenSSH-0.61_10.tar.gz
yum install perl-Log-Dispatch

以上步骤一般都是:
perl Makefile.PL
make
make install

4.2 第二步:ssh互信(也可以选择直接password登录)
安装完成后,所有节点配置用户下 ssh 互信,注意这里不能禁止password登陆,否则会出现错误,另外按正常步骤下来如果有问题,请检查权限问题:

mkdir ~/.ssh
chmod 700 ~/.ssh
ssh-keygen -t rsa
ssh-keygen -t dsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

ssh db-64 cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh db-64 cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys
ssh db-24 cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh db-24 cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys

scp ~/.ssh/authorized_keys db-64:~/.ssh/authorized_keys
scp ~/.ssh/authorized_keys db-24:~/.ssh/authorized_keys

4.3 被监控节点操作
在192.168.0.23,192.168.0.53,192.168.0.55上建立用户如oracle等和一个工作目录如/home/oracle/mysclog。

必须保证该用户对所要扫描的log具有读取权限,同时对目录具有读写权限。
如果要扫描/var/log/messages的话,可以查看:

http://anonexp.blogspot.com/2013/04/acl-how-to-enable-read-permission-for.html

4.4 管理机部署脚本
新建工作目录如/home/oracle/mysclog(这个要求跟被监控节点一致)

注意:必须保证该用户对所要扫描的log具有读取权限,同时对目录具有读写权限。另外管理机和被监控节点需要的目录是相同的。

一共包括如下三个文件:
mysclog.cnf
mysclog.pl
sclog.pl

其中mysclog.cnf为配置文件
mysclog.pl为主控脚本
sclog.pl为扫描脚本

4.5 mysclog.cnf说明
mysclog.cnf的格式如下:
ip,logs,keys,skip_keys,server,from,to,phonelist
一级分割符为”,”,二级分割符为”|”,参考如下:

192.168.0.23,/data/app/diag/rdbms/teststd01/test/trace/alert_test.log|/home/oracle/mysclog/tmpalert.log,ORA-|Corrupt|Deadlock|start|logon denied,ORA-3136,mail.aaaa.com,dba\@aaaa.com,dba\@aaaa.com|dba\@aaaa.com,13813800000|13813800000
192.168.0.53,/home/oracle/11.2.0/diag/rdbms/testadg03/test/trace/alert_test.log,ORA-|Corrupt|Deadlock|start|logon denied,ORA-3136,mail.aaaa.com,dba\@aaaa.com,dba\@aaaa.com|dba\@aaaa.com,13813800000|13813800000
192.168.0.55,/home/oracle/11.2.0/diag/rdbms/testadg04/test/trace/alert_test.log,ORA-|Corrupt|Deadlock|start|logon denied,ORA-3136,mail.aaaa.com,dba\@aaaa.com,dba\@aaaa.com|dba\@aaaa.com,13813800000|13813800000

拿第一条说明下:

192.168.0.23,/data/app/diag/rdbms/teststd01/test/trace/alert_test.log|/home/oracle/mysclog/tmpalert.log,ORA-|Corrupt|Deadlock|start|logon denied,ORA-3136,mail.aaaa.com,dba\@aaaa.com,dba\@aaaa.com|dba\@aaaa.com,13813800000|13813800000

ip: 192.168.0.23
logs(有两个log要扫描,以|分割了): /data/app/diag/rdbms/teststd01/test/trace/alert_test.log|/home/oracle/mysclog/tmpalert.log
keys(有多个key要扫描,以|分割了): ORA-|Corrupt|Deadlock|start|logon denied
skip_keys:ORA-3136 (可以有多个,以|分割)
from:dba\@aaaa.com
to:dba\@aaaa.com|dba\@aaaa.com (可以有多个,以|分割)
phonelist:13813800000|13813800000 (可以有多个,以|分割)

4.6监控
可以以crontab的形式,或者后台形式(需要稍微修改代码)运行

调试模式:
perl mysclog.pl -u oracle -p dddddddddd -c 2 -f /home/oracle/mysclog/mysclog.cnf -up 1 -l debug
其中
-c是并发度;
-up 1表示把脚本sclog.pl上传或者更新到被监控节点上,一般用默认值就行了
-l 表示日志输出等级,默认就行,调式时可以用debug

一般运行模式:
perl mysclog.pl -u oracle -p dddddddddd -c 2 -f /home/oracle/mysclog/mysclog.cnf

可通过如下命令获取帮助:

 [oracle@MHA-monitor mysclog]$ perl mysclog.pl

===============================================================================
Info  :
        Created By noodba (www.noodba.com) .
Usage :
Command line options :

   -h,--help           Print Help Info. 
   -i,--interval       Time(second) Interval(default 120). 
   -d,--workdir        workdir(default "/home/oracle/mysclog").
   -l,--level          log level(default "info").   
   -u,--user           user name.
   -p,--pswd           user password.
   -f,--conf           scan host config file.
   -c,--concurrency    Parallel process(default 5).
   -up,--updatepl      update scan pl(default 0). 
Sample :
   shell> perl mysclog.pl -u mcheck -p 123456 
===============================================================================

5.7 第七步:错误处理
下面这个错误时因为list of known hosts里面没有,可以先手工ssh一下:

Sat Oct 12 10:49:51 2013 - [warning] ssh to 192.168.2.24 err. error_count:1
Sat Oct 12 10:49:58 2013 - [warning] ssh to 192.168.2.24 err. error_count:2
Sat Oct 12 10:50:04 2013 - [warning] ssh to 192.168.2.24 err. error_count:3
Sat Oct 12 10:50:04 2013 - [error][/home/oracle/dgha/dgha_reverse.pl, ln65] ssh check error,exit.
Killed by signal 1.
Killed by signal 1.
[oracle@MHA-monitor dgha]$ ssh 192.168.2.24
The authenticity of host '192.168.2.24 (192.168.2.24)' can't be established.
DSA key fingerprint is 0d:dd:12:8b:ed:6a:e8:26:dc:a1:00:97:de:d1:bd:98.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.2.24' (DSA) to the list of known hosts.
oracle@192.168.2.24's password: 
Permission denied, please try again.
oracle@192.168.2.24's password:

5.参考资料1 http://search.cpan.org/~salva/Net-OpenSSH-0.60/lib/Net/OpenSSH.pm
2 MHA mha4mysql-manager-0.55\samples\scripts\power_manager.pl

图片、css、js等文件往往会占用掉一个网站大量的服务器带宽和页面载入时间,如果使用nginx做前端服务器可以设置类似的静态文件客户端的缓存时间。
例如:

location ~ \.(gif|jpg|jpeg|png|bmp|ico|swf|css|js)$ {
expires 15d;
access_log off;
}

将类似静态文件的客户端缓存时间设置为15天,这样客户在30天内重新访问这些文件时只需要在本地缓存中读取,而不用重新从服务器获取,大大提高了网站访问速度。
当然,对于这些静态文件的访问记录计入日志,在一般情况下也是没有意义的,将accss_log设为off,也能在一定程度上降低服务器压力。

对于一个运行中的进程,我们可以使用kill -STOP pid命令将其暂停执行,使用kill -CONT pid命令恢复其运行。

下面用一个实例说明:

1、首先使用tar命令打包/usr目录:

[root@vps /]# tar zcf usr.tar.gz usr/

开启一个新窗口查看其进程状态:

[root@vps ~]# ps aux|grep tar
root 18900 2.7 0.4 2760 1068 pts/1 R+ 09:23 0:00 tar zcf usr.tar.gz usr/

进程处于运行状态(R)

2、使用kill -STOP 命令将该进程暂停:

[root@vps ~]# kill -STOP 18900

再观察其状态:

[root@vps ~]# ps aux|grep tar
root 18900 1.4 0.4 2816 1120 pts/1 T 09:23 0:00 tar zcf usr.tar.gz usr/

此刻进程处于暂停状态了(T)

3、使用kill -CONT恢复进程执行

[root@vps ~]# kill -CONT 18900
[root@vps ~]# ps aux|grep tar
root     18900  1.5  0.4   2816  1128 pts/1    R    09:39   0:00 tar zcf usr.tar.gz usr/

进程恢复运行状态(R)

附:

ps的用法:

常用参数
-A 显示所有进程(等价于-e)(utility)
-a 显示一个终端的所有进程,除了会话引线
-N 忽略选择。
-d 显示所有进程,但省略所有的会话引线(utility)
-x 显示没有控制终端的进程,同时显示各个命令的具体路径。dx不可合用。(utility)
-p pid 进程使用cpu的时间
-u uid or username 选择有效的用户id或者是用户名
-g gid or groupname 显示组的所有进程。
U username 显示该用户下的所有进程,且显示各个命令的详细路径。如:ps U zhang;(utility)
-f 全部列出,通常和其他选项联用。如:ps -fa or ps -fx and so on.
-l 长格式(有F,wchan,C 等字段)
-j 操作格式
-o 用户自定义格式。
v 以虚拟存储器格式显示
s 以信号格式显示
-m 显示所有的线程
-H 显示进程的层次(和其它的命令合用,如:ps -Ha)(utility)
e 命令之后显示环境(如:ps -d e; ps -a e)(utility)
h 不显示第一行

au(x) 输出格式 :
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
USER: 行程拥有者
PID: pid
%CPU: 占用的 CPU 使用率
%MEM: 占用的记忆体使用率
VSZ: 占用的虚拟记忆体大小
RSS: 占用的记忆体大小
TTY: 终端的次要装置号码 (minor device number of tty)

STAT: 该行程的状态:
D: 不可中断的静止
R: 正在执行中
S: 静止状态
T: 暂停执行
Z: 不存在但暂时无法消除
W: 没有足够的记忆体分页可分配
<: 高优先序的行程
N: 低优先序的行程
L: 有记忆体分页分配并锁在记忆体内 (即时系统或捱A I/O)

START: 行程开始时间
TIME: 执行的时间
COMMAND:所执行的指令

进程STAT状态:

D 无法中断的休眠状态(通常 IO 的进程);
R 正在运行,在可中断队列中;
S 处于休眠状态,静止状态;
T 停止或被追踪,暂停执行;
W 进入内存交换(从内核2.6开始无效);
X 死掉的进程;
Z 僵尸进程不存在但暂时无法消除;
W: 没有足够的记忆体分页可分配
WCHAN 正在等待的进程资源;
<: 高优先级进程
N: 低优先序进程
L: 有记忆体分页分配并锁在记忆体内 (即时系统或捱A I/O),即,有些页被锁进内存

s 进程的领导者(在它之下有子进程);
l 多进程的(使用 CLONE_THREAD, 类似 NPTL pthreads);
+ 位于后台的进程组;

kill 终止进程有十几种控制进程的方法,下面是一些常用的方法:

kill -STOP [pid]
发送SIGSTOP (17,19,23)停止一个进程,而并不消灭这个进程。
kill -CONT [pid]
发送SIGCONT (19,18,25)重新开始一个停止的进程。
kill -KILL [pid]
发送SIGKILL (9)强迫进程立即停止,并且不实施清理操作。
kill -9 -1
终止你拥有的全部进程。
SIGKILL 和 SIGSTOP 信号不能被捕捉、封锁或者忽略,但是,其它的信号可以。所以这是你的终极武器。