1、nginx原理
模块:核心模块、基础模块、第三方模块
handlers、filters、proxies
将客户的请求转location,然后conf配置文件中确定加载什么模块。
进程:master、worker
master主进程接收信号,转发给worker进程处理。重启worker进程,监控worker
kill -HUP pid master从容重启进程,服务不中断。
master接到信号--》加载配置文件--》重启worker--》发信号关闭老worker进程
请求处理过程:
master创建listen的socket--》fork出worker进程
worker抢占accept_mutex锁--》注册listenfd读事件--》accept链接--》读取请求--》解析请求--》处理请求--》产生数据返回客户端--》断开连接
nginx+php-fpm工作过程:
1、FastCGI进程管理器php-fpm进程初始化
2、php-fpm启动主进程
3、php-fpm启动stat_server个CGI子进程
4、php-fpm主进程管理子进程,监听9000端口
5、php-fpm子进程等待链接
6、nginx接到客户的请求,通过location将php后缀文件转发给127.0.0.1:9000来处理
7、php-fpm选择链接一个子CGI解析器
8、nginx将环境变量和标准输出发送给CGI解析器
9、CGI子进程处理完后将标准输出和错误原链接返回。
10、CGI子进程关闭连接,完成请求
11、CGI子进程等待处理下一个请求。
负载均衡:
upstream {
server:192.168.1.1 wight:2
}
策略:round,ip-hash,constant-hash,least-conn,least-time
心跳检测:keepalive nginx_upstream_check_module模块
重试:upstream server、proxy_pass
2、apache原理
prefork和worker两种模式:http://blog.csdn.net/hguisu/article/details/7395181
prefork模式:控制进程在最初建立“StartServers”个子进程后,为了满足MinSpareServers设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足MinSpareServers设置的值为止。这就是预派生(prefork)的由来。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。
worker模式:由主控制进程生成“StartServers”个子进程,每个子进程中包含固定的ThreadsPerChild线程数,各个线程独立地处理请求。同样,为了不在请求到来时再生成线程,MinSpareThreads和MaxSpareThreads设置了最少和最多的空闲线程数;而MaxClients设置了所有子进程中的线程总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。
按需加载模块
3、memcache原理:
多路复用I/O:消息通知模式,只有在用户连接做好I/O准备才会通知
多线程:线程数一般设置为CPU核数,同时处理多个请求。
内存分配方式:slab,page,chuck
Memcached并不是直接根据需要记录的数据的大小来直接分配相应大小的内存。在一条新的记录到来时,Memcached会首先检查该记录 的大小,并根据记录的大小选择记录所需要存储到的Slab类型
一个需要考虑的特殊情况就是对记录的更新。在对一个记录进行更新的时候,记录的大小可能会发生变化。在这种情况下,其所对应的Slab类型也可能会发生变化。因此在更新时,记录在内存中的位置可能会发生变化。只不过从用户的角度来说,这并不可见
memcache渗透性:返回NULL
cas:检查并设置:
$ips
=
$m
->get(
'ip_block'
, null,
$cas
);
$m
->cas(
$cas
,
'ip_block'
,
$ips
);
4、redis原理
sds:
struct sdshdr {
len = 11;
free = 0;
buf = "hello world\0"; // buf 的实际长度为 len + 1
};
list:双端链表
typedef struct listNode {
// 前驱节点
struct listNode *prev;
// 后继节点
struct listNode *next;
// 值
void *value;
} listNode;
typedef struct list {
// 表头指针
listNode *head;
// 表尾指针
listNode *tail;
// 节点数量
unsigned long len;
// 复制函数
void *(*dup)(void *ptr);
// 释放函数
void (*free)(void *ptr);
// 比对函数
int (*match)(void *ptr, void *key);
} list;
hash:字典和压缩列表(
REDIS_ENCODING_ZIPLIST
和
REDIS_ENCODING_HT
)
/*
* 哈希表
*/
typedef struct dictht {
// 哈希表节点指针数组(俗称桶,bucket)
dictEntry **table;
// 指针数组的大小
unsigned long size;
// 指针数组的长度掩码,用于计算索引值
unsigned long sizemask;
// 哈希表现有的节点数量
unsigned long used;
} dictht;
/*
* 哈希表节点
*/
typedef struct dictEntry {
// 键
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 链往后继节点
struct dictEntry *next;
} dictEntry;
- Redis 字典的底层实现为哈希表,每个字典使用两个哈希表,一般情况下只使用 0 号哈希表,只有在 rehash 进行时,才会同时使用 0 号和 1 号哈希表。
- 哈希表使用链地址法来解决键冲突的问题。
从图中可以看到, 跳跃表主要由以下部分构成:
- 表头(head):负责维护跳跃表的节点指针。
- 跳跃表节点:保存着元素值,以及多个层。
- 层:保存着指向其他元素的指针。高层的指针越过的元素数量大于等于低层的指针,为了提高查找的效率,程序总是从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。
- 表尾:全部由
NULL
组成,表示跳跃表的末尾。
因为跳跃表的定义可以在任何一本算法或数据结构的书中找到, 所以本章不介绍跳跃表的具体实现方式或者具体的算法, 而只介绍跳跃表在 Redis 的应用、核心数据结构和 API 。
set:intset和字典(REDIS_ENCODING_INTSET
和 REDIS_ENCODING_HT
)
压缩列表:
area |<---- ziplist header ---->|<----------- entries ------------->|<-end->|
size 4 bytes 4 bytes 2 bytes ? ? ? ? 1 byte
+---------+--------+-------+--------+--------+--------+--------+-------+
component | zlbytes | zltail | zllen | entry1 | entry2 | ... | entryN | zlend |
+---------+--------+-------+--------+--------+--------+--------+-------+
^ ^ ^
address | | |
ZIPLIST_ENTRY_HEAD | ZIPLIST_ENTRY_END
|
ZIPLIST_ENTRY_TAIL