笔记

编程语言

C语言

自实现锁

使用gcc 提供的 __sync_bool_compare_and_swap, 可以实现自己的锁,该函数是一个 原子操作:

// 如果 \*lock 的值等于 old, 那么就把set 写入*lock, 否则不写
__sync_bool_compare_and_swap(lock, old, set)

用法示例: example

内存池

nginx 的内存池实现

用法示例: example

相关链接: http://www.alidata.org/archives/1390

相关链接: http://www.cnblogs.com/jzhlin/archive/2012/06/06/2537710.html

哈希表

glib 库(gnome基本库) 提供了一套 哈希表

示例: example

相关链接: https://developer.gnome.org/glib/2.30/glib-Hash-Tables.html#g-hash-table-new

终端下UI库

curses 库

示例: example

正则表达式

PCRE: 很多开源项目都用它来支持正则,如: nginx, apache

示例: example

相关链接: http://www.pcre.org/pcre.txt

内存覆盖bug

strcpy函数并不会检测参数范围,如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
include <stdio.h>
int main ()
{
    int flag;
    char buf[10];

    strcpy (buf, argv[1]);

    if (strcmp (“abc123”, buf) == 0) {
        flag = 1;
    } else
        flag = 0;

    if (flag) {
        printf (“successful”);
    }
}

这是一个验证密码的程序, 有办法让你不知道密码,也能登陆。只要输入的字符串足够长就行, 原理: strcpy 不会考虑第一个参数 的内存大小,如果 argv[1]足够大 导致buf已经复制不下,那么会 溢出复制。就是往内存中后续的 地址复制。 内存中存储可能如下:

--------------
| --内存中---|
|____________|
| buf 变量   |
|____________|
| flag 变量  |
--------------

当输入足够长, 利用strcpy函数不保证边界检测的特性, 后续的字符会覆盖下面的变量flag

牛逼宏定义

大小写转换:

#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)

java

FAQ

Ivy is not avaliable

Ivy是ant的一个插件, 报这个错说明Ivy插件没有安装,或者没有配置好. 解决方法:

  1. 从官网下载Ivy tgz包并下载编译成jar包, 然后把Ivy.jar 拷贝到/usr/share/ant/lib/ 下
  2. 配置环境变量 export ANT_HOME=/usr/share/ant/

python

元编程

在python 中,普通Object 是类的实例, 类是元类的实例. 如下:

class A:
    pass

a = A()

定义了一个类A, 定义了一个实例a, 其中 a是类A的一个实例, 但其实类A也是实例( 类也是实例 ), 它是谁的实例呢,是元类的实例(默认所有类的元类是type)。 当解析器解析到 class A 时,就在内存中创建了一个对象 A, 只不过这个对象是 类对象, 它可以创造实例

定义一个元类

默认所有类的元类是 type, 要定义一个元类,必须继承自type, 并重写__new__方法, __new__方法在解析到classs 字段的时候会调用,也就是创建一个类的时候,会调用__new__:

class Metaclass(type):
    def __new__(meta, name, bases, dct):
        """
        它有四个参数:
        meta: 元类,这里是 MetaClass
        name: <要使用此元类的类>的类名
        bases: <要使用此元类的类>的基类
        dct: <要使用此元类的类>的属性, 包括函数,类变量, 如: __init__函数
        """

        dct['add_attr'] = 'haha'

        # 调用type(name, bases, dct) 来创建类
        return super(Base, meta).__new__(meta, name, bases, dct)
使用元类

这样就定义了一个元类, 要使用这个元类, 在普通类中增加 __metaclass__变量, 指向元类:

class A:
    __metaclass__ = Metaclass
    pass

a = A()
print a.add_attr
注意

元类可以被继承, 如果类A的__metaclass__为MetaClass, 类B继承自类A, 则创建类B的时候, 也会用MetaClass类

相关链接:

http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python

magic 方法

__get__

定义了__get__ 方法的类(object子类)的实例,作为另一个类的类变量时,方位这个变量时会调用__get__方法:

class attrCls(object):

    default = list

    def __get__(self, instance, owner):
        """
        这个方法称为Descriptor
        @instance: 调用这个属性的实例, 如果是通过类名调的, 这个值为None
        @owner: 调用这个属性的类
        """
        return self.default()

class Cls:
    objects = attrCls()

print type(Cls.objects)  ==> list
Cls.objects.reverse()

并且__get__方法会被继承, 如何attrCls的子类,也会有同样的效果

自实现python版的http框架

示例: example

常见问题

如何生成跨平台不乱码的csv

csv很容易在多个平台之间乱码,使用如下代码可以减少乱码情况:

fp = codecs.open(dump_file, 'w', encoding='utf16')
title = u'性名\t平台\tid\t数量\n'
fp.write(title)

for items in result:
    try:
        line = '%s\t%s\t%s\t%s\n' % (items[0], items[1], item[2], item[3])
        fp.write(line)
    except Exception as E:
        pass
fp.close()
unicode转码

普通字符串怎么变成unicode:

> s = u'\u4f60\u597d'
> print s
你好
> s = '\u4f60\u597d'
> print s
'\u4f60\u597d'
> print s.encode('utf-8')
'\u4f60\u597d'
> print s.decode('utf-8')
'\u4f60\u597d'
> print s.decode('unicode-escape')
你好

计算机网络

arp协议

arp 命令

查看 arp缓存表:

$> arp -a -i eth0

清空arp缓存:

$> sudo arp -ad -i eth0

arp 攻击

由于arp 协议很简单, 没有验证,所以hacker 可以很容易发送假的arp reply 给局域网 其他机器, 但前提条件是 attack 机器必须和 目标机器在同一局域网. 对于网络管理员, 控制arp攻击的方法有以下几种.

在交换机层做静态arp表

这样做的坏处是, 增加新设备需要手动增加arp表

打开交换机的Port Security功能

打开这个开关后, 交换机不允许一个物理连接的机器,更换MAC地址。如果hacker机器刚开始在 局域网内, 绑定的MAC 是A, 那么当它想攻击其他机器时,它的ARP报文里, MAC需要变成其他 机器,这时交换机会拒绝服务,因为不允许这个端口的机器 MAC 改变

hack

Dos 攻击

Dos 攻击的种类很多,但原理都是发送大量的请求,使server不能服务正常的请求. 知名的攻击类别有:

  • SYN Flood攻击
  • DDos 分布式Dos攻击
  • Pingofdeath 死亡之Ping

如何攻击

Http-Alive

善用Http服务的Keep-Alive来进行攻击,如果发送大量请求需要创建大量同Http服务的连接, 那么连接数太大时,容易被服务器上游的防火墙防御, 所以最要用已建立的连接,发送大量请求

HEAD请求

如果靠发送大量的GET, POST请求来进行攻击,服务端对每个GET/POST请求都会response, 那么服务器的response可能在还没有攻击完成前,就把hacker机器的带宽沾满了,从而无法继续进行攻击. 而HEAD请求不同, 它只要求server返回HTTP头部, 详情见 HTTP HEAD协议

Choose your target wisely

Dos攻击的目的是消耗server的CPU和RAM, 所以挑选请求的时候,要挑选CPU密集型的请求,如搜索(搜索词语注意随机性)

Keep dead工具

下载链接: http://www.esrun.co.uk/blog/wp-content/uploads/2011/03/Keep-Dead.zip

防范

server上游安装防火墙, 过滤Dos攻击

SYN Flood攻击

设置net.ipv4.tcp_syncookies = 1,就是给每一个请求连接的IP地址分配一个Cookie,如果短时间内连续受到某个IP的重复SYN报文,就认定是受到了攻击,以后从这个IP地址来的包会被一概丢弃。

csrf 攻击

也叫跨站请求伪造攻击, 它的原理如下:

  1. 被攻击银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000 (GET请求来转账,小bug)

  2. 危险网站B,它里面有一段HTML的代码如下:

    <img src=http://www.mybank.com/Transfer.php?toBankId=11&money=1000>
    
  3. 首先,你登录了银行网站A,然后访问危险网站B. 噢,这时你会发现你的银行账户少了1000块...... 因为网站B在你不知情的情况下 用你的身份(cookie)伪造了一个请求

http

GET

以下是用chrome 浏览器请求 django server的抓包结果

_images/network_http_get.png

总结:

  1. GET请求的参数, 是放在请求行里的
  2. 请求中得换行是 0d0a, 空格是 20
  3. http请求是由服务端先close, 最后服务端维护2MSL

POST

POST发送数据时,分为 表单JSON 两种,以下是两种的区别

_images/network_http_post.png
表单 vs JSON

到底是使用 表单 还是 json 发送数据呢,其实区别不大。如果你要传输的数据有很强的格式,如:

a = b
c = [1,2,3]
d = {a:b
     c:d}

那么最好用 json 方式来传输数据;如果你要传输的数据很简单,就用 表单 格式吧, 相关连接

上传文件

用post上传文件时, 文件数据是放在请求体内的,并不是放在首部。但是首部会指明文件数据从请求体哪个字节开始, 下面就是 一个客户端上传文件的请求:

_images/network_http_post_upload.png

Warning

上传文件的时候,必须用multipart格式(非www-form-data), 并且其他数据必须用表单,不能用JSON,参见: http://www.faqs.org/rfcs/rfc2388.html

其中, Content-Type: multipart/form-data; boundary=---------------------------7db15a14291cce 说明 了这个请求是“multipart/form-data”格式的,且“boundary”是 “—————————7db15a14291cce”这个字符串。

不难想象,“boundary”是用来隔开表单中不同部分数据的。例子中的表单就有 2 部分数据, 用“boundary”隔开。“boundary”一般由系统随机产生,但也可以简单的用“————-”来代替。 紧接着 boundary 的是该部分数据的描述。

接下来才是数据。

pipeline

Http1.1 引入了两个提高性能的特性: keepalivepipeline.

pipeline 就是可以在一个connection上,并行的发送多个http请求,后一个请求不用等前一个请求处理完毕. 但是这个特性很遗憾, 没有被大部分程序使用

FAQ

python requests 库的timeout作用:

requests 发http请求的时候,timeout参数并不是整个请求的超时,而是目标服务器没有响应的时间。例如:

requests.get('http://www.google.com', timeout=2)

如果没有开vpn, 这个请求也不会2秒后报错, 因为大部分时间block 在DNS查询, 这步查询不受requests 的timeout影响。

URL 编码

url 编码只能使用utf-8, 所以在url中出现汉字,浏览器会自动把汉字转换成utf-8表示(每个字节前加%), 如:

http://www.baidu.com?wq=春节

浏览器处理过后,其实发送的是:

http://www.baidu.com?wq=%E6%98%A5%E8%8A%82

http basic auth 这是http协议的一个feature, 它把账号和密码放在header里(未加密),向server发出请求, 如果验证失败,就返回401

socket TCP 编程

TCP 协议

_images/network_socket_tcp_dump.png

这是chrome 浏览器的抓包. 可以看到, chrome 发送http请求时, TCP会默认打开几个TCP 协议选项:

建立连接

三次握手

_images/network_socket_tcp_connect.png

listen 函数的backlog

_images/network_socket_tcp_listen.png

服务端exampe:

int
main(int argc, char **argv)
{
    int listenfd, connfd;
    char    buf[MAXLINE];
    ssize_t n;
    pid_t   childpid;
    socklen_t   clilen;
    struct  sockaddr_in cliaddr, servaddr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);                 // 1. socket
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    bind(listenfd, (SA *) &servaddr, sizeof(servaddr));         // 2. bind
    listen(listenfd, LISTENQ);                                  // 3. listen

    while(1) {
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd, (SA *) &cliaddr, &clilen);    // 4. loop accept

        while(1) {
            if((n = read(connfd, buf, MAXLINE)) == 0)
            break;
            n = write(connfd,buf,n);
        }
        close(connfd);
    }
}

客户端example:

int
main(int argc, char **argv)
{
    char    buf[BUFFSIZE];
    int     sockfd;
    int     tffd;
    char    time[64];
    ssize_t n;
    struct  sockaddr_in servaddr;

    if(argc != 2){
        printf("usage: cmd <IPaddress>/n");
        exit(0);
    }

    sockfd = socket(AF_INET, SOCK_STREAM, 0);               // 1. socket
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    connect(sockfd, (SA *) &servaddr, sizeof(servaddr));    // 2. connect

    while(1)
        {
        if((n = read(STDIN_FILENO,buf,BUFFSIZE)) == 0)
            break;
        n = write(sockfd, buf, n);
        n = read(sockfd, buf, BUFFSIZE);
        n = write(STDOUT_FILENO,buf,n);
    }
    exit(0);
}

其他

Connection reset by peer

当连接A kill掉,或者强行关掉连接后, 连接B 还向A write, write 不会返回错(因为这相当于A半关闭,B可以写), 但write会使连接A 发送一个RST到B, 如果RST先到B, 这时连接B还调用了read(也有可能RST后到, 连接B先read, 这时read 正常返回0, 因为对端close了), 就会导致 Connection reset by peer. 这种情况可能是:

  1. 客户端关掉页面, 服务器还不知道
  2. 服务器的并发连接数超过了其承载量,服务器会将其中一些连接关闭 (见net.ipv4.tcp_abort_on_overflow)

net.ipv4.tcp_abort_on_overflow 这个内核参数值决定了系统调用listen中backlog参数的作用。默认这个值为0,所以当 backlog队列已满时,新来的SYN请求, server不予理会,那么client会重发SYN, 那时backlog队列也许已经恢复了。 如果这个值设为1, 那么当backlog满的时候 新来的SYN, 服务器会直接返回RST, 导致”Connection reset by peer”

技术

django

中间件

django 自带了一下中间件:

django.contrib.sessions.middleware.SessionMiddleware

django.middleware.common.CommonMiddleware:

自动补全/
UA forbidden
etags

django.middleware.gzip.GZipMiddleware:

使用gzip对response.content进行压缩

django.contrib.auth.middleware.AuthenticationMiddleware:

提供了app(auth), 其中包括url (login, logout, password_change)

session

配置

使用django session 需要在middle中配置: django.contrib.sessions.middleware.SessionMiddleware

这个中间件的作用是:

在request之前,给request对象增加一个session属性,是sessionStore类型(这个类型可以在setting中用SESSION_ENGINE来配置)
在response之后,查看response的status, 如果不等于500, 就把session对象存储下来(看SESSION_ENGINE来决定用什么存)
配置 作用
SESSION_COOKIE_NAME sessionid 默认的session字段名
SESSION_CACHE_ALIAS default session 使用cache时,用哪个cache配置
SESSION_COOKIE_AGE  
SESSION_COOKIE_DOMAIN None http协议里session 的domain字段。 允许跨域session
SESSION_COOKIE_HTTPONLY  
SESSION_COOKIE_PATH  
SESSION_COOKIE_SECURE  
SESSION_ENGINE django.contrib.sessions.backends.db django自带以下engine: django.contrib.sessions.backends.db: 用mysql实现的session django.contrib.sessions.backends.file: 用file django.contrib.sessions.backends.cache: 用mem, 不持久 django.contrib.sessions.backends.cached_db: 用mem + mysql
SESSION_EXPIRE_AT_BROWSER_CLOSE  
SESSION_FILE_PATH file session 使用保存session的路径
SESSION_SAVE_EVERY_REQUEST False 是否每个请求都store一遍session, 默认只有session改变的时候才store
SESSION_SERIALIZER  
cache

默认django自带的session cached engine, 都是使用django.core.cache的功能来实现cache

清理

session 会每次生成一条记录, 用 clearsessions 可以清理过期的session. 可以把这条命令加在crontab里

测试

测试类

  • django.utils.unittest.TestCase 基本测试类

  • django.test.TransactionTestCase 事务测试类:

    每个test_函数执行过后,都会恢复数据库到初始状态。
    
  • django.test.LiveServerTestCase 活服务测试类:

    每次测试的时候,会在后台模拟启动一个服务端(python manager.py runserver), 这就可以用 Selenium 来自动化测试
    

优化

  • 如果不使用django的session, 那么在settings里把session的中间件注掉,减少开销
  • 可以使用其他模板引擎, 如jinja2, 但要注意, django的contrib里的app, 都是用DTL实现的

其他

信号

django 的信号,其实就是定义一个全局类, 这个类的作用就是观察者, 可以注册 一堆callback, 最后通过send()方法来调用每个signal 上的callback. 它的作用就是解藕, 无其他作用

_images/django-signal.png

第一步, 注册 signal, 这步建议把代码写在每个app下的signal.py 文件中:

import django.dispatch
delete_done = django.dispatch.Signal(providing_args=['obj'])

第二步, 关联对应的listener, 建议把这行代码写在 models.py 中:

class Article:
    pass

def callback(sender, **kwargs):
    pass

signals.delete_done.connect(callback, sender=Article)

第三步, 触发信号:

signals.delete_done.send(sender=Article, obj=self)
$ pip install -U mongoengine
sitemap

django 有sitemap模块,可以让你用类的方式,自动生成sitemap.xml, 供search engine 使用

问题

有些django版本,创建了新app并写了models后,无法migrate来创建数据库,提示已migrated. 这种情况下, 在新app目录下,删除掉 migrations目录即可

flask

flask 是一个web框架,相当于 flask = Werkzeug + jinja2 + threadlocal

模板

flask 的模板默认是使用jinja2, 你也可以使用其他模板渲染

注册Filters

修改 app.jinja_env 就可以

Context Processors

你可以为模板渲染注入新的变量, 通过Context Processors, 如下, 就是为模板增加了一个user变量:

@app.context_processor
def inject_user():
    return dict(user=g.user)

github

FAQ

  1. github Key is already in use

    出现这个的原因是你在以前也用过GitHub, 并且提交了你的密钥. 这个时候你可以通过在命令行里输入:

    ssh -T -i ~/.ssh/id_rsa git@github.com
    

    来查看到底是哪一个账户在使用此密钥,会出现如下提示:

    Hi <XXX>! You've successfully authenticated, but GitHub does not provide shell access.
    

    就是这个XXX账号, 占用了当前sshkey, 登陆这个账号,删除掉sshkey就行了

goaccess

goaccess 是一款用C语言实现的log解析工具, 用它可以分析web服务日志

第一步: 配置文件

书写 ~/.goaccessrc 文件,在其中配置日志的格式:

color_scheme 0
date_format %a %b %d %H:%M:%S %Y
log_format {%^} {%^} [%^] %h %^[%d] %^ %r %^(%^ %s) %^

第二步: 执行

使用命令:

$> goaccess -f 日志路径 > result.html

hadoop

  1. start hadoop

    sh bin/start-all.sh

  2. exec wordcount

    1. put input into hdfs

      ./bin/hadoop dfs -put input/a input

    2. exec

      ./bin/hadoop jar example.jar wordcount input output

    3. get output from hdfs

      ./bin/hadoop dfs -get output/part-r-00000 .

Web 管理窗口

  • HDFS 管理窗口 localhost:50070
  • JobTracker 管理窗口 localhost:50030
HDFS 接口

实例代码:

import org.apache.hadoop.fs.*;
import org.apache.hadoop.conf.*;
class Person
{
    public String talk() {
    return "Person —— >> talk()" ;
    }
}

public class a
{
    public static void main(String[] args) throws Exception
    {
        Configuration conf = new Configuration ();
        FileSystem hdfs = FileSystem.get (conf);
        FileSystem local = FileSystem.getLocal (conf);

        Path inputDir = new Path (args[0]);
        Path hdfsFile = new Path (args[1]);

        FileStatus[] inputFiles = local.listStatus (inputDir);
        FSDataOutputStream out = hdfs.create (hdfsFile);

        for (int i = 0; i < inputFiles.length; i++) {
            FSDataInputStream in = local.open (inputFiles[i].getPath ());
            byte buffer[] = new byte [256];
            int bytesRead = 0;
            while ((bytesRead = in.read (buffer)) > 0) {
                out.write (buffer, 0, bytesRead);
            }
            in.close ();
            System.out.println ("aaaa:" + inputFiles[i].getPath ());
        }
        out.close ();
        System.out.println(new Person().talk()) ;
    }
}
mapper
  • 一个类要作为mapper, 需继承MapReduceBase 基类, 并实现 Mapper 接口

    void configure (JobConfjob)

    void close ()

    void map (K1 key,

    V1 value, OutputCollector <K2, V2> output, Reporter reporter) throws IOException

partitioner
  • 一个定制的patitioner, 只需要实现 configure () 和 getPartition () 函数

    configure: 将hadoop对作业的配置应用到patitioner 上

    getPartition: 返回一个介于0和reduce任务数之间的整数,指向将要发送到的reducer

reduce
  • 需要继承 MapReduceBase 基类

    void reducer (K1 key,

    V1 value, OutputCollector <K2, V2> output, Reporter reporter) throws IOException

运行
  • 启动mapreduce

    > hadoop jar <jar包> <主类> <输入路径> <输出路径>

    如果只运行map, 可以跟参数 -D mapred.reduce.tasks=0

this is a test

部署新版本

  • 10.18.10.20 为线上服务器,负责为sdk端提供服务。
  • 10.18.10.96 为离线服务器,负责离线脚本及队列workers的运行。

umengplus的nginx 在 10.18.10.20 上,其配置在 /etc/nginx/sites-enabled/all 中:

class SimpleTest(TestCase):

    def test_add(self):
        self.assertEqual(1+1, 2)

    def test_bar(self):
        json_obj = self.get('/bar/get/%s/' % TCT.ak)
        self.assertEqual(json_obj['st'], 200)

list

这里列出了我收集的开发工具

监控类

工具 特点
munin 熟悉, 用perl写的
tsar 高效,用c写的. 淘宝开源的服务器监控工具, 可以随意增加插件. 地址: https://github.com/kongjian/tsar

文字类

工具 特点
apiary 类似markdown的语法, 生成漂亮的API文档

lucene

Baseful Use

_images/s_lucene_hello.jpg

maven

archetype

archetype 简单来说,就是maven预先定义好的一些java项目模板(有j2ee的helloworld, 有webapp的helloword 等), 也可以自己定义 自己的模板, 举个例子:

> mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

注意, archetype:generate 是生成项目的命令,用 -DarchetypeArtifactId 来选择模板,这里选择的是 maven-archetype-quickstart

quick-start

用maven创建一个java项目:

> mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

创建完会生成一个新项目目录,目录结构比较固定, 如下:

.
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── mycompany
    │               └── app
    │                   └── App.java
    └── test
        └── java
            └── com
                └── mycompany
                    └── app
                        └── AppTest.java

这个pom.xwl 是这个新项目的主要maven配置, maven会生成一个简单的hello world程序. 接着执行打包:

> mvn package

他会把项目打成jar包,放在最外面的target目录下, target/my-app-1.0-SNAPSHOT.jar, 接着执行这个jar包试试:

> java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
> Hello World!

mongo

性能调优

mongo 性能相关的因素:

  • RAM
  • locks time
  • page fault
  • connections number
使用覆盖索引
索引在内存中

最好确保 index 大小可以全部放入内存,否则连index都要 swap,就会比较慢, 使用:

$> db.collection.totalIndexSize ()

来查看单个表 index 大小

replica set

机制

master 把接收的请求,写入 oplog 表里。 oplog 表记录了所有的操作,也就是说,master 和slave 之间传递的是 oplog里的记录 oplog 是一个 capped 表,它的大小,可以配置的, 可以用 db.printReplicationInfo() 查看

步骤
  1. 有两天机器pc1, pc2, 保证两台机器的互通

  2. 分别在pc1, pc2 上启动mongod:

    pc1 > mongod --port 12345 --dbpath /srv/mongodb/rs0-0 --replSet rs0 --smallfiles --oplogSize 128
    pc2 > mongod --port 12346 --dbpath /srv/mongodb/rs0-0 --replSet rs0 --smallfiles --oplogSize 128
    
  3. 在primary 上配置rs0:

    pc1 > mongo --port 12345
    mongo> rsconf = {
                    _id: "rs0",
                    members: [
                                {
                                _id: 0,
                                host: "<hostname>:27017"        ===> 别用 localhost ,用对外ip
                                }
                        ]
                    }
    
    pc1 > rs.initiate (rsconf)
    
    查看rs状态
    
    pc1 > rs.status ()
    
  4. 添加别的机器(pc2) 到 rs0:

    pc1 > mongo --port 12345
    mongo > rs.add ("<pc2的ip>:port")
    
  5. 到此完成了,还可以添加 Arbiter 到 master 和 slave

  6. slave 不支持一般 mongo 操作,除非在slave上使用 rs.slaveOk () 才可以

TODO

query

snapshot ???

$text ???

$where ???

func max_time_ms ???

mongoengine

mongoengine 是纯python框架,实现了mongo的ORM, 整体结构如下

_images/mongoengine_design.png

为什么要使用 metaclass:

自定义文档(MyDocument) 继承自Document,  Document 很多函数都需要知道当前文档有哪些field,
所以需要metaclass, 在创建MyDocument类的时候, 把所有自定义field 存放在 cls._fields 中,
父类Document 中的方法访问cls._fields 就可以知道自定义了哪些field

配置从读主写

example:

from mongoengine import connect
from pymongo import ReadPreference
connect('mydb', host='mongodb://server1:27017,server2:27017,server3:27017',
        replicaSet='replset',
        read_preference=ReadPreference.SECONDARY_PREFERRED)

问题

mongoengine 可以理解为对pymongo的封装,跟pymongo比,它最大的消耗在 从pymongo查询的结果,转换为Document实例, 这确实很费资源

mongoengine 在文档实例save的时候,会调用validate来验证每一个field, 这步没有必要,比较耗时

在使用queryset类时,同时用 as_pymongo() 和 only() 函数,对dict类的解析有问题, 不能解析到dict内部(二级hash), 这是mongoengine 的问题 https://github.com/MongoEngine/mongoengine/issues/563, 还未修复

mysql

重置slave

  1. >stop slave;
  2. >change master_host=’xx.xx.xx.xx’, master_user=’sns’,master_bin_log=’mysql_bin.00001’,master_position=146;
  3. >start slave;
  4. >show slave statusG; 查看 slave_io_running 是否是yes

云存储

阿里云

_images/s_oss.png

阿里云文档: http://www.aliyun.com/product/oss

scrapy

feature

scrapy 有以下特性:

  • 内建的支持数据提取(css selector 和 xpath expressions)
  • 内建的交互shell, 供debug xpath和css表达式
  • 编码检测,自动识别
  • 扩展性强(signals, middlewares, extensions, pipeline)
  • 很多内建的扩展
  • cookies and session handling
  • HTTP features like compression, authentication, caching
  • user-agent spoofing
  • robots.txt
  • crawl depth restriction
  • and more
  • 内建一个telnet控制台,用来查看running中的scrapy进程状态
  • 内建一些直接能用的类,如Sitemap Spider, 如自动下载图片的spider

深度 or 广度

scrapy 默认是深度遍历, 如果想改成广度遍历,添加:

DEPTH_PRIORITY = 1
SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.FifoMemoryQueue

爬虫封禁

爬虫可能被网站封掉,最终极的方案是“商业合作”, 在这之前,有一些技巧可以做:

  • 收集一个UA池来轮询发请求
  • 禁用cookies (某些网站使用cookie来检测爬虫行为)
  • 使用delay来防止IP封禁(见DOWNLOAD_DELAY)
  • 使用代理IP池, 免费的有TOR, 收费的有http://proxymesh.com/

sentry


配置

安装

安装sentry前,需要先安装mysql 和redis. sentry可以用pip 安装:

> pip install sentry
启动

先启动mysql:

> /etc/init.d/mysqld restart

再创建一份sentry新配置文件:

> sentry init <配置文件名>

修改配置文件,配置database信息,如下:

DATABASES = {
        'default': {
            # You can swap out the engine for MySQL easily by changing this value
            # to ``django.db.backends.mysql`` or to PostgreSQL with
            # ``django.db.backends.postgresql_psycopg2``

            # If you change this, you'll also need to install the appropriate python
            # package: psycopg2 (Postgres) or mysql-python

            'ENGINE': 'django.db.backends.mysql',
            'NAME': '数据库名',
            'USER': '账号名',
            'PASSWORD': 'msyql密码',
            'HOST': 'localhost',
            'PORT': '3307',
            }
}

接下来需要先搞好mysql:

# 创建数据库
mysql> create database 数据库

# 创建账号
mysql> create user 用户@主机 IDENTIFIED by '密码'

# 给这个用户授权

mysql> grant all on sentry.* to user@'localhost'

接下来搞好redis, sentry 内部用redis是用来做缓存的,提高性能. Redis启动略过,sentry里redis配置如下:

SENTRY_BUFFER = 'sentry.buffer.redis.RedisBuffer'
SENTRY_REDIS_OPTIONS = {
    'hosts': {
        0: {
            'host': '127.0.0.1',
            'port': 6379,
        }
    }
}

接下来在启动sentry 之前, 可以选择两种worker处理方式:

  1. 一种是celery队列异步处理。这种方式除了启动sentry web 外,还要启动多个celery worker进程, 注意,这些celery worker 进程可能会crash掉,所以需要用supervisor 来监控
  2. 另一种是不用celery队列,来了一个event事件,同步处理,性能可能差些。但是不用起celery worker进程了,不用担心celery crash 掉

如果是用celery队列,那么先启动celery worker:

# 起3个worker
> sentry --config=./sentry.settings.py celery worker -B &
> sentry --config=./sentry.settings.py celery worker -B &
> sentry --config=./sentry.settings.py celery worker -B &

接下来就是启动main server:

> sentry --config=./sentry.settings.py start

然后创建superuser:

> sentry --config=senry.settings.py createsuperuser

整个过程完成,现在可以用web登陆sentry了


报警邮件

configure as this:

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_HOST_PASSWORD = 'yourpassword'
EMAIL_HOST_USER = 'service@163.com'
EMAIL_PORT = your_port
EMAIL_USE_TLS = True
SERVER_EMAIL = 'service@163.com'

Cannot receive email notifytion After configure email:

Enter email test web(http://localhost:9000/manage/status/mail/) and type [send a test email]. If it report STARTTLS extension not supported by server , you must set EMAIL_USE_TLS = False in your sentry configure


问题

启动时报错: Transaction managed block ended with pending COMMIT/ROLLBACK:

$> sentry start

Performing upgrade before service startup...
Syncing...
Creating tables ...
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
Migrating...
Running migrations for djcelery:
   - Migrating forwards to 0004_v30_changes.
      > djcelery:0001_initial
      TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK

这是因为权限问题, sentry配置文件里的user, 没有足够的权限操作sentry数据库, 用超级账号调用:

mysql> grant all on sentry.* to user@'localhost'

sentry After start sentry web, cannot mark event to reslove:

This maybe cause by SENTRY_URL_PREFIX configure. If your add resversal proxy in front of sentry, you must set SENTRY_URL_PREFIX to the IP of resversal proxy

solr

安装

solr 5 之前,可能需要打成solr.war包,放在tomcat里启动。solr 5以后,就不支持tomcat方式启动了

requirements
需求 版本
java 1.7

Warning

solr 从5开始,不支持打成war包在web container里(如tomcat)运行了, 因为它自己就是一个独立的server

介绍

solr 家目录

下载solr并解压后,那个目录是solr的安装目录。解压完就相当于安装完了,之后,需要定一个 solr 家目录, 用来放置多个core文件的目录

solr 启动的时候,必须指定家目录。 家目录有两个作用, 一是指定配置文件,二是放置index索引文件. 家目录结构一般如下:

<solr-home-directory>/
|
|___solr.xml                    solr基础功能配置
|___zoo.cfg                     如果启动cloud模式,则家目录必须有这个配置(zookeeper的配置)
|___核心1/
    |____core.properties
    |
    |____conf/
    |    |__solrconfig.xml      solr高级功能配置
    |    |__stopwords.txt
    |    |__protwords.txt
    |    |__synonyms.txt
    |    |__managed-schema      功能和schema.xml类似
    |    |__elevate.xml
    |    |__currency.xml
    |    |__schema.xml          schema
    |
    |____data/
         |___index/
         |___tlog/

Warning

schema.xml 和 managed-schema 的功能一样,都是管理solr schema 的配置文件。具体solr使用哪个,要由solrconfig.xml里的 <schemaFactory class=”ManagedIndexSchemaFactory”> 标签来控制. managed-schema是允许solr动态来修改schema

solr 有两种模式:

SolrCloud       collections
standalone      cores

使用

下载solr

在终端执行:

wget http://archive.apache.org/dist/lucene/solr/5.0.0/solr-5.0.0.tgz -c
部署solr

为自己的项目增加solr, 分四步:

  • 定义schema. 告诉solr增加的文档怎么增加到索引中
  • 安装并启动solr, 创建core
  • 填充要索引的文档。 一般是把database import 到solr里
  • 向用户开放搜索接口
定义schema

SOLR加载数据,创建索引和数据时,核心数据结构的配置文件是schema.xml,该配置文件主要用于配置数据源,字段类型定义, 搜索类型定义等。schema.xml的配置直接影响搜索结果的准确性与效率。

Warning

默认<home>/<core>/conf/solrconfig.xml 里 schemaFactory=ManagedIndexSchemaFactory, 这样的话solr使用接口管理schema, 所以conf/schema.xml并不起作用。 修改schemaFactory=ClassicIndexSchemaFactory, 然后再创造schema.xml, 并重启solr


要定义field, 先要定义fieldType, fieldType 告诉solr每种类型对应的底层Java类, 例如:

<fieldType name="text_general" class="solr.TextField" positionIncrementGap="100">
  <analyzer type="index">
    <tokenizer class="solr.StandardTokenizerFactory"/>
      <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
        <!-- in this example, we will only use synonyms at query time
        <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/>
        -->
      <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
  <analyzer type="query">
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.StopFilterFactory" ignoreCase="true" words="stopwords.txt" />
    <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
</fieldType>

定义fieldType时,可用的属性:

属性名 作用
name The name of the fieldType. This value gets used in field definitions, in the “type” attribute. It is strongly recommended that names consist of alphanumeric or underscore characters only and not start with a digit. This is not currently strictly enforced.
class The class name that gets used to store and index the data for this type. Note that you may prefix included class names with “solr.” and Solr will automatically figure out which packages to search for the class - so “solr.TextField” will work. If you are using a third-party class, you will probably need to have a fully qualified class name. The fully qualified equivalent for “solr.TextField” is “org.apache.solr.schema. TextField”.
positionIncrementGap For multivalued fields, specifies a distance between multiple values, which prevents spurious phrase matches
autoGeneratePhraseQueries For text fields. If true, Solr automatically generates phrase queries for adjacent terms. If false, terms must be enclosed in double-quotes to be treated as phrases.
docValuesFormat Defines a custom DocValuesFormat to use for fields of this type. This requires that a schema-aware codec, such as the SchemaCodecFactory has been configured in solrconfig.xml.
postingsFormat Defines a custom PostingsFormat to use for fields of this type. This requires that a schema-aware codec, such as the SchemaCodecFactory has been configured in solrconfig.xml.
indexed If true, the value of the field can be used in queries to retrieve matching documents
stored If true, the actual value of the field can be retrieved by queries
docValues If true, the value of the field will be put in a column-oriented DocValues structure
sortMissingFirst  
sortMissingLast Control the placement of documents when a sort field is not present. As of Solr 3.5, these work for all numeric fields, including Trie and date fields.
multiValued If true, indicates that a single document might contain multiple values for this type
omitNorms If true, omits the norms associated with this field (this disables length normalization and index-time boosting for the field, and saves some memory). Defaults to true for all primitive (non-analyzed) field types, such as int, float, data, bool, and string. Only full-text fields or fields that need an index-time boost need norms.
omitTermFreqAndPositions If true, omits term frequency, positions, and payloads from postings for this field. This can be a performance boost for fields that don’t require that information. It also reduces the storage space required for the index. Queries that rely on position that are issued on a field with this option will silently fail to find documents. This property defaults to true for all fields that are not text fields.
omitPositions Similar to omitTermFreqAndPositions but preserves term frequency information
termVectors  
termPositions  
termOffsets These options instruct Solr to maintain full term vectors for each document, optionally including the position and offset information for each term occurrence in those vectors. These can be used to accelerate highlighting and other ancillary functionality, but impose a substantial cost in terms of index size. They are not necessary for typical uses of Solr
required Instructs Solr to reject any attempts to add a document which does not have a value for this field. This property defaults to false.

定义field 可用的属性:

属性名 作用
name mandatory - the name for the field
type mandatory - the name of a field type from the <types> fieldType section
indexed true if this field should be indexed (searchable or sortable)
stored true if this field should be retrievable
docValues true if this field should have doc values. Doc values are useful for faceting, grouping, sorting and function queries. Although not required, doc values will make the index faster to load, more NRT-friendly and more memory-efficient. They however come with some
limitations they are currently only supported by StrField, UUIDField and all Trie*Fields, and depending on the field type, they might require the field to be single-valued, be required or have a default value (check the documentation of the field type you’re interested in for more information)
multiValued true if this field may contain multiple values per document
omitNorms: (expert) set to true to omit the norms associated with this field (this disables length normalization and index-time boosting for the field, and saves some memory). Only full-text fields or fields that need an index-time boost need norms. Norms are omitted for primitive (non-analyzed) types by default.
termVectors [false] set to true to store the term vector for a given field. When using MoreLikeThis, fields used for similarity should be stored for best performance.
termPositions Store position information with the term vector. This will increase storage costs.
termOffsets Store offset information with the term vector. This will increase storage costs.
required The field is required. It will throw an error if the value does not exist
default a value that should be used if no value is specified when adding a document.
solr常用命令

启动停止服务:

# 创建一个home dir, 里面必须有solr.xml, 如果想使用cloud模式,还必须有zoo.cfg
./bin/solr start -s <home dir>

./bin/solr status

./bin/solr stop -p <port>

# 健康监测
./bin/solr healthcheck -c <core>

创建core:

./bin/solr create <core name>

查询接口:

curl http://localhost:8983/solr/gettingstarted/select?q=video
curl http://localhost:8983/solr/core/query -d 'q=*:*'

更新文档:

# 删除所有文档
curl http://localhost:8080/solr/update --data-binary "<delete><query>*:*</query></delete>" -H 'Content-type:text/xml; charset=utf-8'
curl http://localhost:8080/solr/update --data-binary "<commit/>" -H 'Content-type:text/xml; charset=utf-8'

扩展

solr 可以横向扩展,有两种方式:

  • cloud

    类似于数据库的sharding 模式,把数据分布在多台server上。当一个查询来到时,先去各个机器查询结果,最后把每个机器的结果merge在一起

  • replica

    类似于数据库的replica模式, 一个solr节点可以sync多份,那么对每一个slave结点,都可以查询。(可能slave结点会有延迟更新的问题)

优化solr的主要方式,是优化schema:

  • set stored=”false” for all fields possible (esp large fields) when you only need to search on the field but don’t need to return the original value.
  • set indexed=”false” if you don’t need to search on the field, but only return the field as a result of searching on other indexed fields.
  • remove all unneeded copyField statements
  • for best index size and searching performance, set “index” to false for all general text fields, use copyField to copy them to the catchall “text” field, and use that for searching.
  • For maximum indexing performance, use the ConcurrentUpdateSolrServer java client.
  • Remember to run the JVM in server mode, and use a higher logging level that avoids logging every request

supervisord

进程守护管理. 用supervisord 可以管理非daemon的进程,可以自动crash重启, web监控. supervisord分两个组件: supervisordsupervisorctl

Warning

一个supervisord 只能管理本地的subprocess, 如果有多台机器,每个机器上都有supervisord实例,这种还需要一个一个去管理. 或者自己写程序来管理,毕竟每个instance的API都可以通过网络来访问


原理

supervisord 是通过fock的方式来执行 subprocess 的,所以它能准确的捕捉到子进程意外退出信号 CHILD, 也能拿到status. 但 这样做的缺点是: 一个supervisord只能管理一台机器

client 通过conf里配置的方式跟server连接(unix domain 或者 tcp)


使用

配置一份conf, 里面有server 和 client的配置。 然后在server上启动supervisord:

supervisord -c deploy/supervisor.conf

再在client上执行:

> supervisorctl -c deploy/supervisor.conf status

test                             RUNNING   pid 4753, uptime 13:54:04

启动停止subprocess:

# 重启某个服务
> supervisorctl -c deploy/supervisor.conf restart <subprocess>
# 重启supervisord (它会重启所有subprocess)
> supervisorctl -c deploy/supervisor.conf reload

client支持的命令有:

add    clear  fg        open  quit    remove  restart   start   stop  update
avail  exit   maintail  pid   reload  reread  shutdown  status  tail  version

Warning

supervisord现在没法reload每个subprocess, 它现在的做法只能restart subprocess

插件

superlance 是一款supervisord的插件,使用它,可以让supervisord子进程退出时,发送邮件到指定邮箱 配置如下:

[eventlistener:fatalmailbatch]
command=/python2.7/bin/fatalmailbatch --toEmail="your@company.com" --fromEmail="send@company.com" --smtpHost="smtp.163.com" --userName="send@company.com" --password="passwd" --subject="[supervisord]: fatal error"
events=PROCESS_STATE,TICK_60

uwsgi

uwsgi 是一种协议, uWSGI是一个纯c写的web容器。 uWSGI实现了wsgi协议和uwsgi协议, 并且它的速度很快. uwsgi 默认是在当前python 下编译的,如果你有多个python版本环境, 有两种方式可以解决:

  1. 在每个python下编译安装各自的uwsgi
  2. 编译uwsgi用 Bonus: multiple Python versions for the same uWSGI binary, 不绑定uwsgi到具体的python版本

nginx 和 uwsgi 应用交互

在nginx中配置uwsgi时,会有这么一行:

include     uwsgi_params;

这里面其实定义了很多变量:

uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;
uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  HTTPS              $https if_not_empty;
uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;

也许就是利用环境变量传递这些值给uwsgi 应用的, uwsgi 会把这些变量转换成http header, 放在request里, 所以: 如果有些值, nginx 层有,但是django层看不到, 那么可以利用nginx 的uwsgi_param指令,把值传到django里

uwsgi插件

uwsgi 默认编译了很多插件在里面, 可以用 uwsgi --plugins-list 命令来查看有哪些可以使用的插件

route-cache

conf:

[uwsgi]
chdir = .
# plugins = router_cache
module = mysite.wsgi:application
master = True
processes = 1
stats = /tmp/test_stat.sock
http = :9090
harakiri = 8

cpu_affinity = 1
log-micros = true
cache2 = name=mycache,items=300,hash=murmur2
route = ^/feedback cache:key=${REQUEST_METHOD}${REQUEST_URI}${QUERY_STRING},name=mycache,content_type=application/json
route = ^/feedback cachestore:key=${REQUEST_METHOD}${REQUEST_URI}${QUERY_STRING},name=mycache,expires=10
route = ^/feedback log:hey i am printing $1


# route = ^/$ cache:key=myhome_for_${cookie[PHPSESSID]},name=mycache
# ; store each successfull request (200 http status code) for '/'
# route = ^/$ cachestore:key=myhome_for_${cookie[PHPSESSID]},name=mycache
autoload = true
alarm

uwsgi 可以设置报警, 就是if then, example:

alarm-backlog = mailme cmd:mail -s 'uWSGI alarm' -a 'From: foobar@example.com' admin@example.com

有用的alarm条件有这几个:

  • log-alarm: 用regex监控log
  • larm-backlog: 如果backlog表满了
  • alarm-segfault: uwsgi 段错误了

Warning

注意,uwsgi缓存只会以文本缓存response的内容, 所以查询缓存并返回给client的时候,它默认是以text/html返回的。可以用 content_type指令来指定

uwsgi 的缓存可以监控状态:: https://pypi.python.org/pypi/uwsgicachetop

其他插件

Legion(军团系统)

uwsgi的集群, 当不适用前端web server时, uwsgi也可以配高可用,就是这套集群系统。 但是还是nginx的upstream集比较安全可靠.

cheaper

根据uwsgi 负载, 动态资源分配(workprocess 数量内存占用...)

交互式启动uwsgi

command:

uwsgi --chdir . --file mysite/wsgi.py --callable application  --master --processes 1 --http :9090 --harakiri 10

常见问题

当设置了 max-requests 后,每个worker会在收到那么多请求后自动重启,会在uwsgi log 里写入这么几行:

Fire in the hole
Sat Jul 11 13:48:19 2015 - Fire in the hole !!! (60 seconds to detonation)
Gracefully killing worker 1 (pid: 22813)...
Sat Jul 11 13:48:19 2015 - stopping gevent signals watchers for worker 1 (pid: 22813)...
Sat Jul 11 13:48:19 2015 - stopping gevent sockets watchers for worker 1 (pid: 22813)...
Sat Jul 11 13:48:19 2015 - main gevent watchers stopped for worker 1 (pid: 22813)...
Sat Jul 11 13:48:19 2015 - worker 1 (pid: 22813) core 93 is managing "GET /"
Sat Jul 11 13:48:19 2015 - worker 1 (pid: 22813) core 95 is managing "GET /"

如果同时使用 geventcpu_affinity 时,有可能导致worker进程不能被有效回收, 导致可工作的worker越来越少,最终服务挂掉. 这个可以从uwsgi log里看到, respawn的worker id 越来越固定

未解决

调优: If you are running only a single application you can disable multiple interpreters. 为什么???

进阶

减负线程: https://uwsgi-docs.readthedocs.org/en/latest/OffloadSubsystem.html

Harakiri 模式

Harakiri有两种模式:

  1. 每个worker在接受每个request前,调用alarm, 这种方式是不可靠的。因为handler里面可能调用alarm来取消当前设置的超时
  2. master 进程创建一个shared memory zone, 每个worker处理请求之前,在pool中记录start时间, 结束后删除。另外有一个monitor进程,不停去检测pool 中超时的worker, 找到后kill

标准配置

example:

[uwsgi]
chdir = .
chmod-socket=664
module = mysite.wsgi:application
master = True
processes = %(%k * 4)
pidfile = /tmp/%n.pid
uwsgi-socket = /tmp/%n.sock
# http 监控(返回json)
# stats = 127.0.0.1:1717
stats = /tmp/%n_stat.sock
# uwsgi 退出的时候,清理环境, 如unix文件/ pid文件h
vacuum = True
harakiri = 50
harakiri-verbose = True
single-interpreter = True
memory-report = True
max-requests = 10000
listen = 8192

log-truncate = True
disable-logging = True
# logformat = i am a logline reporting "%(method) %(uri) %(proto)" returning with status %(status)
daemonize = /home/log/uwsgi/%n_uwsgi.log
procname-master = uwsgi --ini %n.ini (master)
procname = uwsgi --ini %n.ini

need-app = True

# where n is the number of cpus to dedicate per-worker
# 和gevent配合使用,可能会导致worker不被回收, 可用的worker会慢慢减少,减到1个,server就会挂掉
# cpu_affinity = 1

# 只对tcp有效
# so-keepalive = True

# 设置worker一定时间自动重启(second), 默认不自动重启
# max-worker-lifetime = 600

vim

vim命令

命令 作用 例子 例子说明
map 绑定vim快捷键 :map <F5> i{<Esc>ea}<Esc> 按F5 把 abc ==> {abc}

编码

首先查看本机vim是否支持unicode编码, 可以执行:

:version

来查看 是否有 +multi_byte 选项,如果没有,说明编译vim的时候,没有加 入 –with-features=big

文件编码转换

其他编码 ==> utf-8:

:set encoding=utf-8
:w!

插件

Vundle

vim 插件安装容易,卸载麻烦。 vundle 是一款vim 插件,利用它可以管理其他vim插件, 让更新、卸载plugin变的容易.

在github 上搜索要安装的plugin名. 把插件写在~/.vimrc 中:

Plugin 'msanders/snipmate.vim'

执行安装命令:

:PluginInstall

插件就自动安装了. 如果想搜索插件, 执行:

:PluginSearch! <插件名>

列出已经安装的插件:

:PluginList

原理

安装Vundle后,需要在 vimrc 中添加以下配置:

set rtp+=~/.vim/bundle/Vundle.vim            "修改vim 的runtimepath
call vundle#begin()                          "调用初始化函数(主要是定义一些命令,但没有调用)

Plugin 'gmarik/Vundle.vim'                   "调用自定义的命令 Plugin, 把管理的插件,加入自己维护的变量中
Plugin 'msanders/snipmate.vim'
Plugin 'scrooloose/nerdtree'
Plugin 'klen/python-mode'

call vundle#end()                            "遍历插件列表(由上几行定义), 把这些插件路径加入 runtimepath
filetype plugin indent on

综上, Vundle 主要是维护了一个插件列表, 启动vim的时候,把每个插件的路径,加入到vim 的runtimepath, 其他的交给插件自己定义

相关链接: https://github.com/gmarik/Vundle.vim

snipmate

根据文件扩展名, 进行代码片段补全. 只需要在 ~/.vim/bundle/snipmate.vim/snippets/ 目录下创建响应的 xxx.snippets 文件, 在里面自定义片段即可

python-mode

python 代码检测, rope支持等功能

python-mode 插件本身也是用python写的(vim 自身可以解析python, 因为编译的时候有+python feature), 有时候python脚本会出问 题,要调试的话,在vimrc中加入:

g:pymode_debug = 1

曾遇到一个问题, 保存py文件的时候,报错:

_images/vim_pymode_error.png

是因为rope会初始化项目下所有py文件, 由于这个项目下包含python整个目录,所以相对很大,导致rope初始化的.ropeproject文件有问题, 解决的办法是禁用rope(因为使用rope太慢了, 要遍历整个python目录)

相关链接: https://github.com/klen/python-mode

webbench

This is a tool for http benchmark

download url: http://home.tiscali.cz/~cz210552/webbench.html

use

webbench -c 400 -t 60 “http://www.baidu.com

-c : number of request per second -t : period of request

nginx

demo config

This is a max-optimizal nginx config:

#####################
# global config
#####################

# daemon on;
# master_process on;
# timer_resolution 100ms;
# lock_file /var/run/nginx.lock;
# worker_priority -10;

user admin admin;
pid /var/run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 102400;
error_log /var/log/nginx/error.log;

#####################
# event config
#####################

events {
    use epoll;
    worker_connections 51200;
    multi_accept off;
}

#####################
# http config
#####################

http {
    server_tokens off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;


    keepalive_timeout 10;
    client_header_timeout 10;
    client_body_timeout 10;
    reset_timedout_connection on;
    send_timeout 10;

    # 限制同一IP并发连接数
    limit_conn_zone $binary_remote_addr zone=manyconns:5m;
    limit_conn_status 405;
    limit_conn manyconns 100;

    # 限制同一IP请求量, 每秒不超过2个req, 峰值为10, 那么客户端以5req/s
    # 的速度,每5秒发一次,平均下来,不超过rate限制,不会报错
    limit_req_zone $binary_remote_addr zone=manyreqs:10m rate=2r/s;
    limit_req_status 404;
    limit_req zone=manyreqs burst=10 nodelay;

    include /etc/nginx/mime.types;
    default_type text/html;
    charset UTF-8;


    gzip on;
    gzip_disable "msie[1-6]";   # 为IE6关闭gzip, 兼容老浏览器
    gzip_min_length  1100;
    gzip_comp_level   6;
    gzip_buffers     16 8k;
    gzip_types  text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript image/png image/gif image/jpeg;
    gzip_proxied any;

    ##
    #client_body,client_header config
    ##

    client_body_temp_path   /dev/shm/nginx/client_body_temp 1 2;
    client_body_timeout     60s;
    client_header_timeout   60s;
    client_max_body_size    1m;

    ##
    #request state
    ##
    req_status_zone server "$host,$server_addr:$server_port" 10M;

    ##
    #uwsgi global
    ##

    uwsgi_temp_path             /dev/shm/nginx/uwsgi_temp 2 2;
    uwsgi_max_temp_file_size    1024m;
    uwsgi_temp_file_write_size  8k;
    uwsgi_buffering             on;
    uwsgi_ignore_client_abort   on;
    uwsgi_buffer_size           8k;
    uwsgi_buffers               24 8k;
    uwsgi_busy_buffers_size     8k;
    uwsgi_connect_timeout       60s;
    uwsgi_send_timeout          60s;
    uwsgi_read_timeout          60s;


    # uwsgi cache
    # 配置uwsgi cache 后, nginx 会多启动两个进程 (cache manager process) 和 (cache loader process)
    uwsgi_cache_path /tmp/uwsgi_cache levels=1:2 keys_zone=CACHE:10m inactive=10s;

    ##
    #proxy config
    ##

    proxy_buffering             on;
    proxy_buffers               8 4k;

    proxy_temp_path             /dev/shm/nginx/proxy_temp 1 2;
    proxy_cache_path            /dev/shm/nginx/proxy_cache  levels=1:2 keys_zone=cache:128m inactive=1d max_size=1g;
    proxy_connect_timeout       60s;
    proxy_read_timeout          60s;
    proxy_send_timeout          60s;

    #####################
    # http config
    #####################

    upstream mysite
    {
        # ip_hash;
        # keepalive;
        # least_conn;
        server unix:/tmp/mysite.sock weight=10 max_fails=1 fail_timeout=10;
        server 192.168.1.1:8080 weight=20 backup; # 标示这个server不会做load balancer, 除非其他server挂了
        server 192.168.1.1:8080 weight=20 down;  # 标示这个server不可用
    }

    server {
        # listen 80 backlog=2048 deferred keepalive=on fastopen=100;
        listen 80 backlog=1024 deferred;
        server_name     localhost;

        location / {
            uwsgi_pass  mysite;
        }

        location /cached {
            uwsgi_cache CACHE;
            uwsgi_buffering on;
            uwsgi_cache_key $scheme$request_method$host$request_uri$args;
            uwsgi_pass  mysite;
            uwsgi_cache_valid 200 20s;

            uwsgi_pass  mysite;
        }

        location /nginx_status {

            limit_except GET {
                allow 192.168.1.0/32;
                deny  all;
            }

            access_log off;
            stub_status on;
            access_log off;
            allow 127.0.0.1;
        }

        location /reqstat {
            access_log off;
            allow 127.0.0.1;
            deny all;
            req_status_show;
        }
    }

    include /usr/local/nginx/conf/sites-enabled/*.conf;
}

use

简介

nginx 是开源、多平台的http 服务器,以下是它的特性:


_images/s_nginx_feature.png

nginx 虽然是开源的,但Igor Sysoev 已为nginx 成立公司,提供商业级的产品与支持

安装
依赖
依赖库 作用 备注
PCRE(required) 正则匹配
zlib(required) gzip 压缩使用  
openssl https 需要  
编译

configure选项

选项(默认安装) 作用
–with-rtsig-module (no) 使用rtsig module 处理事件驱动. 默认情况下, nginx 不安装rtsig module
–with-select_module (no) 使用select module 处理事件驱动. 如果nginx 没有找到epoll, 该模块将会被安装
–without-select_module (no) 不使用select module 处理事件驱动
–with-poll_module (no) 与select 模块类似,默认不会安装
–without-poll_module (no) 不使用poll module 处理事件驱动
–with-aio_module (no) 使用AIO方式处理文件访问
–without-http_charset_module (yes) 这个模块可以降服务器发出的http响应重新编码
–without-http_gzip_module (yes)  
–without-http-ssi_module (yes) 该模块可以在Response 中加入特定的内容,如html固定页头/页尾
–without-http_userid_module (yes) 这个模块可以通过HTTP请求头,认证用户信息,以确定请求是否合法
–without-http_access_module (yes) 这个模块可以限制客户端IP
–without-http_auth_basic_module 这个模块可以提供最简单的用户密码验证
–without-http_autoindex_module 目录浏览功能
–without-http_geo_module 这个模块可以定义一些变量,这些变量的值与客户端IP地址关联. 例如: 这样Nginx 针对不同地域显示不同的语言
–without-http_map_module 这个模块可以建立一个key/value映射表, 这样可以针对不同的url做特殊处理

configure 脚本的主要目的是 检测编译依赖库是否存在 , 生成ngx_modules.c文件


信号操作
信号 作用
nginx -s stop 停止服务
nginx -s quit 优雅的停止服务
nginx -s reload 重载配置
nginx -s reopen 日志文件回滚
kill -s SIGUSR2 平滑升级nginx
配置
core模块
选项 作用
daemon <on> 是否以daemon方式运行。默认是on, 提供off的目的是为了开发调试
master_process <on> 是否启动worker进程。 同上,提供off只是为了调试
timer_resolution <time> By default, gettimeofday() is called each time a kernel event is received. With reduced resolution, gettimeofday() is only called once per specified this.
pid <path> pid文件路径,默认在<prefix/logs/nginx.pid>
lock_file <path> nginx 多个进程访问共享内存时,需要用lock来同步,但现在的nginx都用的是atomic, 所以这个指令相当于已经废弃
user <user> worker进程的owner
worker_processes <num> 指定worker进程个数, 可以设置为``auto``, nginx自动检测CPU核心数
worker_rlimit_nofile <int> 102400 设置一个worker进程可以打开的最大文件句柄数
worker_priority <num> nginx进程优先级, 默认-10. 值越低, 优先级越高。 注意优先级不要设置太高,否则 系统调用的优先级会被比下去
worker_cpu_affinity <...> CPU亲和性
worker_rlimit_core coredump 文件的最大大小
worker_rlimit_sigpending 只对RTSIG 系统有用。On systems that support rtsig connection processing method, changes the limit on the number of signals that may be queued (RLIMIT_SIGPENDING) for worker processes. Used to increase the limit without restarting the main process
working_directory <path> Defines the current working directory for a worker process. It is primarily used when writing a core-file
env <...> 默认的,nginx的worker进程会清除从parent继承来的所有环境变量, 这个指令可以保留 部分变量
event模块
选项 模块 作用
worker_connections <num> event_core 一个worker进程可以建立的最大连接总数, 它不能超过 worker_rlimit_nofile, 否则 没有意义. 默认512
connections <num> event_core 貌似被废弃了, nginx文档里没有这个指令, 从代码看默认512
use <event> event_core 使用那种connection processing method, nginx默认会选择最高效的方式
multi_accept <on> event_core 当事件模型通知有新请求时,尽可能对本次调度中客户端的所有TCP请求都建立连接, 默认off. 如果同一时间过来的请求量太大,一个worker进程会花费很多在accept上, 所以这时应该关闭
accept_mutex <on> event_core
  1. 避免惊群效果 (每个 accept 上一把锁);
  2. 负载平衡 (如果当前worker的请求量已达到worker_connections的7/8, 则这个worker 不参与竞争新来的request) ,默认是on
accept_mutex_delay event_core 和accept_mutex 配合用, 如果一个worker进程未拥有accept mutex,它至少延迟这么 长时间之后再尝试抢夺, 默认500ms
epoll_events <num> epoll ??????, 默认512
worker_aio_requests <num> epoll ??????, 默认32
http模块
选项 模块 作用
error_log errlog 改变error log 路径, 默认是 /prefix/logs/error.log
client_header_timeout http_core 收到一个请求后,nginx 建立与客户端的TCP链接, 链接建立后开始read 客户端发送的数据。如果这个客户端是个hacker, 它hung住来浪费nginx 资源,那么nginx在这段时间内会一直维持着这个连接。此参数设置 nginx等待时间,如果超过时间,nginx直接返回408错误, 默认60s
client_body_timeout <time> http_core 同上,请求体超时
connection_pool_size http_core nginx为一个连接能开辟内存的上限, 默认256. 这个值对性能调优效果很小,不应该设置
request_pool_size http_core nginx为一个请求能开辟的内存上限,默认4k, 这个值对性能调优效果很小,不应该设置
client_header_buffer_size http_core 处理一个请求头最大开辟的内存大小, 默认1k
large_client_header_buffers http_core 处理一个请求头,如果请求过大,超过client_header_buffer_size指定缓存,那么使用 这个设置项
client_max_body_size <size> http_core 根据请求头的content-Length, 来限制请求. 若超出限制,nginx 返回413
client_body_buffer_size <num> http_core 一个请求的请求体需要存在内存中, 这个值指定了buffer大小, 如果超过这个值, nginx 会把请求体写入磁盘
client_body_in_file_only <on> http_core 对于一个请求,如果请求体过大,超过client_body_buffer_size大小
client_body_temp_path http_core 同上,如果请求过大,nginx会把请求存在临时文件中
sendfile <on> http_core 启用sendfile 系统调用,来减少static file 请求时,用户态和内核态的切换时间
aio http_core 使用AIO方式来访问静态文件服务, 和sendfile互斥。两者谁好谁坏很难定论, nginx 论坛上,建议使用AIO, 因为sendfile对预读的控制不好. 应使用aio + directio
tcp_nodelay <on> http_core 对keepalive连接是否使用TCP_NODELAY选项, 与NOPUSH选项互斥, 其实是关闭 Nagle’s 算法, Nagle 是避免传输数据小,网络带宽无法有效利用,将多个数据组 包一起发送, 但是HTTP的数据更多是偏向流处理,而不是类似telnet,等待用户输入 数据, 所以默认是TCP_NODELAY 开启
send_timeout http_core nginx 返回response 给客户端的时候,每发送一个packet, 都希望收到ack, 如果在 这个指定的时间内,没有收到ack, 说明client可能掉了,那么关闭连接
keepalive_requests <num> http_core nginx 建立一个keep alive 连接后,在这个连接上处理了<num>个请求后,就关闭连接
keepalive_disable <...> http_core nginx 可以设置对哪些浏览器不keep alive, 可选的有: msie6, safari
satisfy <...> http_core nginx 有多种限制access的方式,如ip, base_auth, 这个指令是决定把所有限制<与> 还是<或>
log_not_found <on> http_core 访问不存在的文件, 这种请求是否记录在error log里, 默认记录<on>
server_tokens <on> http_core 在错误response是否显示nginx版本, 默认on
error_page http_core error_page 500 502 503 504 /50x.html;
try_files http_core try file
error_log http_core 指定error log路径
open_file_cache <max> <inact> http_core max=102400 inactive=20s open_file_cache 会告诉 Nginx 去缓存打开的文件, “未找到”的错误,有关文件的元 数据和他们的权限,等等。这样做的好处是,一个高 需求的文件要求时,Nginx 的可以 立即开始发送数据;而且也知道立即发送一个 404, 但是,有一个不太理想的缺点:如果磁盘上有变化,服务器不会立即作出反应。 最多 缓存<max>个, 非活跃的缓存 <inact>秒后从缓存剔除
open_file_cache_valid <time> http_core 30s 而活动(最近要求的文件)每<time>秒重新验证一次。
open_file_cache_min_uses http_core 如果定义活跃的item, 这个指令就是定义: 在<inact>时间内至少访问过 <time>次
open_file_cache_errors http_core 缓存404
resolver http_core 配置nginx 内部的DNS服务器
resolver_timeout http_core 配置DNS查询超时
read_ahead <size> http_core ???
lingering_close <on> http_core ???
lingering_time <on> http_core ???
lingering_timeout <on> http_core ???
其他模块
选项 模块 作用
error_log errlog 改变error log 路径, 默认是 /prefix/logs/error.log
基本配置
选项 作用
user <用户名> worker进程的执行用户
use <事件模型> 选择事件模型(如: epoll)
listen <num...>

监听端口, 它有以下参数:

default_server: 默认server, nginx 可能有多个server配置,设置这个后当前 server就成为默认server(server_name没有匹配)

bind: 当设置 listen xx.xx.xx.xx:80 时,默认nginx 不会限制IP, 就是不会绑定 xx.xx.xx.xx这个IP接口, 设置bind则绑定

fastopen: 打开TCP fastopen选项, TCP fastopen特性只有kernel 版本大于3才支持 这个参数水很深,不要随便设置

backlog <num>: 默认512, 修改完net.core.somaxconn之后一定要修改这个,否则 等于没改

deferred: 默认新来一个TCP连接,三次握手后master进程就唤醒worker进程来 接待。 设置这个参数后,三次握手完成master并不立 刻唤醒worker, 而是这个连接 上真来了数据,才唤醒worker, 它减轻了worker的负担。降低服务端进行 epoll_ctl、epoll_wait(linux下)的次数(系统调用)和降低服务端保持的连接句柄数 需要根据业务特征来决定

server_name <...>

虚拟主机配置, nginx 检测request头的HOST字段,拿来匹配server, 按以下顺序匹配:

  1. 字符串完全匹配, 如: www.test.com
  2. 通配符前匹配, 如: *.test.com
  3. 通配符后匹配, 如: www.test.*
  4. 正则匹配, 如: ~^(?<user>.+).example.net$; (注意,正则表达式前要增加~)
  5. 都没有匹配, 使用default_server

server_name 是忽略大小写的,因为它是把server_name转成小写

server_names_hash_bucket_size 为了提高快速找到server_name的能力, nginx使用了散列桶, 这个参数指定散列桶的 大小, 越大越占内存,但速度越快. 默认32|64|128
server_names_hash_max_size 效果同上. 默认512
server_name_in_redirect <yes> 重定向的时候,把原请求里的HOST, 换成server_name写的第一个主机名
location <...> 用请求中的url来匹配, 见 location
alias <path> 指定文件路径
root <path> 指定文件路径(和alias 互为两种方式)
index <path> 指定主页的html文件, 默认为(index.html)
error_page <code> <url> 错误重定向, 出现<code>对应的错误response时,nginx 把结果重定向到url
resursive_error_pages <on> 是否打开”错误重定向”的递归
try_files <path1> <path2> <url> 按顺序尝试每一个path
limit_except {...} 按http方法, 限制客户端请求种类, 见 limit_except
limit_rate <num> 对每一个TCP连接限速
ignore_invalid_headers <on> 如果出现不合法的HTTP头部时, nginx 会忽略错误继续处理。但如果这个选项被off nginx 会直接返回400
underscores_in_headers <on> http头部是否允许带下划线
log_not_found <on> 404是否记录日志
merge_slashes <on> 是否合并url中相邻的/, 如: //test//a.txt 会变成 /test/a.txt
resolver <ip> 设置DNS服务器地址
resolver_timeout <time> DNS解析超时时间, 默认30s
server_tokens <on> 返回错误页面时,是否在server中注明nginx版本, 默认on

内部资源分配
选项 作用
connnection_pool_size <num> 每个TCP连接分配的内存池初始大小, 默认256, 如果这个值太大,内存占用会很多, 如果很小,造成分配次数增多
send_timeout <time> nginx 向客户端发送了数据,但客户端超过这么长时间都没有去接收数据,那么 nginx 会关闭这个连接
reset_timeout_connection ??? 向客户端发送RST来关闭连接, 减少服务端的FIN-WAIT状态套接字
lingering_close ???
lingering_time ???
lingering_timeout ???
keepalive_disable <...> 对某些浏览器禁用 keepalive 功能
keepalive_timeout <time> keepalive 超时时间, 每个http1.1的连接, 默认nginx都是长连接,超时时间由 这个值来定
keepalive_requests <num> 一个keepalive 连接上默认最多能发送的request 个数
tcp_nopush <on> 是否开启FreeDSB的TCP_NOPUSH 或Linux 的TCP_CORK功能
proxy_buffering <on> proxy服务器接收后端upstream响应的时候,是否使用buffer先把response缓存起 来,等upstream处理完了,再全部send到客户端. 打开的好处是, 只要代理nginx 和后端nginx交互完后,就可以关闭这条链接,减少upstream服务器开销,这对于 客户端响应很慢的情况来说,是很有利的,后端之间的链接,不用等客户端响应

文件访问
选项 作用
open_file_cache ???
open_file_cache_errors ???
open_file_cache_min_uses ???
open_file_cache_valid ???

MIME
选项 作用
type {...} 配置文件扩展名与mime映射
default_type <...> 默认MIME类型
types_hash_bucket_size 上面映射的散列桶大小
types_hash_max_size 上面散列桶的个数

性能调优
选项 作用
worker_cpu_affinity <...> worker和CPU绑定 (仅对Linux系统起作用, 内部调用sched_setaffinity()来实现, 示例: worker_cpu_affinity 1000 0100 0010 0001
ssl_engine <device> ssl硬件加速。如果服务器上有SSL硬件加速设备,就可以用这个指令配置
timer_resolution <time> 默认情况下,每次内核事件调用,都要执行一次 gettimeofday
worker_prority <int> 在Linux系统中,每个进程都有优先级,范围为[-19,+20], -19优先级最高, 如果想让分配CPU资源的时候,多分给nginx, 可以增加nginx优先级,默认nginx 值 为0 (不建议低于-5, 内核进程的优先级)
accept_mutex <on>
  1. 避免惊群效果 (每个 accept 上一把锁);
  2. 负载平衡 (如果当前worker的请求量已达到worker_connections的7/8, 则这个worker 不参与竞争新来的request)
multi_accept <on> 当事件模型通知有新请求时,尽可能对本次调度中客户端的所有TCP请求都建立连接
worker_connections 每个worker 的连接池大小。 所以: nginx能接收的总的连接数 = worker_connections * worker_processes
反向代理
_images/s_nginx_rproxy.png

Nginx代理和squid代理机制不太一样, 客户端发送请求, nginx代理服务器接收完整个请求,才向upstream转发请求, 这样做的目的 主要是 降低upstream 的压力. 因为客户端到nginx代理之间一般式走外网, 速度较慢。 而代理和upstream之间一般式走内网, 速度很快

buffer

Nginx proxy use buffer default. Without buffers, data is sent from the proxied server and immediately begins to be transmitted to the client. If the clients are assumed to be fast, buffering can be turned off in order to get the data to the client as soon as possible. With buffers, the Nginx proxy will temporarily store the backend’s response and then feed this data to the client. If the client is slow, this allows the Nginx server to close the connection to the backend sooner. It can then handle distributing the data to the client at whatever pace is possible.

If you want turn it off, you should set proxy_buffering to off

refer: https://www.digitalocean.com/community/tutorials/understanding-nginx-http-proxying-load-balancing-buffering-and-caching

负载均衡

upstream负载均衡有两种机制: ip_hashweight

ip_hash 是nginx按客户端ip, 自动的把请求打在上游集群中特定一台, 配置如下:

upstream backend {
    ip_hash
    server backend1.example.com;
    server backend2.example.com;
    server backend3.example.com;
}

weight 是按权重来分流量, 配置如下:

upstream backend {
    server backend1.example.com weight=2 max_fails=3 fail_timeout=30s;
    server backend2.example.com weight=2 max_fails=2 fail_timeout=20s;
    server backend3.example.com weight=1;
}

一份反向代理配置如下:

upstream real.sites {

    server 123.123.123.123;

    // 用keepalive保存长连接,降低频繁创建连接的开销
    keepalive 16;
}

proxy_cache_path /path/to/cache levels=1:2 keys_zone=static_cache:100m;

server {
    server_name     www.example.com;

    // 把真正的IP地址放到header的X-Forwarded-For里面
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_http_version 1.1;

    proxy_set_header Connection "";

    # 当某一个上游返回503错误时, nginx 继续换一个上游转发
    # 默认一个上游返回错误时,nginx是不会换一个上游转发的
    proxy_next_upstream http_503;

    // 把静态资源缓存起来,减少服务器间数据传输
    location ~ \.(css|js|jpg|png|gif|ico)$ {

    proxy_cache static_cache;

    proxy_pass http://real.sites;
    }

    location / {
        proxy_pass http://real.sites;
    }
}

性能调优
内核参数调优

内核调优

debug nginx

方法1: 打开 –with-debug

方法2: Debugging nginx with DTrace pid provider

相关链接: http://nginx.org/en/docs/nginx_dtrace_pid_provider.html

减少系统调用

系统调用的开销是比较大的,因为它会从用户态进入内核态,再退出, x这种切换是比较昂贵的. 我们可以通过配置nginx来减少不必要的系统调用,如: open/gettimeofday

通过strace命令来查看nginx接受一个请求会调用哪些系统调用,然后分析可以通过配置关闭的调用, 如: gettimeofday


其他
location 配置

= 匹配符, 完全匹配才处理, e.g:

location = / {
     # 完全匹配才处理, 只处理url/
     ...
}

~ 匹配符, 表示执行一个正则匹配, 大小写敏感

~* 匹配符, 表示执行一个正则匹配, 大小写不敏感

^~ 表示普通字符匹配,如果该选项匹配,只匹配该选项,不匹配别的选项,一般用来匹配目录

可以使用正则, e.g:

location  = / {
     # 只匹配"/".
     [ configuration A ]
}

location  / {
     # 匹配任何请求,因为所有请求都是以"/"开始
     # 但是更长字符匹配或者正则表达式匹配会优先匹配
     [ configuration B ]
}

location ^~ /images/ {
     # 匹配任何以 /images/ 开始的请求,并停止匹配 其它location
     [ configuration C ]
}

location ~* \.(gif|jpg|jpeg)$ {
     # 匹配以 gif, jpg, or jpeg结尾的请求.
     # 但是所有 /images/ 目录的请求将由 [Configuration C]处理.
     [ configuration D ]
}
log 配置

log配置有四个指令:

error_log logs/error.log warn;
log_format gzip '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $bytes_sent '
                '"$http_referer" "$http_user_agent" "$gzip_ratio"';

access_log /spool/logs/nginx-access.log gzip buffer=32k;
open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;

默认的error日志,在logs/error.log. 默认的access日志, 在logs/access.log. 可以通过 log_format 指令来定义format, 然后 在access_log指令里用.

Warning

尽量不要在log 路径里写变量, 否则nginx 每写一条日志,都要打开关闭一次文件描述符, 虽然可以用open_log_file_cache 指令 来优化这点,但还是很影响性能


写log时默认使用同level的log配置, 如果当前level没有log配置, 就继承上层配置。如果当前level有多个log配置,那么每个配置都写 一遍

Don’t use If

IfIsEvil: http://wiki.nginx.org/IfIsEvil

限制请求方法

配置:

limit_except GET POST DELETE {
    deny all;
}

意思是, 除了GET POST DELETE 方法外,其他一切请求都deny

扩展
模块 作用
nginx_upstream_check_module upstream 集群健康监测模块
问题

implement

configure script

The configure script is a bash script which in charge of generating 3 important file:

  • ngx_auto_headers.h
  • ngx_auto_config.h
  • Makefile

Those files are used in compiled. The configure script generating code by checking compile environment and parsing configure arguments. For example:

ngx_feature="/dev/poll"
ngx_feature_name="NGX_HAVE_DEVPOLL"
ngx_feature_run=no
ngx_feature_incs="#include <sys/devpoll.h>"
ngx_feature_path=
ngx_feature_libs=
ngx_feature_test="int  n, dp; struct dvpoll  dvp;
                 dp = 0;
                 dvp.dp_fds = NULL;
                 dvp.dp_nfds = 0;
                 dvp.dp_timeout = 0;
                 n = ioctl(dp, DP_POLL, &dvp);
                 if (n == -1) return 1"
. auto/feature

this pice checking /dev/poll feature. It generate a test.c file and compile it. If successed, it echo one preprocess macro into auto_headers.h

Architecture
_images/s_nginx_arch.jpg

nginx 把一个Http请求的处理,分为若干个步骤, 按处理顺序如下:


步骤 模块/命令 hander
server selection (*) listen, server_name  
NGX_HTTP_POST_READ_PHASE HttpRealIpModule (第三方)  
NGX_HTTP_SERVER_REWRITE_PHASE rewrite ngx_http_rewrite_handler (rewrite)
NGX_HTTP_FIND_CONFIG_PHASE (*) location  
NGX_HTTP_REWRITE_PHASE rewrite ngx_http_rewrite_handler (rewrite)
NGX_HTTP_POST_REWRITE_PHASE (*)    
NGX_HTTP_PREACCESS_PHASE degradation, limit_zone, limit req, HttpRealIpModule ngx_http_limit_conn_handler (limit_conn) ngx_http_limit_req_handler (limit_req)
NGX_HTTP_ACCESS_PHASE allow, deny, auth_basic ngx_http_auth_basic_handler (auth_base) ngx_http_access_handler (access)
NGX_HTTP_POST_ACCESS_PHASE (*)    
NGX_HTTP_TRY_FILES_PHASE (*) try_files  
NGX_HTTP_CONTENT_PHASE autoindex, Core, DAV, EmptyGif, FastCGI, FLV, gzip_static, index, memcached, perl, proxy, random_index, scgi, stub_status, uwsgi ngx_http_static_handler (static) ngx_http_autoindex_handler (autoindex) ngx_http_index_handler (index)
NGX_HTTP_LOG_PHASE access_log ngx_http_log_handler (log)

Warning

其中每一个phase里每个handler的顺序是可能改变的, 由 ngx_module_t *ngx_modules 来定(auto configure 生成的)

modules
_images/s_nginx_modules.png

There are 5 type of modules:

  • NGX_CORE_MODULE
  • NGX_HTTP_MODULE
  • NGX_CONF_MODULE
  • NGX_EVENT_MODULE
  • NGX_MAIL_MODULE
模块 类型 idx cmd init master init module init process init thread exit thread exit process exit master conf_context
core CORE 0 Y               链接
errlog CORE 1 Y               conf save in cycle
conf CONF 2 Y           Y    
events CORE 3 Y                
event_core EVENT 4 Y   Y Y         链接
epoll EVENT 5 Y               链接
regex CORE 6 Y   Y           链接
http CORE 7 Y               连接
_core HTTP 8 Y               链接
_log HTTP 9 Y               链接
_upstream HTTP 10 Y               链接
_static HTTP 11                  
_gzip_static HTTP 12 Y               链接
_autoindex HTTP 13 Y               链接
_index HTTP 14 Y               链接
_auth_basic HTTP 15 Y               链接
_access HTTP 16 Y                
_limit_conn HTTP 17 Y                
_limit_req HTTP 18 Y                
_geo HTTP 19 Y                
_map HTTP 20 Y                
_split_clients HTTP 21 Y                
_referer HTTP 22 Y                
_rewrite HTTP 23 Y                
_proxy HTTP 24 Y                
_fastcgi HTTP 25 Y                
_uwsgi HTTP 26 Y                
_scgi HTTP 27 Y                
_memcached HTTP 28 Y                
_empty_gif HTTP 29 Y                
_browser HTTP 30 Y                
_upstream_ip_hash HTTP 31 Y                
_upstream_least_conn HTTP 32 Y                
_upstream_keepalive HTTP 33 Y                
_stub_status HTTP 34 Y                
_write_filter HTTP 35                  
_header_filter HTTP 36                  
_chunked_filter HTTP 37                  
_range_header_filter HTTP 38                  
_gzip_filter HTTP 39 Y                
_postpone_filter HTTP 40                  
_ssi_filter HTTP 41 Y                
_charset_filter HTTP 42 Y                
_userid_filter HTTP 43 Y     Y          
_headers_filter HTTP 44 Y                
_copy_filter HTTP 45 Y                
_range_body_filter HTTP 46                  
_not_modified_filter HTTP 47                  

Tip

http 模块原本负责(联系全局模块和http协议模块, 实现http协议), 但目前实现http协议已经分离出来,为http_core模块。这样做 是因为将来nginx还要实现除了http之外的协议,如SPDY

upstream 模块

配置一个代理需要两部:

  1. 先定义一个upstream(如果就一台backend, 这步也可以省略)
  2. 在某个location里配置upstream

如下:

location / {
    proxy_pass http://127.0.0.1;
}

如果一个location配置了转发, 那么他的context 会这样变化:

clcf->handler = ngx_http_proxy_handler
并且一个请求来的时候,会先检测它的handler是否为空,如果不为空,就调用handler然后直接返回(相当于交由backend处理). 但这个
交由backend处理, 就是各种upstream模块的工作了, 一个upstream模块需要实现7个回调函数
回调函数 函数功能 upstream代理模块
create_request 根据nginx与后端服务器通信协议(比如HTTP、 Memcache),将客户端的HTTP请求信息转换为对 应的发送到后端服务器的真实请求。 ngx_http_proxy_create_request 由于nginx与后端服务器通信 协议也为HTTP,所以直接拷贝客户端的请求头、请求体(如果有 )到变量r->upstream->request_bufs内。
process_header 根据nginx与后端服务器通信协议,将后端服务 器返回的头部信息转换为对客户端响应的HTTP响 应头。 ngx_http_proxy_process_status_line 此时后端服务器返回的 头部信息已经保存在变量r->upstream->buffer内,将这串字符 串解析为HTTP响应头存储到变量r->upstream->headers_in内。
input_filter_init 根据前面获得的后端服务器返回的头部信息, 为进一步处理后端服务器将返回的响应体做初 始准备工作。 ngx_http_proxy_input_filter_init 根据已解析的后端服务器 返回的头部信息,设置需进一步处理的后端服务器将返回的响 应体的长度,该值保存在变量r->upstream->length内。
input_filter 正式处理后端服务器返回的响应体。 ngx_http_proxy_non_buffered_copy_filter 本次收到的响应体 数据长度为bytes,数据长度存储在r->upstream->buffer内, 把它加入到r->upstream->out_bufs响应数据链等待发送给客户端
finalize_request 正常结束与后端服务器的交互,比如剩余待取数 据长度为0或读到EOF等,之后就会调用该函数。 由于nginx会自动完成与后端服务器交互的清理 工作,所以该函数一般仅做下日志,标识响应正 常结束。 ngx_http_proxy_finalize_request 记录一条日志,标识正常结 束与后端服务器的交互,然后函数返回。
reinit_request 对交互重新初始化,比如当nginx发现一台后端 服务器出错无法正常完成处理,需要尝试请求 另一台后端服务器时就会调用该函数。 ngx_http_proxy_reinit_request 设置初始值,设置回调指针, 处理比较简单。
abort_request 异常结束与后端服务器的交互后就会调用该函 数。大部分情况下,该函数仅做下日志, 标识响应异常结束。 ngx_http_proxy_abort_request 记录一条日志,标识异常结束 与后端服务器的交互,然后函数返回。

它们之间的调用顺序如下:

_images/s_nginx_upstream_sort.jpg
Load-Balance 模块

Load-Balance 模块是一个辅助模块,它之作用于upstream模块,它的目标是: 如何从多台backend中选择一个合适的服务器来处理请求

它需要处理四个回调函数:

回调函数 函数功能 upstream代理模块
uscf->peer.init_upstream 解析配置文件过程中被调用,根据upstream里 各个server配置项做初始准备工作,另外的核心 工作是设置回调指针us->peer.init。配置文件 解析完后就不再被调用。 ngx_http_upstream_init_round_robin 设置:us->peer.init = ngx_http_upstream_init_round_rob in_peer; ngx_http_upstream_init_ip_hash 设置:us->peer.init = ngx_http_upstream_init_ip_hash_peer
us->peer.init 在每一次nginx准备转发客户端请求到后端服务 器前都会调用该函数,该函数为本次转发选择合 适的后端服务器做初始准备工作,另外的核心工 作是设置回调指针r->upstream->peer.get和 r->upstream->peer.free等。 ngx_http_upstream_init_round_robin_peer 设置:r->upstream->peer.get = ngx_http_upstream_get_round _robin_peer; r->upstream->peer.free = ngx_http_upstream _free_round_robin_peer; ngx_http_upstream_init_ip_hash_peer 设置:r->upstream->peer.get = ngx_http_upstream_get_ip_hash _peer; r->upstream->peer.free为空。
r->upstream->peer.get 在每一次nginx准备转发客户端请求到后端服务 器前都会调用该函数,该函数实现具体的为本 次转发选择合适后端服务器的算法逻辑,即完 成选择获取合适后端服务器的功能。 ngx_http_upstream_get_round_robin_peer 加权选择当前权值最高 (即从各方面综合比较更有能力处理当前请求)的后端服务器。 ngx_http_upstream_get_ip_hash_peer 根据ip哈希值选择后端服务器
r->upstream->peer.free 在每一次nginx完成与后端服务器之间的交互后 都会调用该函数。如果选择算法有前后依赖性, 比如加权选择,那么需要做一些数值更新操作; 如果选择算法没有前后依赖性,比如ip哈希, 那么该函数可为空; ngx_http_upstream_free_round_robin_peer 更新相关数值, 比如rrp->current等。 空
other
location tree

对于每一个server, 里面可能有多个location 配置, 所有location 有两种可能: 正则匹配精确匹配(以它开头) 最简单的做法是, nginx实现一个数组,把所有location配置塞进去。一个请求到来的时候,依次遍历数组去判断.

但是nginx要实现一个feature: 先处理 精确匹配, 再处理 正则匹配. 并且遍历数组的效率明显偏低. 所以解析完一个server 下的所有location配置以后, nginx 构造了两个变量:

ngx_http_location_tree_node_t   *static_locations;
ngx_http_core_loc_conf_t       **regex_locations;

static_locations 是一个平衡排序三叉树,保存了 精确匹配 的所有location, 而regex_locations是一个数组, 保存了 正则匹配. 一个请求来的时候,先遍历精确匹配的三叉树(效率很高), 如果不匹配再遍历正则匹配的数组

_images/s_nginx_location_tree.jpg
conf context of modules

There are config struct for each nginx module

struct:

ngx_flag_t               daemon;
ngx_flag_t               master;
ngx_msec_t               timer_resolution;
ngx_int_t                worker_processes;
ngx_int_t                debug_points;
ngx_int_t                rlimit_nofile;
ngx_int_t                rlimit_sigpending;
off_t                    rlimit_core;
int                      priority;
ngx_uint_t               cpu_affinity_n;
uint64_t                *cpu_affinity;
char                    *username;
ngx_uid_t                user;
ngx_gid_t                group;
ngx_str_t                working_directory;
ngx_str_t                lock_file;
ngx_str_t                pid;
ngx_str_t                oldpid;
ngx_array_t              env;
char                   **environment;

struct:

typedef struct {
    ngx_flag_t  pcre_jit;
} ngx_regex_conf_t;

struct:

typedef struct {
    ngx_uint_t    connections;
    ngx_uint_t    use;
    ngx_flag_t    multi_accept;
    ngx_flag_t    accept_mutex;
    ngx_msec_t    accept_mutex_delay;
    u_char       *name;
} ngx_event_conf_t;

struct:

typedef struct {
    ngx_uint_t  events;
    ngx_uint_t  aio_requests;
} ngx_epoll_conf_t;

struct:

typedef struct {
    void        **main_conf;        // sizeof(void *) * ngx_http_max_module
    void        **srv_conf;         // sizeof(void *) * ngx_http_max_module
    void        **loc_conf;         // sizeof(void *) * ngx_http_max_module
} ngx_http_conf_ctx_t;

ngx_http_core_conf:

// ===========================   main conf ====================================

typedef struct {
    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */
    ngx_http_phase_engine_t    phase_engine;
    ngx_hash_t                 headers_in_hash;
    ngx_hash_t                 variables_hash;
    ngx_array_t                variables;       /* ngx_http_variable_t */
    ngx_uint_t                 ncaptures;
    ngx_uint_t                 server_names_hash_max_size;
    ngx_uint_t                 server_names_hash_bucket_size;
    ngx_uint_t                 variables_hash_max_size;
    ngx_uint_t                 variables_hash_bucket_size;
    ngx_hash_keys_arrays_t    *variables_keys;      /*很多固定变量, 有些模块会增加自己的变量, 如:
                                                      http_upstream, http_proxy, http_fastcgi,
                                                      http_browser, http_stub, http_gzip,
                                                      http_ssi_filter, http_userid_filter,
                                                      每个模块的 ctx->preconfiguration()函数,负责添加
                                                      自己的var */
    ngx_array_t               *ports;
    ngx_uint_t                 try_files;       /* unsigned  try_files:1 */
    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];
} ngx_http_core_main_conf_t;


// ===========================   server conf ====================================

typedef struct {
    /* array of the ngx_http_server_name_t, "server_name" directive */
    ngx_array_t                 server_names;
    /* server ctx */
    ngx_http_conf_ctx_t        *ctx;
    ngx_str_t                   server_name;
    size_t                      connection_pool_size;
    size_t                      request_pool_size;
    size_t                      client_header_buffer_size;
    ngx_bufs_t                  large_client_header_buffers;
    ngx_msec_t                  client_header_timeout;
    ngx_flag_t                  ignore_invalid_headers;
    ngx_flag_t                  merge_slashes;
    ngx_flag_t                  underscores_in_headers;
    unsigned                    listen:1;

    #if (NGX_PCRE)
    unsigned                    captures:1;
    #endif
    ngx_http_core_loc_conf_t  **named_locations;
} ngx_http_core_srv_conf_t;


// ===========================   local conf ====================================

struct ngx_http_core_loc_conf_s {
    ngx_str_t     name;          /* location name */
#if (NGX_PCRE)
    ngx_http_regex_t  *regex;
#endif
    unsigned      noname:1;   /* "if () {}" block or limit_except */
    unsigned      lmt_excpt:1;
    unsigned      named:1;              // location 后面的路径
    unsigned      exact_match:1;        // location 的=号匹配(完全匹配)
    unsigned      noregex:1;            // 不按正则匹配
    unsigned      auto_redirect:1;
#if (NGX_HTTP_GZIP)
    unsigned      gzip_disable_msie6:2;
#if (NGX_HTTP_DEGRADATION)
    unsigned      gzip_disable_degradation:2;
#endif
#endif
    ngx_http_location_tree_node_t   *static_locations;
#if (NGX_PCRE)
    ngx_http_core_loc_conf_t       **regex_locations;
#endif
    /* pointer to the modules' loc_conf */
    void        **loc_conf;
    uint32_t      limit_except;
    void        **limit_except_loc_conf;
    ngx_http_handler_pt  handler;           /* 如果这个location 设置了upstream 转发,那么这个回调钩子就处理转发 */
    /* location name length for inclusive location with inherited alias */
    size_t        alias;
    ngx_str_t     root;                    /* root, alias */
    ngx_str_t     post_action;
    ngx_array_t  *root_lengths;
    ngx_array_t  *root_values;
    ngx_array_t  *types;
    ngx_hash_t    types_hash;
    ngx_str_t     default_type;
    off_t         client_max_body_size;    /* client_max_body_size */
    off_t         directio;                /* directio */
    off_t         directio_alignment;      /* directio_alignment */
    size_t        client_body_buffer_size; /* client_body_buffer_size */
    size_t        send_lowat;              /* send_lowat */
    size_t        postpone_output;         /* postpone_output */
    size_t        limit_rate;              /* limit_rate */
    size_t        limit_rate_after;        /* limit_rate_after */
    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
    size_t        read_ahead;              /* read_ahead */
    ngx_msec_t    client_body_timeout;     /* client_body_timeout */
    ngx_msec_t    send_timeout;            /* send_timeout */
    ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */
    ngx_msec_t    lingering_time;          /* lingering_time */
    ngx_msec_t    lingering_timeout;       /* lingering_timeout */
    ngx_msec_t    resolver_timeout;        /* resolver_timeout */
    ngx_resolver_t  *resolver;             /* resolver */
    time_t        keepalive_header;        /* keepalive_timeout */
    ngx_uint_t    keepalive_requests;      /* keepalive_requests */
    ngx_uint_t    keepalive_disable;       /* keepalive_disable */
    ngx_uint_t    satisfy;                 /* satisfy */
    ngx_uint_t    lingering_close;         /* lingering_close */
    ngx_uint_t    if_modified_since;       /* if_modified_since */
    ngx_uint_t    max_ranges;              /* max_ranges */
    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */
    ngx_flag_t    client_body_in_single_buffer;
                                           /* client_body_in_singe_buffer */
    ngx_flag_t    internal;                /* internal */
    ngx_flag_t    sendfile;                /* sendfile */
#if (NGX_HAVE_FILE_AIO)
    ngx_flag_t    aio;                     /* aio */
#endif
    ngx_flag_t    tcp_nopush;              /* tcp_nopush */
    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
    ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
    ngx_flag_t    port_in_redirect;        /* port_in_redirect */
    ngx_flag_t    msie_padding;            /* msie_padding */
    ngx_flag_t    msie_refresh;            /* msie_refresh */
    ngx_flag_t    log_not_found;           /* log_not_found */
    ngx_flag_t    log_subrequest;          /* log_subrequest */
    ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
    ngx_flag_t    server_tokens;           /* server_tokens */
    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */
    ngx_flag_t    etag;                    /* etag */
#if (NGX_HTTP_GZIP)
    ngx_flag_t    gzip_vary;               /* gzip_vary */
    ngx_uint_t    gzip_http_version;       /* gzip_http_version */
    ngx_uint_t    gzip_proxied;            /* gzip_proxied */
#if (NGX_PCRE)
    ngx_array_t  *gzip_disable;            /* gzip_disable */
#endif
#endif
#if (NGX_HAVE_OPENAT)
    ngx_uint_t    disable_symlinks;        /* disable_symlinks */
    ngx_http_complex_value_t  *disable_symlinks_from;
#endif
    ngx_array_t  *error_pages;             /* error_page */
    ngx_http_try_file_t    *try_files;     /* try_files */
    ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */
    ngx_open_file_cache_t  *open_file_cache;
    time_t        open_file_cache_valid;
    ngx_uint_t    open_file_cache_min_uses;
    ngx_flag_t    open_file_cache_errors;
    ngx_flag_t    open_file_cache_events;
    ngx_log_t    *error_log;
    ngx_uint_t    types_hash_max_size;
    ngx_uint_t    types_hash_bucket_size;
    ngx_queue_t  *locations;
};

ngx_http_log_conf:

// ===========================   main conf ====================================

typedef struct {
    ngx_array_t                 formats;    /* array of ngx_http_log_fmt_t */
    ngx_uint_t                  combined_used; /* unsigned  combined_used:1 */
} ngx_http_log_main_conf_t;


// ===========================   local conf ====================================

typedef struct {
    ngx_array_t                *logs;       /* array of ngx_http_log_t */
    ngx_open_file_cache_t      *open_file_cache;
    time_t                      open_file_cache_valid;
    ngx_uint_t                  open_file_cache_min_uses;
    ngx_uint_t                  off;        /* unsigned  off:1 */
} ngx_http_log_loc_conf_t;

ngx_http_upstream_conf:

// ===========================   main conf ====================================
typedef struct {
    ngx_hash_t                       headers_in_hash;
    ngx_array_t                      upstreams;
} ngx_http_upstream_main_conf_t;

ngx_http_autoindex_conf:

// ===========================   local conf ====================================
typedef struct {
    ngx_flag_t     enable;
    ngx_flag_t     localtime;
    ngx_flag_t     exact_size;
} ngx_http_autoindex_loc_conf_t;

ngx_http_gzip_static_conf:

// ===========================   local conf ====================================
typedef struct {
    ngx_uint_t  enable;
} ngx_http_gzip_static_conf_t;

ngx_http_index_conf:

// ===========================   local conf ====================================
typedef struct {
    ngx_array_t             *indices;    /* array of ngx_http_index_t */
    size_t                   max_index_len;
} ngx_http_index_loc_conf_t;

ngx_http_base_auth_conf:

// ===========================   local conf ====================================
typedef struct {
    ngx_http_complex_value_t  *realm;
    ngx_http_complex_value_t   user_file;
} ngx_http_auth_basic_loc_conf_t;

struct

ngx_str_t

struct define:

typedef struct {
    size_t      len;
    u_char     *data;
} ngx_str_t;

nginx string is not like normal C-str. Which is not end with ‘\0’. It’s controlled by len. So it can share memory sources bewteen sereval string objects.

ngx_list_t

struct define:

typedef struct ngx_list_part_s  ngx_list_part_t;

struct ngx_list_part_s {
    void             *elts;
    ngx_uint_t        nelts;
    ngx_list_part_t  *next;
};


typedef struct {
    ngx_list_part_t  *last;
    ngx_list_part_t   part;
    size_t            size;
    ngx_uint_t        nalloc;
    ngx_pool_t       *pool;     // memory pool object
} ngx_list_t;

The node of nginx list is array. It’s high performance for index item in it. The memory calloc picture like

_images/s_nginx_struct_list.png
ngx_table_elt_t

struct defined:

typedef struct {
    ngx_uint_t        hash;
    ngx_str_t         key;
    ngx_str_t         value;
    u_char           *lowcase_key;
} ngx_table_elt_t;

This struct is designed for http headers. Because header key is not case sensitive.

ngx_module_t

struct defined:

struct ngx_module_s {

    // this value represent the order of this moudle in one type of modules.
    // the value is important because it affect how proccess one request with much modules

    ngx_uint_t            ctx_index;

    // like up, but it's all modules

    ngx_uint_t            index;

    // remind and unused
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    ngx_uint_t            spare2;
    ngx_uint_t            spare3;

    // version of this module (it always 1)
    ngx_uint_t            version;

    // this value assign to context of this type of module
    // Because the different of each type module is great. So the content this value is different
    void                 *ctx;

    // this directive process config of nginx about this module
    ngx_command_t        *commands;

    // type of this module. which have 5 value:
    //      NGX_HTTP_MODULE
    //      NGX_CORE_MODULE
    //      NGX_CONF_MODULE
    //      NGX_EVENT_MODULE
    //      NGX_MAIL_MODULE
    ngx_uint_t            type;

    // method point. it will be called by nginx in different time


    // unused. always NULL
    ngx_int_t           (*init_master)(ngx_log_t *log);

    // it will be called in init of every module.(It will be called before fork worker process)
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);

    // it will be called in init of every worker process.
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);

    // unused. always NULL. (because nginx don't support thread model)
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);

    // like up
    void                (*exit_thread)(ngx_cycle_t *cycle);

    // it will be called before worker process exit
    void                (*exit_process)(ngx_cycle_t *cycle);

    // it will be called before master process exit
    void                (*exit_master)(ngx_cycle_t *cycle);


    // remind & unused
    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};
ngx_log_t

struct define:

struct ngx_log_s {
    ngx_uint_t           log_level;
    ngx_open_file_t     *file;

    ngx_atomic_uint_t    connection;

    ngx_log_handler_pt   handler;
    void                *data;

    /*
     * we declare "action" as "char *" because the actions are usually
     * the static strings and in the "u_char *" case we have to override
     * their types all the time
     */

    char                *action;
    ngx_log_t           *next;
};
ngx_pool_t

struct define:

struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt   handler;
    void                 *data;
    ngx_pool_cleanup_t   *next;
};


typedef struct ngx_pool_large_s  ngx_pool_large_t;

struct ngx_pool_large_s {
    ngx_pool_large_t     *next;
    void                 *alloc;
};


typedef struct {
    u_char               *last;
    u_char               *end;
    ngx_pool_t           *next;
    ngx_uint_t            failed;
} ngx_pool_data_t;


struct ngx_pool_s {
    ngx_pool_data_t       d;
    size_t                max;
    ngx_pool_t           *current;
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large;
    ngx_pool_cleanup_t   *cleanup;
    ngx_log_t            *log;
};

The relationship within them like that:

_images/s_nginx_struct_mempool.jpg

它的逻辑:

  • 没有局部free的方法,一般使用方式都是在一个pool上一点一点palloc, 等连接关闭时,对整个pool进行destory
  • 开辟内存小于max的时候,在pool->d这个内存链表里开辟(不停开辟block)
  • 开辟内存大于max的时候,在pool->large 这个内存链表上开辟(同上)
  • pool->cleanup 是内存池提供的回调函数,free的时候会依次调用,一般不需要处理它
  • pool->chain 目前还没使用

例子

ngx_buf_t

TODO

ngx_chain_t

TODO

ngx_hash_t

The reduced graph of hash struct is:

_images/s_nginx_struct_hash1.jpg

The detail graph of hash struct is:

_images/s_nginx_struct_hash2.jpg

FAQ

压缩指令对老IE浏览器支持的问题

老IE浏览器对 http Gzip 压缩功能的支持不太好, 所以为了避免怪异的问题,最好关掉对IE老浏览器的gzip功能:

gzip_disable "MSIE [1-6]\.";

IE老浏览器对gzip 支持不好,可能会体现出一下几个问题

  • 当页面中存在多个iframe, 并且这些iframe独立使用相同的javaScript脚本时,可能会出现脚本不运行或者运行不正常的情况. 这个 问题的发生与IE6在请求javascript脚本时共享资源有关
  • IE6通过各种方式从一个页面跳转到另一个页面,并且跳转页响应数据头部的cache-control头域设置了no-cache指令, 这也可能造成 javascript脚本不运行或者运行不正常. 该问题与IE6处理缓存数据有关
  • JavaScript 脚本中的汉字没有被正确编码导致解析失败, 因而造成脚本不运行或者运行不正常. 这种问题在首次加载页面运行脚本时 不会发生,因为IE6可以使用正确的编码对脚本中的汉字进行解析,使脚本正常,但当使用刷新功能时,由于IE6不会再记录上次使用 的编码规则,使得脚本中的汉字被解析成乱码
使用nginx防盗链

使用rewrite 和 valid_referers 指令,可以完成基本的防盗链:

location ~* ^.+\.(gif|jpg|png|swf|flv)$ {

    # 指定哪些referer被认为是valid的
    valid_referers blocked server_names *.abc.com;

    if ($invalid_referer) {
        # 禁止盗图
        rewrite ^/ http://abc.com/forbidden.jpg permanent;
    }
}
rewrite VS 重定向

nginx 的rewrite 指令,就是返回一个重定向。所以对于客户端,访问rewrite的url, 会导致两次请求。

在log中记录header

记录requests 的header使用 $http_<name> 变量, 记录response的header使用 $sent_http_<name> 变量, 如下:

underscores_in_headers on;
log_format STAT '[$time_local]`$http_x_up_calling_line_id`"$request"`"$http_user_agent"`$status`[$remote_addr]`$http_range`"$http_referer"`$request_time`$body_bytes_sent`$http_deviceid`$http_x_forwarded_for`$host`$http_cookie |token=`$http_HTTP_AUTHORIZATION`';

python库

urllib3

urllib3 是一个python版的http请求库,跟urllib比,它支持 文件上传, 重用连接retry 功能

调用例子:

import urllib3
http = urllib3.PoolManager()
r = http.request('GET', 'http://example.com/')
print r.status
print r.headers['server']

Warning

注意, urllib3 在创建TCP连接的时候,会默认把 Nagle 算法禁掉, 这也是http客户端的普遍做法

实现

urllib3 是对 httplib 的包装, 发请求的时候,拼凑http请求; 收到回复的时候,解析response; timeout 也是用httplib 库的timeout参数

_images/software_python-lib_urllib3_impleanment.png
协议

用urllib3 库发POST请求的时候, urllib3 使用multipart格式来传递参数, 所以如果传:

{'aaa': 'bbb',
 'ccc': 'ddd'}

则发出的请求是这样的格式:

_images/software_python-lib_urllib3_protocol.png

Warning

这样会造成请求包较大, 因为它把多个参数分在多个part里. requests 库没有这个问题

虽然requests 内部使用了urllib3, 但是requests 却不会用multipart 方式来拼凑post 请求体。 因为它不是直接调用urllib3的 send request 方法, 而是使用其内部的connection_from_host来获取conn, 自己拼凑post body. 也就是说,两个库拼凑body的方式 不一样.

numpy

numpy 是一个用c实现的python库,用来进行大规模数组、矩阵计算, 众所周知, python的循环迭代非常慢, 所以在进行大量计算时,可以使用numpy库来优化

相关地址: http://wiki.scipy.org/Tentative_NumPy_Tutorial

linux 笔记

bash

PS1 命令行提示符

在~/.bashrc中加入:

export PS1='\n\e[1;37m[\e[m\e[1;32m\u\e[m\e[1;33m@\e[m\e[1;35m\H\e[m \e[4m`pwd`\e[m\e[1;37m]\e[m\e[1;36m\e[m\n\$'

history 在多终端下的表现

  • There is the history in the history file.
  • There is the history in the memory of a bash process.
  • The history in the memory of one bash process is not synced with the history in the memory of any other bash process.
  • The history in the memory of a bash process is not synced with the history in the file, unless explicitly asked to or during some specific event (see below).
  • During startup bash will read the history file. The content of the history file is now in the memory of the bash process.
  • During normal use only the history in memory is manipulated.
  • During shutdown the history in memory is written to the history file, overwriting any previous content of the history file.
  • 可以在 ~/.bashrc 里,加入 shopt -s histappend, 来防止多terminator 相互覆盖.bash_history, 但是时间戳会乱

history 设置

  • HISTFILESIZE=2000
  • HISTSIZE=2000
  • HISTTIMEFORMAT=”%Y%m%d-%H:%M:%S: ” 每个操作,加上时间戳
  • export HISTTIMEFORMAT
  • shopt -s histappend

scripts example

log 跨机器传输(retry): example

计算字符串的md5值

在终端输入:

echo -n "863224020062594" | md5 | awk '{print $1}'

终端发送邮件

  1. 安装sendmail包

  2. 配置sendmail /etc/mail.rc:

    set from=<from@com> smtp=<stmp.com>
    set smtp-auth-user=<from@com> smtp-auth-password=<password> smtp-auth=login
    
  3. 启动sendmail服务:

    service sendmail start
    
  4. mail -s ‘this is test’ yourmail@com

tips

  • 要在远端最快启动一个静态文件服务器,用以下命令:

    python -m SimpleHTTPServer 7777
    
  • tcpdump 过滤抓包:

    sudo tcpdump -t -XX -i eth0 src host 10.32.30.138  and port 80
    
  • 在服务器上删除无用的log文件时, 最好使用 echo "" > access.log 代替 rm access.log, 因为该文件可能还在被进程不停的写,这时候删除了inode, 但磁盘空间并没有释放, 还需要用lsof来找问题.

基础

进程

  • 僵尸进程的目的,是子进程退出后,保留子进程的执行状态,以供父进程获取

  • 任何长系统调用(会block的), 都有可能被信号中断, 像CHILD这样的信号。所以要检测EINTR(打断信号) 信号, 重启该系统调用

  • 多进程 vs 多线程

    • 线程比进程更轻
    • 线程间同步、通信更容易
    • 进程更好隔绝问题,一个进程挂了不会影响其他进程, 而一个线程挂了,会导致这个进程内的所有线程都挂
    • 程间切换比进程间切换更轻;
    • 一个进程能打开的文件描述符是有上限的,如果有单进程多线程方式来写web server, 那么文件描述符资源就会是问题
  • linux 系统load的含义

    对于单核机器来说,load=1.0 表示cpu的使用率已满(进程调度器中, ready的进程为0, running的进程刚好占满了运行队列), 这种情况下, 再多执行一个进程,这个新进程都要等待CPU有空再分片到自己. 对于多核机器,是累加关系。双核CPU的满负载状态,load=2.0, 四核满负载load=4.0

  • 内核为每个进程维护着这个进程打开的文件表, 结构如下图

_images/linux_core_proc_fd.jpg
  • 为什么操作系统要分用户态和内核态

    由于操作系统和用户共享了计算机系统的硬件和软件,必须保证用户程序中的一个出错仅影响正在运行的程序。采用共享,许多进程可能会受到一个程序中的一个漏洞 (bug) 的不利影响。例如,如果一个进程陷入死循环,那么这个死循环可能会阻止很多其他进程 的正确操作。在多道程序设计中可能会发生更为微妙的错误,如一个错误的程序可能修改另一个程序、另一程序的数据,甚至操作系统本身。 如果没有保护来处理这些错误,那么计算机只能一次执行一个进程,否则所有输出都 值得怀疑。操作系统的合理设计必须确保错误程序(或恶意程序)不会造成其他程序执行错误。(用户空间不允许访问某些东西,如: 进程表)

文件系统

linux ext文件系统结构

_images/linux_fs_ext_arch.jpg

tip

  • 查看操作系统支持的文件系统
ls  -l  /lib/moudules/$(uname -r) /kernel/fs

防火墙

开一个端口

注意要用 -I, 因为如果 iptable 中设置了 reject ALL, 那么如果用-A 的话, reject 的优先级会在新的append之前:

$> sudo iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 9003 -j ACCEPT

保存配置

sudo iptables-save > /etc/iptables.up.rule

regex

advance regex:

grep --color -P 'proc_time": \d{4,}|proc_time": [4-9]{3,}' req.log

screen

screen 配置的不好,有可能导致vim花屏, screen 标准配置:

bind c screen 1
bind ^c screen 1
bind 0 select 10
escape ^Ee

# the following two lines give a two-line status, with the current window highlighted
hardstatus alwayslastline
hardstatus string '%{= kG}[%{G}%H%? %1`%?%{g}][%= %{= kw}%-w%{+b yk} %n*%t%?(%u)%? %{-}%+w %=%{g}][%{B}%m/%d %{W}%C%A%{g}]'

# # huge scrollback buffer
defscrollback 5000

# no welcome message
startup_message off

# 256 colors
attrcolor b ".I"
termcapinfo xterm 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm'
defbce on

# mouse tracking allows to switch region focus by clicking
# mousetrack on

# default windows
screen -t Shell1  1 bash

select 0
bind c screen 1 # window numbering starts at 1 not 0
bind 0 select 10

linux 调优

通用调优

ulimit 限制

一般一台机器,先用 ulimit -a 看一下,系统是否有限制资源使用, 例如:

> ulimit

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 93592
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1024
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
查看系统当前的并发连接数

使用netstat 命令查看连接数:

> netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"t",state[key]}'

    TIME_WAIT t 2727
    CLOSE_WAIT t 8
    SYN_SENT t 3
    FIN_WAIT1 t 56
    FIN_WAIT2 t 5915
    ESTABLISHED t 6813
    SYN_RECV t 87
    CLOSING t 6
    LAST_ACK t 130

查看网络状态:sar -n DEV 1 查看IO状态:iostat 1 查看cpu状态:mpstat -P ALL

内核调优

http server tuning
内核参数 推荐值
fs.file-max 4872754 一个进程可以打开的最大文件句柄数, 这个值直接影响最大并发量
net.ipv4.tcp_tw_reuse 1 TIME-WAIT 状态的套接字重用, http服务器会有大量TIME-WAIT状态socket
net.core.somaxconn 262144 listen 函数的backlog参数上限
net.ipv4.tcp_synack_retries 2 为了打开对端的连接,内核需要发送一个SYN并附带一个回应前面一个SYN的 ACK。也就是所谓三次握手中的第二次握手。这个设置决定了内核放弃连接之前发 送SYN+ACK包的数量。
net.ipv4.tcp_tw_recycle 0 启动 timewait 快速回收, 这个值打开会有一定的危险,还是关闭比较好, 而且处于timewait状态的socket占用内存很少,现在机器不差这点内存。并且有 tcp_max_tw_buckets 来限制总timewait数,唯一的缺点就是log里报: time wait bucket table overflow
net.ipv4.tcp_syn_retries 2 在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_fin_timeout 15 服务器一个socket 在FIN-WAIT2 状态维护的时间
net.ipv4.tcp_max_tw_buckets 18000 服务器TIME-WAIT状态套接字的数量限制,如果超过这个数量, 新来的TIME-WAIT套接字会直接释放 (过多的TIME-WAIT 套接字很影响服务器性能), 默认是180000, 太大了.过多的timewait 是浪费资源
net.ipv4.tcp_max_syn_backlog 181920 listen 函数的backlog参数
net.core.netdev_max_backlog 262144 当网卡接收包的速度大于内核处理速度,会有一个队列放这些包, 这个值是队列的最大值
net.ipv4.ip_local_port_range 1024 65535 定义了作为客户端, port的取值范围。 如果服务器要请求 其他server(作为客户端), 那么这个值还是有必要的
net.ipv4.tcp_syncookies 1 与性能无关,用于解决SYN攻击, 给每一个请求连接的IP地址分配一个 Cookie,如果短时间内连续受到某个IP的重复SYN报文,就认定是受到了攻击, 以后从这个IP地址来的包会被一概丢弃。
net.ipv4.tcp_keepalive_time 600 保活定时器的间隔, 时间约短,server回收资源越快, 每个alive最大 探测时间= (tcp_keepalive_intvl * tcp_keepalive_probes)
net.ipv4.tcp_keepalive_intvl 30
net.ipv4.tcp_keepalive_probes 3 如果HTTP服务开启了keepalive, 这个参数就起作用了. server每两小时发送 一次 探测包,如果这个包没有回应,这个参数指定重试多少次, 默认是9 (太多)
net.core.rmem_max 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.core.rmem_default 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.ipv4.tcp_rmem 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.ipv4.udp_rmem_min 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.core.wmem_max 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.core.wmem_default 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.ipv4.tcp_wmem 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
net.ipv4.udp_wmem_min 这个值在一定程度上影响了并发量,但需要根据具体业务来调整
其他
内核参数 推荐值
net.ipv4.tcp_abort_on_overflow 这个值决定了系统调用listen中backlog参数的作用。默认这个值为0,所以当 backlog队列已满时,新来的SYN请求, server不予理会,那么client会重发SYN, 那时backlog队列也许已经恢复了。 如果这个值设为1, 那么当backlog满的时候 新来的SYN, 服务器会直接返回RST, 导致”Connection reset by peer”

高并发配置模板

  • :net.ipv4.tcp_no_metrics_save = 1 默认情况下一个tcp连接关闭后,把这个连接曾经有的参数比如慢启动门限snd_sthresh,拥塞窗口snd_cwnd 还有srtt等信息保存到dst_entry中, 只要dst_entry 没有失效,下次新建立相同连接的时候就可以使用保存的参数来初始化这个连接.通常情况下是关闭的。
  • :net.unix.max_dgram_qlen = 1024 unix socket 最大队列,默认为10, 明显不够

其他

socket 连接中,打开TCP_DEFER_ACCEPT 选项

oh-my-zsh

好用的插件

要启用zsh的插件, 在~/.zshrc 中设置:

plugins=(git autojump last-working-dir tmux tmuxinator web-search fancy-ctrl-z themes)
  • autojump
  • last-working-dir
  • tmux
  • tmuxinator
  • web-search
  • fancy-ctrl-z

升级oh-my-zsh

要升级oh-my-zsh, 终端输入:

> upgrade_oh_my_zsh

算法

哈希算法

给一个value, 通过hash算法,算出一个固定长度的hash值,就是hash算法. 也就是:

hash_func(value) = hash_value

hash 算法的目标是 不可逆, 无冲突, 但这从数学上来说是不可能的。牛逼的hash算法,只是让逆向算法的难度提升到目前计算机无法在百年内算出来, 并且很难造出冲突的两个value。

Note

王小云教授曾经成功制造出MD5的碰撞,即md5(a) = md5(b)。这样的碰撞只能随机生成,并不能根据一个已知的a求出b(即并没有破坏MD5的无冲突特性)。但这已经让他声名大噪了。

常见算法

一个数组求最大最小值

  1. Pick 2 elements(a, b), compare them. (say a > b)
  2. Update min by comparing (min, b)
  3. Update max by comparing (max, a)

This way you would do 3 comparisons for 2 elements, amounting to 3N/2 total comparisons for N elements.

找出海量数据最大的前10个数

使用最小堆, 遍历海量数组的同时,构造一个最小二叉树。这个二叉树有以下特点:

  • 根的数最小
  • 根左边的元素,全部比根小;根右边的元素,全部比根大

每次遍历到一个数, 先用它和根相比。如果比根小,pass. 如果比根大,那么把它加入根. 这样的话,越往后,遍历一个数越快(因为最小堆越来越大), 详情见: http://www.benfrederickson.com/heap-visualization/

思考

变的比他强大,强大到不用在乎

感悟

用户反馈时间戳错乱

尝试使用mongoengine 的 singal 机制:

@classmethod
def pre_save(cls, sender, document, **kwargs):
    if not '_id' in document or document._created:
        timestamp = UTILS.current()
        today = UTILS.today()
        document.created_at = timestamp
        document.updated_at = timestamp
        document.date = today
        if document.content:
            document.content = document.content.strip()

对mongoengine document对象的_created 变量没有过多了解,只是按字面意思理解, 导致 所有save() 操作都会更新时间戳. 感悟:: 1. 事情对一个变量不了解,那么它很可能引发大得BUG, 不管这种可能性有多小, 它总会发生, 而且在最糟糕的时候发生 2. 数据一定要备份

箴言

  • 再好的苍蝇,还是苍蝇
  • 如何发自内心的原谅一个人?变的比他强大,强大到不用在乎
  • 如果你不知道去哪, 路上的石头就不能算障碍,只有挡了你的道,它才是障碍
  • 你必须非常努力, 才能看起来毫不费力
  • 模仿,是最真诚的赞美
  • 陌生人不花钱在你身上,你当然可以理解。但陌生人不花时间在你身上跟你讨论事情,你可能无法理解 ... 可认真思考一下,时间比钱更宝贵,毕竟生命有限。陌生人都不愿意花钱在我身上了,当然更不应该花时间在我身上

读书笔记

互联网思维:

  • 以用户为中心, 像小米一样的用户反馈, 多思考消费者(行为、兴趣、分组)
  • “品牌”的打造很重要,并分一朝一夕。产品一旦发布,每一个行为,都影响用户心中的品牌
  • 好产品是关键

静心堂会议室。 有很多开站立会议,报着5分钟十分钟搞定的态度开会, 并把理解寄托给队友, 这种会开完其实没有人懂了。静心会议室,至少三十分钟的会才能进去,如果抱着五分钟搞定的态度进来,请滚出去。

黑客与画家

  • 代码就像画画.画一幅好画,没有必要弄清楚每种颜料的原理,写好的代码,没有必要弄懂计算机的原理。
  • 对大公司来说不是问题,因为生产特别优秀的产品不是它们的获胜手段。大公司只要做到不太烂,就能赢。
  • 大不同才能小创新。
  • 我们在Viaweb举办过一个比赛,看谁能说出我们软件中最差劲的地方。两位客服人员并列第一
  • 向一个项目增加人手,往往会拖慢项目进程。随着参与人数的增加,人与人之间需要的沟通呈现指数式增长。人数越来越多,产生的bug也越多越多。
  • 有钱的客户倾向于更贵的选择,即使便宜的选择更符合他们的需要,他们也不会买
  • 首先,管理企业其实很简单,只要记住两点就可以了:做出用户喜欢的产品,保证开支小于收入。只要做到这两点,你就会超过大多数创业公司。
  • 创业就是压缩
  • 要致富,你需要两样东西:可测量性和可放大性。
  • 乔布斯曾经说过,创业的成败取决于最早加入公司的那十个人。
  • 你努力30倍,最后得到的回报在现实中并不是30倍,而是0到1000倍之间的一个随机数。
  • 大多数时候,促成买方掏钱的最好办法不是让买家看到有获利的可能,而是让他们感到失去机会的恐惧
  • 对速度的追求是人类内心深处根深蒂固的欲望。当你看着计算机这个小玩意,就会不由自主地希望程序运行得越快越好,真的要下一番功夫才能把这种欲望克制住。设计编程语言的时候,我们应该有意识地问自己,什么时候可以放弃一些性能,换来一点点便利性的提高。

google 时代工作方法

  • 让信息尽可能快地离开大脑。
  • 多重任务通常会让你降低效率。
  • 进行组织安排时,要绕开的是实际制约而不是假性制约。
  • 我每周都会安排时间阅读本周所有的会议笔记

一万小时天才理论

  • 天才 = (10000 小时 X 精深联系) * 激情(兴趣)
  • 精神练习要连续化, 练习打球,而不要一个动作、挥球、击球,一个动作一个动作去练习

魔鬼搭讪学

  • 因为我知道即使追到了其实也留不住多久,我只是做一个男人在年轻时该做的事情,真正让我恐慌的不是眼前的美女而是流逝的时间。
  • 其实,这跟我一贯强调的说真话原则也是一致的。扪心自问,我走过去搭讪,这跟她是哪里人、干什么工作、学什么专业根本就没有关系,我只是觉得她看着顺眼,我只关心此刻能跟她相处的时间,至于她的爱好和履历那都是以后的事情。
  • 搭讪泡妞是一个做减法的过程,首先要认识了很多对象,然后再把其中对你不来电的拣出来筛掉,剩下的才是你美好生活的开始

害羞心理学

  • 把自己的害羞置于一个心理学手术台上, 以一个外科医生冷静而超然的态度面对它
  • 阅读好的报纸,电影,然后写影评
  • 能够放得开的人,学东西越快
  • 害羞的孩子,总是害怕做错任何事情

如何进行关键对话

  • 当我们面对重要问题保持沉默时,我们的生活便开始上演悲剧了
  • 在很多情况下,当上司或老板固执己见时,表现得高度自信时,人们往往倾向于抑制内心的真实想法,不敢冒犯具有权威的上级
  • 为了吸引对方的注意力, 我们总是故作冷淡(这种思维方式简直是奇哉怪也!)
  • 如果总是通过转移目标或掩饰问题的方式来营造虚伪的安全感时, 这种策略只能逃避真正的问题,永远不会真正解决问题
  • 找到了共同目的, 你就有合理的理由和健康的气氛展开话题了
  • 应当学会使用更准确的词汇来形容情绪和感受

哈佛凌晨四点半

  • 尽力而为 与 竭尽全力 是存在差别的
  • 那些能做成大事的人, 从来不会拒绝做小事
  • 自信的人往往会看到好的一面, 并且会尽快找到解决问题的方法;不自信的人往往看到不好的一面,总是将细小的问题最大化
  • 不要人云亦云
  • 与其什么都不知道总以为自己是正确的,不如有些错误的想法

少有人走的路

  • 父母的爱心至关重要, 即使家庭生活混乱, 倘若有爱存在, 照样可以培养出懂得自律的孩子
  • 患有人格失调的父母, 为孩子树立的是反面的榜样。他们的病情,也会影响到婚姻、交友和事业。他们不肯担负自己的责任, 导致人生问题重重
  • 一旦新的资讯与过去的观念发生冲突,需要对地图大幅度修正,我们就会感到恐惧,宁可对新的资讯视而不见。我们的态度也变得 相当奇特—-不只是被动抗拒新的资讯,甚至指责新的资讯混淆是非,说它们是异端邪说。 我们话费大量时间和精力,去捍卫 过去而陈腐的观念,却不去考虑如何更新旧的地图,这是多么可悲的事情啊!
  • 和原始人相比,现代人已经发生诸多的变化,这说明我们完全可以在一定程度上,违背与生俱来的本性,发展第二天性。 人之为人,或许就在于我们可以超越本性
  • 放弃人生的某些东西, 一定会给心灵带来痛苦。 不管是谁,经历人生的急转弯,都必须放弃某些快乐, 放弃属于自己的一部分. 回避放弃只有一个办法,那就是永远停留在原地,不让双脚踏上旅途
  • 真正的爱是自由的选择。真正相爱的人,不一定非要生活在一起,充其量只是选择一起生活罢了.
  • 真爱的本质,就是希望对方拥有独立自主的人格。
  • 敢于追求独自主,本身就是自尊自爱的体现。我尊重自己,才不愿得过且过
  • 不要喜欢教育别人 真正爱别人,就会承认对方和自己是不同的、完全独立的个体。基于这样的认识, 我们就不会轻易对他人说: “我是对的,你是错的”
  • 传统的观念认为: 友谊意味着永不冲突,甚至意味着互相吹捧。 这样的友谊实际弱不经风, 它也不配称为友谊。 友谊需以爱为出发点, 适当的职责和批评是必不可少的, 帮助对方达到心智成熟
  • 追求心智成熟, 不得不忍受痛苦,从童年的自我进入成年的自我,不得不摆脱孩子的身份,转而称为称职的父母.

如何阅读一本书

_images/mind_note_how_to_read.png

零成本创业


The writer give some suggestions for entrepreneur base on his experience. Instead of giving some actural suggestion, this book tell us what mind or character a entprepreneur should be take. It tell us that we should use boss’s mind to slove problem instead of employee’s.

  • Entrepreneur find market first. Before thinking about funds, they must find out what to sell and how to sell. Funds is used to expand scale of business
  • If you are willing to gathering a clique(呼朋引伴), feeling security behind people, then you are not suitable for entrepreneurship. Because entrepreneur must be take a risk by himself. This risk cannot transfer to another.
  • Everything is matter of negotiation(谈判). The question is whether you have try.
  • Usually only two kind of entrepreneur will be successful. The one focus on Product and the one foucs on Customer.
  • We have read a book named 《Customer is Second》. Inside there is a word make me shock. It said: "Who will buy it even if your employee do not willing to buy it.
  • "你不是不适应台北的生活,而是不适应一个人关在台北宿舍的生活。 那是坐牢,不是台北生活", 很多人有勇气去做某件事,却 不见得有勇气改变自己的生活
  • The engineer I met usually relapsed into(陷入) computer. They contact to anthor rarely and most of them are uncommunicative. They cannot express their mind and don't have sense of organization. If they can overcome it, they will own high achievement(成就).
  • Relationship between entrepreneur and marking. If we are a dog food company. Our consumer is Woman, cosutomer is Dog, and buyer is Husband. Producting is making perfect production and Marking is find Woman who feed dog.
Warning!

Tools

工具 作用
cheatsheet Dold the command key, it will show all shortcuts in current application
omnifocus 高效的GTD任务管理工具
pomotodo Mac下比较好用的番茄钟
paste 一款复制粘特神器
广告终结者 浏览器广告插件
jq 终端命令行工具,解析json
fluidapp

一款能把页面打包成app的应用,把打包的页面生成一个app,放在Dock上.

例如: 把Gmail打包

trello
团队任务协作平台,类似于github的可视化issue页面. 有以下功能:
  • 把task assign to 某人
  • 添加comments
  • label 一个任务的紧急度
apiblueprint 一种近似markdown的api文档编辑格式, 它只是一种格式,基于这种格式 把apib文件渲染成html的工具有 aglio.