• 2010-03-14

    最大文件描述符限制对高性能Web架构的影响

    Views: 22392 | No Comments

    不同于Windows平台的高效的线程机制, Linux/Unix系统早期通过多进程来解决Web大规模并发访问量的问题. 而随着Linux内核的改进, 单纯增加进程数量的方式也慢慢被弃用, 转而使用单进程异步IO接口, 比如epoll(Linux), kqueue(BSD).

    单进程异步IO接口可以简单理解为, 进程把大量的socket连接交给内核管理, 一旦连接中有数据需要处理, 则激活进程进行处理. 这样, 一个进程同时打开的socket(也即文件描述符)就有成千上万个.

    Linux系统默认的单个进程所能打开的最大文件描述符一般是1024(可通过ulimit -n命令查询), 这就导致了文件描述符被用完的情况出现. 所以, 要开发能处理高并发请求的Web系统, 必须修改操作系统的最大文件描述符.

    即使使用的是Apache这样的多进程Web服务器, 也有可能被最大文件描述符限制给绊倒, 因为后端可能使用的是fastcgi, memcached这些应用. 比如memcached, 一般使用的就是单进程异步IO接口, fastcgi的PHP脚本与其连接时, 将保持TCP长连接, 也是会消耗大量的文件描述符.

    所以, 运行在Linux之上的高性能Web系统, 一定要扩大进程的最大文件描述符限制, 这是有很惨痛经验教训的.

    Posted by ideawu at 2010-03-14 10:59:04
  • 2009-12-29

    Linux下编译安装Apache/Nginx/Lighttpd+PHP+MySQL

    Views: 29305 | 1 Comment

    使用Ubuntu Linux, 编译过程提示缺啥补啥即可.

    Apache:

    ./configure --prefix=/home/work/httpd --enable-so --enable-rewrite --enable-vhost-alias
    

    配置文件:

    LoadModule php5_module        modules/libphp5.so
    AddType application/x-httpd-php .php
    # PhpIniDir /home/work/php/php.ini
    

    MYSQL:

    Continue reading »

    Posted by ideawu at 2009-12-29 11:32:25 Tags: ,
  • 2009-10-28

    如何为Linux生成和打上patch

    Views: 11352 | No Comments

    通过diff工具生成补丁, patch工具打上补丁.

    在使用diff之前, 你需要保留一份未修改过的源码, 然后在其它地方修改源码的一份拷贝. diff对比这两份源码生成patch. 修改过的源码必须保留原来的文件名, 例如, 如果你修改源码中的a.c文件, 那么, 修改后的文件还是名为a.c, 在修改之前你可以复制a.c为a.orig.c进行备份.

    diff命令必须在整个Linux源码的根目录的上一级目录中执行.

    1. 为单个文件生成补丁

    diff -up linux-2.6.28.8/net/sunrpc/svc.orig.c linux-2.6.28.8/net/sunrpc/svc.c

    这条命令会产生类似如下的输出, 你将它重定向到一个文件中, 这个文件就是patch.

    --- linux-2.6.28.8/net/sunrpc/svc.orig.c 2009-03-17 08:50:04.000000000 +0800
    +++ linux-2.6.28.8/net/sunrpc/svc.c 2009-03-30 19:18:41.859375000 +0800
    @@ -1050,11 +1050,11 @@ svc_process(struct svc_rqst *rqstp)

    参数详解:
    -u 显示有差异行的前后几行(上下文), 默认是前后各3行, 这样, patch中带有更多的信息.
    -p 显示代码所在的c函数的信息.

    Continue reading »

    Posted by ideawu at 2009-10-28 19:01:37
  • 2009-08-21

    遭遇SIGPIPE

    Views: 28686 | 2 Comments

    我写了一个服务器程序, 在Windows下在cygwin环境编译后执行, 然后用C#写了多线程客户端进行压力测试. 程序一直运行正常. 但当在Linux下测试时, 总是莫名退出. 最后跟踪到是write调用导致退出. 用gdb执行程序, 退出时提示"Broken pipe".

    最后问题确定为, 对一个对端已经关闭的socket调用两次write, 第二次*可能*会生成SIGPIPE信号, 该信号默认结束进程.

    具体的分析可以结合TCP的"四次握手"关闭. TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道, 但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制, 一个端点无法获知对端已经完全关闭.


    截图来自: UNPv1

    对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.

    为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:

    signal(SIGPIPE, SIG_IGN);

    这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭.

    PS: Linux下的SIGALRM似乎会每1秒钟往后偏移1毫秒, 但Windows下经过测试完全准时, 不差1毫秒.

    Posted by ideawu at 2009-08-21 00:36:57 Tags:
  • 2009-07-16

    Linux+Keepalived双机互备

    Views: 28983 | 3 Comments

    大规模Web应用有两个要解决的问题: 高可用性和负载均衡. Keepalived可以提供IP层的高可用性, 一旦某一台机器的网络出现问题, 另一台服务器会立即(几秒或者更少的时间)使用出故障的服务器的IP进行工作. 具体的工作原理会在我学习之后, 发布在后期的文章中.

    现在有两台虚拟机ServerA和ServerB. 两个对外提供Web服务器的虚IP(VIP)192.168.200.100和192.168.200.200, 虚IP用在keepalived的配置中, 网卡接口配置有内网IP.

    ServerA:
    eth0: 192.168.200.128
    VIP: 192.168.200.100
    
    ServerB:
    eth0: 192.168.200.129
    VIP: 192.168.200.200
    

    如果两台服务器都正常地提供网络服务, 那么, 发往192.168.200.100的服务请求会被ServerA处理, 发往192.168.200.200的服务请求会被ServerB处理. 假设只有ServerB出现故障, 那么, 所有的请求都由ServerA进行处理. 当只有ServerA出现故障时, 也是同理.

    安装Ubuntu Linux Server

    本文使用的Linux为Ubuntu 9.04 服务器版, 请到http://www.ubuntu.com.cn/getubuntu/download-server/下载. 需要安装gcc, make开发环境.

    Continue reading »

    Posted by ideawu at 2009-07-16 23:09:45
  • 2007-03-25

    编译C语言代码

    Views: 16030 | 2 Comments

    一般我们会在某个C语言源码文件(如a.c)中使用"include"指令包含其它的文件(如b.h或者b.c).

    这样设想:
    // file: a.c
    include "b.h"
    //可能有include "b.c"
    //main()

    // file: b.h
    // 函数声明等

    // file: b.c
    include "b.h"
    // 函数定义
    我们可以先把b.c编译为模块b.o
    gcc -c b.c

    然后编译a.c
    gcc a.c b.o

    或者我们在a.c文件中直接包含b.c那么就可以这样编译:
    gcc a.c

    Posted by ideawu at 2007-03-25 22:12:31
|<<<34567891011>>>| 7/15 Pages, 86 Results.