點擊上方 IT牧場 ,選擇 置頂或者星標技術乾貨每日送達!
Nginx全程是什麼?Nginx ("engine x") 是一個高性能的 HTTP 和 反向代理 服務器,也是一個 IMAP/POP3/SMTP 代理服務器。
daemon守護線程
驚羣現象
相對於線程,採用進程的優點
進程之間不共享資源,不需要加鎖,所以省掉了鎖帶來的開銷。
採用獨立的進程,可以讓互相之間不會影響,一個進程退出後,其它進程還在工作,服務不會中斷,master進程則很快重新啓動新的worker進程。
編程上更加容易。
多線程的問題
而多線程在多併發情況下,線程的內存佔用大,線程上下文切換造成CPU大量的開銷。想想apache的常用工作方式(apache也有異步非阻塞版本,但因其與自帶某些模塊衝突,所以不常用),每個請求會獨佔一個工作線程,當併發數上到幾千時,就同時有幾千的線程在處理請求了。這對操作系統來說,是個不小的挑戰,線程帶來的內存佔用非常大,線程的上下文切換帶來的cpu開銷很大,自然性能就上不去了,而這些開銷完全是沒有意義的。
異步非阻塞
-
不需要創建線程,每個請求只佔用少量的內存 -
沒有上下文切換,事件處理非常輕量
connection
連接過程
struct ngx_connection_s {
void *data;
ngx_event_t *read;
ngx_event_t *write;
ngx_socket_t fd;
ngx_recv_pt recv;
ngx_send_pt send;
ngx_recv_chain_pt recv_chain;
ngx_send_chain_pt send_chain;
ngx_listening_t *listening;
off_t sent;
ngx_log_t *log;
ngx_pool_t *pool;
struct sockaddr *sockaddr;
socklen_t socklen;
ngx_str_t addr_text;
ngx_ssl_connection_t *ssl;
struct sockaddr *local_sockaddr;
ngx_buf_t *buffer;
ngx_queue_t queue;
ngx_atomic_uint_t number;
ngx_uint_t requests;
unsigned buffered:8;
unsigned log_error:3; /* ngx_connection_log_error_e */
unsigned unexpected_eof:1;
unsigned timedout:1;
unsigned error:1;
unsigned destroyed:1;
unsigned idle:1;
unsigned reusable:1;
unsigned close:1;
unsigned sendfile:1;
unsigned sndlowat:1;
unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */
unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */
unsigned accept_context_updated:1;
unsigned aio_sendfile:1;
ngx_buf_t *busy_sendfile;
ngx_atomic_t lock;
};
連接池
競爭問題
ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;
//可以看出來隨着空餘連接的增加,disabled的值降低
if (ngx_use_accept_mutex) {
if (ngx_accept_disabled > 0) { //當disabled的值大於0時,禁止競爭,但每次-1
ngx_accept_disabled--;
} else {
if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
return;
}
if (ngx_accept_mutex_held) {
flags |= NGX_POST_EVENTS;
} else {
if (timer == NGX_TIMER_INFINITE
|| timer > ngx_accept_mutex_delay) {
timer = ngx_accept_mutex_delay;
}
}
}}
request
struct ngx_http_request_s {
uint32_t signature; /* "HTTP" */
ngx_connection_t *connection;
void **ctx;
void **main_conf;
void **srv_conf;
void **loc_conf;
ngx_http_event_handler_pt read_event_handler;
ngx_http_event_handler_pt write_event_handler;
ngx_http_cache_t *cache;
ngx_http_upstream_t *upstream;
ngx_array_t *upstream_states;
/* of ngx_http_upstream_state_t */
ngx_pool_t *pool;
ngx_buf_t *header_in;
ngx_http_headers_in_t headers_in;
ngx_http_headers_out_t headers_out;
ngx_http_request_body_t *request_body;
time_t lingering_time;
time_t start_sec;
ngx_msec_t start_msec;
ngx_uint_t method;
ngx_uint_t http_version;
ngx_str_t request_line;
ngx_str_t uri;
ngx_str_t args;
ngx_str_t exten;
ngx_str_t unparsed_uri;
ngx_str_t method_name;
ngx_str_t http_protocol;
ngx_chain_t *out;
ngx_http_request_t *main;
ngx_http_request_t *parent;
ngx_http_postponed_request_t *postponed;
ngx_http_post_subrequest_t *post_subrequest;
ngx_http_posted_request_t *posted_requests;
ngx_int_t phase_handler;
ngx_http_handler_pt content_handler;
ngx_uint_t access_code;
ngx_http_variable_value_t *variables;
ngx_uint_t ncaptures;
int *captures;
u_char *captures_data;
size_t limit_rate;
/* used to learn the Apache compatible response length without a header */
size_t header_size;
off_t request_length;
ngx_uint_t err_status;
ngx_http_connection_t *http_connection;
ngx_http_spdy_stream_t *spdy_stream;
ngx_http_log_handler_pt log_handler;
ngx_http_cleanup_t *cleanup;
unsigned subrequests:8;
unsigned count:8;
unsigned blocked:8;
unsigned aio:1;
unsigned http_state:4;
/* URI with "/." and on Win32 with "//" */
unsigned complex_uri:1;
/* URI with "%" */
unsigned quoted_uri:1;
/* URI with "+" */
unsigned plus_in_uri:1;
/* URI with " " */
unsigned space_in_uri:1;
unsigned invalid_header:1;
unsigned add_uri_to_alias:1;
unsigned valid_location:1;
unsigned valid_unparsed_uri:1;
unsigned uri_changed:1;
unsigned uri_changes:4;
unsigned request_body_in_single_buf:1;
unsigned request_body_in_file_only:1;
unsigned request_body_in_persistent_file:1;
unsigned request_body_in_clean_file:1;
unsigned request_body_file_group_access:1;
unsigned request_body_file_log_level:3;
unsigned subrequest_in_memory:1;
unsigned waited:1;
unsigned cached:1;
unsigned gzip_tested:1;
unsigned gzip_ok:1;
unsigned gzip_vary:1;
unsigned proxy:1;
unsigned bypass_cache:1;
unsigned no_cache:1;
/*
* instead of using the request context data in
* ngx_http_limit_conn_module and ngx_http_limit_req_module
* we use the single bits in the request structure
*/
unsigned limit_conn_set:1;
unsigned limit_req_set:1;
unsigned cacheable:1;
unsigned pipeline:1;
unsigned chunked:1;
unsigned header_only:1;
unsigned keepalive:1;
unsigned lingering_close:1;
unsigned discard_body:1;
unsigned internal:1;
unsigned error_page:1;
unsigned ignore_content_encoding:1;
unsigned filter_finalize:1;
unsigned post_action:1;
unsigned request_complete:1;
unsigned request_output:1;
unsigned header_sent:1;
unsigned expect_tested:1;
unsigned root_tested:1;
unsigned done:1;
unsigned logged:1;
unsigned buffered:4;
unsigned main_filter_need_in_memory:1;
unsigned filter_need_in_memory:1;
unsigned filter_need_temporary:1;
unsigned allow_ranges:1;
unsigned stat_reading:1;
unsigned stat_writing:1;
/* used to parse HTTP headers */
ngx_uint_t state;
ngx_uint_t header_hash;
ngx_uint_t lowcase_index;
u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN];
u_char *header_name_start;
u_char *header_name_end;
u_char *header_start;
u_char *header_end;
/*
* a memory that can be reused after parsing a request line
* via ngx_http_ephemeral_t
*/
u_char *uri_start;
u_char *uri_end;
u_char *uri_ext;
u_char *args_start;
u_char *request_start;
u_char *request_end;
u_char *method_end;
u_char *schema_start;
u_char *schema_end;
u_char *host_start;
u_char *host_end;
u_char *port_start;
u_char *port_end;
unsigned http_minor:16;
unsigned http_major:16;
};
HTTP
處理流程
-
初始化HTTP Request(讀取來自客戶端的數據,生成HTTP Requst對象,該對象含有該請求所有的信息)。 -
處理請求頭。 -
處理請求體。 -
如果有的話,調用與此請求(URL或者Location)關聯的handler -
依次調用各phase handler進行處理。
-
獲取location配置。 -
產生適當的響應。 -
發送response header. -
發送response body.
從這個圖中可以清晰的看到解析http消息每個部分的不同模塊。
keepalive長連接
長連接的定義:所謂長連接,指在一個連接上可以連續發送多個數據包,在連接保持期間,如果沒有數據包發送,需要雙方發鏈路檢測包。
在這裏,http請求是基於TCP協議之上的,所以建立需要三次握手,關閉需要四次握手。而http請求是請求應答式的,如果我們能知道每個請求頭與響應體的長度,那麼我們是可以在一個連接上面執行多個請求的,這就需要在請求頭中指定content-length來表明body的大小。在http1.0與http1.1中稍有不同,具體情況如下:
對於http1.0協議來說,如果響應頭中有content-length頭,則以content-length的長度就可以知道body的長度了,
客戶端在接收body時,就可以依照這個長度來接收數據,接收完後,就表示這個請求完成了。
而如果沒有content-length頭,則客戶端會一直接收數據,直到服務端主動斷開連接,才表示body接收完了。
而對於http1.1協議來說,如果響應頭中的Transfer-encoding爲chunked傳輸,則表示body是流式輸出,body會被分成多個塊,
每塊的開始會標識出當前塊的長度,此時,body不需要通過長度來指定。如果是非chunked傳輸,
而且有content-length,則按照content-length來接收數據。
否則,如果是非chunked,並且沒有content-length,則客戶端接收數據,直到服務端主動斷開連接。
pipeline管道線
lingering_close延遲關閉
Nginx中的數組
ngx_array_s是Nginx中的數組,原型爲ngx_array_t。
typedef struct {
void *elts; //指向數據的指針
ngx_uint_t nelts; //數組中元素的個數
size_t size; //數組中每個元素的大小
ngx_uint_t nalloc; //數據容量
ngx_pool_t *pool; //用來分配內存的內存池
} ngx_array_t;
這裏的數組已經遠遠超出了C語言中數據的概念,類似於Vector。
具體操作參見源碼。
Nginx中的隊列
ngx_queue_t是Nginx中的隊列元素,原型爲ngx_queue_s.
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
具體操作參見源碼。
Nginx中的鏈表
ngx_list_t是Nginx中的list結構。
typedef struct {
ngx_list_part_t *last; //鏈表最後節點
ngx_list_part_t part; //鏈表首部節點
size_t size; //鏈表中存放具體元素的所需內存大小
ngx_uint_t nalloc; //每個節點所含固定大小的數組容量
ngx_pool_t *pool; //用於分配內存的內存池
} ngx_list_t;
ngx_list_part_t是Nginx中的List的元素結構。
struct ngx_list_part_s {
void *elts; //指向數據
ngx_uint_t nelts; //長度
ngx_list_part_t *next;
};
具體操作參見源碼。
Nginx中的string--ngx_str_t
ngx_str_t爲Nginx自身實現的string結構,與c中的字符串不同。
typedef struct {
size_t len; //字符串長度
u_char *data; //指向字符串的指針
} ngx_str_t;
ngx_str_t包括兩部分,一部分是字符串的長度,另外一部分是數據。注意:這裏的數據是指向字符的一個指針,且這個字符串不是以“0”結尾,是通過長度來控制的。使用指針,省去了拷貝所佔用的內存空間。
其他Nginx-String的操作可以看Nginx源碼,還是蠻清晰的。
Ngnix中的內存分配和釋放
在Ngnix中負責內存分配和釋放的結構體爲ngx_pool_t,它的原型爲ngx_pool_s。
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;
};
具體操作參考源碼。
Nginx中的Hash表
ngx_hash_t是Nginx中的hash表。
typedef struct {
ngx_hash_elt_t **buckets;
ngx_uint_t size;
} ngx_hash_t;
其中ngx_hash_elt_t爲數據。
typedef struct {
void *value; //數據 value
u_short len; //數據長度?
u_char name[1]; //key
} ngx_hash_elt_t;
但是ngx_hash_t的實現又有其幾個顯著的特點:
ngx_hash_t不像其他的hash表的實現,可以插入刪除元素,它只能一次初始化,就構建起整個hash表以後,既不能再刪除,也不能在插入元素了。
ngx_hash_t的開鏈並不是真的開了一個鏈表,實際上是開了一段連續的存儲空間,幾乎可以看做是一個數組。這是因爲ngx_hash_t在初始化的時候,會經歷一次預計算的過程,提前把每個桶裏面會有多少元素放進去給計算出來,這樣就提前知道每個桶的大小了。那麼就不需要使用鏈表,一段連續的存儲空間就足夠了。這也從一定程度上節省了內存的使用。
實際上ngx_hash_t的使用是非常簡單,首先是初始化,然後就可以在裏面進行查找了。
Nginx中的紅黑樹
ngx_rbtree_node_s是Nginx中的紅黑樹節點。
struct ngx_rbtree_node_s {
ngx_rbtree_key_t key;
ngx_rbtree_node_t *left;
ngx_rbtree_node_t *right;
ngx_rbtree_node_t *parent;
u_char color;
u_char data;
};
ngx_rbtree_s是Nginx中的紅黑樹。
struct ngx_rbtree_s {
ngx_rbtree_node_t *root;
ngx_rbtree_node_t *sentinel;
ngx_rbtree_insert_pt insert;
};
具體操作參見源碼。
參考:http://tengine.taobao.org/book/#id2
·END·
如果您喜歡本文,歡迎點擊右上角,把文章分享到朋友圈~~
作者:cococo點點
來源:https://www.cnblogs.com/coder2012/p/3141469.html
版權申明:內容來源網絡,版權歸原創者所有。除非無法確認,我們都會標明作者及出處,如有侵權煩請告知,我們會立即刪除並表示歉意。謝謝!
乾貨分享
最近將個人學習筆記整理成冊,使用PDF分享。關注我,回覆如下代碼,即可獲得百度盤地址,無套路領取!
•001:《Java併發與高併發解決方案》學習筆記;•002:《深入JVM內核——原理、診斷與優化》學習筆記;•003:《Java面試寶典》•004:《Docker開源書》•005:《Kubernetes開源書》•006:《DDD速成(領域驅動設計速成)》•007:全部•008:加技術羣討論
近期熱文
•LinkedBlockingQueue vs ConcurrentLinkedQueue•解讀Java 8 中爲併發而生的 ConcurrentHashMap•Redis性能監控指標彙總•最全的DevOps工具集合,再也不怕選型了!•微服務架構下,解決數據庫跨庫查詢的一些思路•聊聊大廠面試官必問的 MySQL 鎖機制
關注我
喜歡就點個"在看"唄^_^
本文分享自微信公衆號 - IT牧場(itmuch_com)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。