nginx
nginx可以作为网关(负载均衡,路由规则,限流,访问控制),缓存服务器,web服务器(可以使用lua开发,openresty框架),反向代理服务器。 支持http,https,http/2,grpc,tcp,udp,websocket协议。
源码结构
src/core (底层核心结构)
启动入口和基础的数据结构和算法。 nginx.c就是入口,初始化,启动多进程。
数据结构 ngx_array ngx_buf ngx_conf_flie ngx_connection ngx_core ngx_file ngx_list ngx_log ngx_module ngx_open_file_cache ngx_parse_time ngx_inet ngx_queue ngx_radixtree ngx_rbtree ngx_resolver ngx_rwlock ngx_shmtx ngx_sha1 ngx_slab ngx_spinlock ngx_times ngx_thread_pool 可以分析出来数据结构分成几类: 一类是通用数据结构,类似c++的stl 一种是网络文件锁相关的结构 一种是内存池线程池相关的结构 还有其他的基数树,红黑树,日志模块等等。
算法 ngx_crc ngx_crc32 ngx_crypt ngx_hash ngx_md5 ngx_regex 都是一些通用算法加解密,哈希,正则,差错校验
src/os(底层封装操作系统相关)
都是一些glibc提供的接口封装,包括 内存分配,原子操作,网络通信,动态库dlopen等(模块功能核心),文件系统,进程线程管理,共享内存等等。
src/event (高性能事件相关)
封装了epoll,定时器,管道,openssl等等
src/http (http模块相关)
就是http协议的实现
src/mail (邮件协议相关)
邮件协议的实现,主要就是通知管理员的作用
src/stream (stream相关)
stream的实现,也就是处理四层协议
src/misc
测试和性能分析相关
实现细节
进程模型
main函数初始化时间,日志,池资源,slab,crc32,信号,pid文件等等之后 执行ngx_master_process_cycle开始执行启动master和worker的流程。
初始化关键函数:ngx_init_cycle 每个工作进程的核心数据结构:ngx_cycle_t
ngx_start_worker_processes->ngx_spawn_process启动工作线程,工作线程初始化socket通信管道后进入自己的循环流程ngx_worker_process_cycle。
1.主进程之后如果需要的话会初始化缓存进程,然后进入监控其他进程的循环。
2.工作线程接下来需要执行的就是模块初始化
核心模块
nginx核心功能实现都在模块里面,所以主要关注点在模块。
ngx_core_module是核心数据结构,初始化模块,模块指令模块上下文都在这里。 ngx_core_module->ngx_core_commands->load_module则是加载其他模块的入口。
其他子模块的核心模块: ngx_event_core_module ngx_http_core_module ngx_mail_core_module ngx_stream_core_module
http模块
handler:负责 处理请求并返回 核心函数:ngx_create_listening,ngx_http_init_connection,ngx_http_wait_request_handler ,ngx_http_handler
filter:过滤,流量控制 核心函数:ngx_http_top_header_filter,ngx_http_top_body_filter,ngx_http_top_request_body_filter
upstream:反向代理 核心函数:ngx_http_upstream_init,ngx_http_upstream_add,ngx_http_upstream_bind_set_slot
load balance:负载均衡 核心函数:根据配置选择对应的负载均衡模块 ngx_http_upstream_hash_module,ngx_http_upstream_random_module ngx_http_upstream_ip_hash_module,ngx_http_upstream_least_conn_module
内存池模型
每个连接分配自己的内存池,连接断开,统一回收。
进程间通信和同步模型
进程间通信使用了: 共享内存(一般使用mmap,也有使用shmget的):封装接口 ngx_shm_alloc 信号:ngx_signal_handler处理信号 套接字:ngx_channel_t封装了使用socketpair生成socket对,作为父子进程间通信方案。
同步使用了: 原子操作:ngx_atoymic_cmp_set,原子操作还作为自旋锁ngx_spinlock的底层实现。 信号量:ngx_shmtx_create封装了sem_init,信号量可能会使进程睡眠,ngx_shmtx_lock底层就是信号量实现。 文件锁:ngx_trylock_fd,使用fcntl实现,F_SETLK(会返回失败)和F_SETLKW(会一直获取直到成功)。其中第三个参数flock就是文件锁结构体,可以精确锁住文件的一部分内容。
惊群问题
accept惊群 linux4.5版本以后设置epoll的标志位EPOLLEXCLUSIVE可以解决,SO_REUSEPORT也可以。
旧版本可以使用accept_mutex,但是会引起cpu负载不均衡问题和性能下降问题。
accept_mutex
accept_mutex原理是同一时间内只运行一个进程listen这个端口的套接字。 需要注意这个锁是进程锁,需要放在共享内存里。 ngx_shmtx_trylock加锁,使用cas,将自己的进程号作为交互标志。 没有拿到锁的进程会进入等待事件,时间是ngx_accept_mutex_delay 处理好accept连接后通过ngx_event_process_posted发布事件到ngx_posted_accept_events(accept接收队列) 然后释放锁,并使用ngx_shmtx_wakeup唤醒其他进程(信号量实现) 然后发布事件到ngx_posted_events(其他事件多列)
openresty
在nginx基础上加入数据库缓存加解密中间件等lua库,可扩展性变得很强,迭代速度快。通过nginx模块的方式加入nginx,对原生nginx不需要做代码改造,可维护性好。
核心组成:nginx,luajit,ngx_lua(http_lua),stream_lua
直接在配置文件里写lua代码
luajit
传统c和lua交互方式
使用栈
c和luajit交互
使用ffi库可以直接调用c函数,还可以调用so动态库
图解
调用自己的lua代码原理:模块中预留了接口或者hook点,其实就是函数指针,执行到这些点就会读取配置里的代码编译执行字节码或者直接执行机器码。
参数优化
fs.file-max = 999999 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.ip_local_port_range = 1024 61000 net.ipv4.tcp_rmem = 4096 32768 262142 net.ipv4.tcp_wmem = 4096 32768 262142 net.core.netdev_max_backlog = 8096 net.core.rmem_default = 262144 net.core.wmem_default = 262144 net.core.rmem_max = 2097152 net.core.wmem_max = 2097152 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn.backlog=1024
|