主頁 > 後端開發 > nginx原始碼層面探究request_time、upstream_response_time、upstream_connect_time與upstream_header_time指標具體含義

nginx原始碼層面探究request_time、upstream_response_time、upstream_connect_time與upstream_header_time指標具體含義

2022-06-30 14:39:11 後端開發

背景概述

最近計劃著重分析一下線上各api的HTTP回應耗時情況,檢查是否有介面平均耗時、99分位耗時等相關指標過大的情況,了解到nginx統計請求耗時有四個指標:request_time、upstream_response_time、upstream_connect_time與upstream_header_time,在查找資料的程序中,發現無論是nginx官方檔案還是熱心網友們的分享,都并沒有讓自己感覺特別詳細、明白地說清楚了這四個指標詳細具體含義的資料,于是自己動手探究了一番nginx原始碼,嘗試從其中找出這4個指標的代碼級別具體含義,
特別說明:本文代碼分析基于nginx 1.10.0版本,從原始碼層面分析一次完整HTTP請求log中request_time、upstream_response_time、upstream_connect_time與upstream_header_time四個指標的具體含義,本文中得出的相應結論僅基于個人學習、研究所得,非權威結論,如有不當之處歡迎指正、一起探討,

一次完整HTTP請求/回應的各耗時階段拆分

首先詳細拆分一下一個完整HTTP請求(非keep alive)生命周期的多個階段(以下C指代客戶端,N指代nginx服務器,S指代上游server):

  1. C向N發起TCP三次握手建立連接成功,C開始向N通過TCP發送HTTP請求具體資料(header/body...)
  2. N開始接收到C發送的資料到全部接收完成
  3. N作為代理向S發起TCP三次握手并建立連接成功,N開始向S發送HTTP資料
  4. S開始接收N發送的資料并全部接收完成
  5. S業務代碼根據業務規則進行處理完成并生成HTTP回應結果
  6. S開始將回應結果發送給N
  7. N開始接收回應結果并接收header部分完成
  8. N接收S回傳的全部回應結果完成
  9. N開始向C回傳全部的HTTP回應結果
  10. C開始接收N回傳的資料并全部接收完成
  11. N向C發起四次揮手關閉TCP連接

其中1-2和9-11這5個階段 的速度直接受到C到N之間的網路質量影響,服務端雖然可以通過降低傳輸資料量、使用更快的協議(如HTTP3.0基于QUIC)等降低傳輸耗時,但無法起到決定性的作用,一般可視為超出了可優化的控制范圍,
3-8這6個階段一般都發生在內網,即N與S都處于同一個機房(甚至同一個機架/同一臺物理機上),網路質量穩定且RTT基本在1ms內,網路耗時較少,正常情況下其主要時間應集中在階段5--各種業務邏輯處理資料并生成結果--這也正是一般后端優化的目標階段,

各耗時指標nginx官方解釋 && 疑問

參考:http://nginx.org/en/docs/http/ngx_http_log_module.html

$request_time
request processing time in seconds with a milliseconds resolution; time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client

request_time是N接收到C第一個位元組數起至N向C發送完最后一個位元組數止后日志記錄的時間,
疑問:接收到第一個位元組包括TCP三次握手的位元組嗎?發送完最后一個位元組的具體定義是什么--N發送最后一個位元組必須收到C的ACK才算發送完成?
參考: http://nginx.org/en/docs/http/ngx_http_upstream_module.html

$upstream_connect_time
keeps time spent on establishing a connection with the upstream server (1.9.1); the time is kept in seconds with millisecond resolution. In case of SSL, includes time spent on handshake. Times of several connections are separated by commas and colons like addresses in the $upstream_addr variable.

upstream_connect_time記錄N與S建立起一個連接的耗時,在SSL中也包括SSL握手的時間,
疑問:有一絲不確定這個時間具體是指N到S的 TCP三次握手開始到連接建立完成時間--對應上面階段3?

$upstream_header_time
keeps time spent on receiving the response header from the upstream server (1.7.10); the time is kept in seconds with millisecond resolution. Times of several responses are separated by commas and colons like addresses in the $upstream_addr variable.

upstream_header_time記錄N接收到S回應header的時間,
疑問:能夠理解會包含N到S連接建立后收到S header的時間--階段4~7,但是否包括upstream_connect_time這個建連時間--階段3呢?

$upstream_response_time
keeps time spent on receiving the response from the upstream server; the time is kept in seconds with millisecond resolution. Times of several responses are separated by commas and colons like addresses in the $upstream_addr variable.

upstream_response_time記錄N接收S完整回應的時間,
疑問:應包含階段4-8,但是否包括upstream_connect_time這個建連時間--階段3?

如上,按照字面意思翻譯得到的各指標含義很簡潔,但是讓人不是很明了,不由的生出一些疑問,于是決定探究一下nginx原始碼嘗試徹底弄清楚這幾個指標的具體含義,

nginx原始碼探究

request_time指標

手上有一份nginx 1.10.0的原始碼,雖然版本比較舊,但是想來指標統計的基本邏輯是不會變的,先探查范圍最大的指標request_time,該指標屬于模塊ngx_http_log_module,其相關代碼在http/ngx_http_variables.c 的ngx_http_variable_request_time函式中:

// http/ngx_http_variables.c
2041 static ngx_int_t
2042 ngx_http_variable_request_time(ngx_http_request_t *r,
2043     ngx_http_variable_value_t *v, uintptr_t data)
2044 {
...
2054     tp = ngx_timeofday(); // 獲取當前時刻
2055
2056     ms = (ngx_msec_int_t) // 當前時刻減去開始時刻得到耗時
2057              ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
2058     ms = ngx_max(ms, 0);
2059
2060     v->len = ngx_sprintf(p, "%T.%03M", (time_t) ms / 1000, ms % 1000) - p;
...
2066     return NGX_OK;
2067 }

關鍵在于ngx_http_variable_request_time函式的呼叫時機以及r->start_sec、msec(ngx_http_request_t.start_sec/msec)的記錄時機,查找原始碼可以發現ngx_http_request_t.start_sec的記錄時間位于http/ngx_http_request.c的ngx_http_create_request函式中,ngx_http_create_request函式會在ngx_http_wait_request_handler被呼叫,一步步往上追溯最后會發現,ngx_http_create_request實際是在N的監聽行程與C建立TCP連接后接收到資料觸發可讀事件后被呼叫,即start_sec/msec記錄的是連接建立后收到第一個可讀位元組時的--此時HTTP所在的應用層還未真正讀取資料,資料只是交付到了TCP所在的傳輸層,

// http/ngx_http_request.c
503 ngx_http_request_t *
 504 ngx_http_create_request(ngx_connection_t *c)
 505 {
 ...
  579     r->main = r;
 580     r->count = 1;
 581
 582     tp = ngx_timeofday();
 583     r->start_sec = tp->sec;
 584     r->start_msec = tp->msec;
 585...
  611 }

而對于ngx_http_variable_request_time的呼叫時機,追溯原始碼發現其被放置于放在ngx_http_core_module全域變數中,而ngx_http_core_module會在ngx_http_log_init函式中注冊到main_conf,最終http/ngx_http_request.c的ngx_http_free_request函式中會呼叫ngx_http_log_request,而后在其中通過main_conf得到log相關handler并執行,其相關代碼如下:

// http/ngx_http_request.c
3410 void
3411 ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)
3412 {
...
3456     log->action = "logging request";
3457
3458     ngx_http_log_request(r);
3459
3460     log->action = "closing request";
3461
3462     if (r->connection->timedout) {
3463         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
3464
3465         if (clcf->reset_timedout_connection) {
3466             linger.l_onoff = 1;
3467             linger.l_linger = 0;
3468
3469             if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER,
3470                            (const void *) &linger, sizeof(struct linger)) == -1)
3471             {
3472                 ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,
3473                               "setsockopt(SO_LINGER) failed");
3474             }
3475         }
3476     }
...
3500 ngx_http_log_request(ngx_http_request_t *r)
3501 {
3502     ngx_uint_t                  i, n;
3503     ngx_http_handler_pt        *log_handler;
3504     ngx_http_core_main_conf_t  *cmcf;
3505
3506     cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
3507
3509     log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;
3510     n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;
3511
3512     for (i = 0; i < n; i++) {
3513         log_handler[i](r);
3514     }
3515 }

可以看到ngx_http_log_request正好在使用setsockopt優雅關閉連接前呼叫,由此得出結論,request_time起始時間為N接收到TCP包觸發第一次可讀event后,應用層正式讀取資料前的時刻,而結束時間為應用層接收完全部資料,即將關閉連接前一時刻,即包括階段2~10,不包括TCP四次揮手的時間,

upstream_connect_time

upstream_connect_time、upstream_header_time與upstream_response_time三個指標均屬于ngx_http_upstream模塊,對應nginx中的connect_time、header_time、response_time三個變數,其初始化代碼位于ngx_http_upstream.c中的ngx_http_upstream_connect函式,相關代碼如下:

// http/ngx_http_upstream.c
1328 static void
1329 ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)
1330 {
1331     ngx_int_t          rc;
1332     ngx_connection_t  *c;
...
1346
1347     ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));
1348
1349     u->state->response_time = ngx_current_msec;
1350     u->state->connect_time = (ngx_msec_t) -1;
1351     u->state->header_time = (ngx_msec_t) -1;
1352
1353     rc = ngx_event_connect_peer(&u->peer);
1354
1355     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1356                    "http upstream connect: %i", rc);
...
1467     ngx_http_upstream_send_request(r, u, 1);
1468 }

可以看到其初始值設定正好處于ngx_event_connect_peer函式前,即N即將開始與S建立連接之前,注意此時response_time被設定為了當前時刻時間,而后繼續追溯原始碼可以發現connect_time最終在ngx_http_upstream_connect函式末尾呼叫的ngx_http_upstream_send_request函式中進行了賦值,相關代碼如下:

// http/ngx_http_upstream.c
1782 static void
1783 ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u,
1784     ngx_uint_t do_write)
1785 {
...
1791     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
1792                    "http upstream send request");
1793
1794     if (u->state->connect_time == (ngx_msec_t) -1) {
1795         u->state->connect_time = ngx_current_msec - u->state->response_time;
1796     }
...
...
1864     if (c->read->ready) {
1865         ngx_http_upstream_process_header(r, u);
1866         return;
1867     }
1868 }

由此可以得出結論,upstream_connect_time起始時刻為N將與S建立連接前一刻,結束時刻為N與S建立連接成功后,即包括階段3,

upstream_header_time

接下來探究upstream_header_time,可在ngx_http_upstream_send_request函式末尾呼叫的ngx_http_upstream_process_header中發現header_time的賦值陳述句:

2047 static void
2048 ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)
2049 {
...
2058
2059     c->log->action = "reading response header from upstream";
...
2104     for ( ;; ) {
2105
2106         n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);
...
}
...
2172     u->state->header_time = ngx_current_msec - u->state->response_time;
...
2184
2185     if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {
2186         return;
2187     }
...

由此可得出結論,即header_time起始時刻應為N與S將建立連接前一刻,結束時刻為建立連接成功并在應用層接收header資料完成,即階段3~7,

upstream_response_time

最后探究upstream_response_time,追溯代碼可以發現response_time最終在ngx_http_upstream_finalize_request函式中被賦值,相關代碼如下:

// http/ngx_http_upstream.c
4064 static void
4065 ngx_http_upstream_finalize_request(ngx_http_request_t *r,
4066     ngx_http_upstream_t *u, ngx_int_t rc)
4067 {
...
4086
4087     if (u->state && u->state->response_time) {
4088         u->state->response_time = ngx_current_msec - u->state->response_time;
4089
4090         if (u->pipe && u->pipe->read_length) {
4091             u->state->response_length = u->pipe->read_length;
4092         }
4093     }
4094
4095     u->finalize_request(r, rc);
4096
4097     if (u->peer.free && u->peer.sockaddr) {
4098         u->peer.free(&u->peer, u->peer.data, 0);
4099         u->peer.sockaddr = NULL;
4100     }
4101
4102     if (u->peer.connection) {
4103
4104 #if (NGX_HTTP_SSL)
4105
4106         /* TODO: do not shutdown persistent connection */
4107
4108         if (u->peer.connection->ssl) {
4109
4110             /*
4111              * We send the "close notify" shutdown alert to the upstream only
4112              * and do not wait its "close notify" shutdown alert.
4113              * It is acceptable according to the TLS standard.
4114              */
4115
4116             u->peer.connection->ssl->no_wait_shutdown = 1;
4117
4118             (void) ngx_ssl_shutdown(u->peer.connection);
4119         }
4120 #endif
4121
4122         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
4123                        "close http upstream connection: %d",
4124                        u->peer.connection->fd);
4125
4126         if (u->peer.connection->pool) {
4127             ngx_destroy_pool(u->peer.connection->pool);
4128         }
4129
4130         ngx_close_connection(u->peer.connection);
4131     }
...

可以看到u->state->response_time = ngx_current_msec - u->state->response_time; 在ngx_close_connection之前執行,由此可以得出結論,upstream_response_time起始時刻為N與S將建立連接前一刻,結束時間為N接收完S全部回應資料將關閉連接前一刻,即階段3~10,

最終結論

經過原始碼追溯最終可以得出request_time、upstream_response_time、upstream_connect_time與upstream_header_time四個指標的關系為:
upstream_header_time = upstream_connect_time(階段3) + N向S發送資料被接收完成時間(階段4) + S業務代碼處理資料回傳并被N接收完header部分資料的時間(階段5~7)
upstream_response_time = upstream_header_time + N接收完S除header部分剩余全部資料的時間(階段8)
request_time = N開始接收C全部資料并完成的時間(階段2) + upstream_response_time + N向C回傳資料并被C接收完成的時間(階段9~10)
至于一開始對于檔案解釋request_time 接收第一個位元組的、發送完最后一個位元組的具體定義,在閱讀程序中也有了答案:
HTTP是應用層協議,其建立于傳輸層的TCP協議之上,而TCP是保證有序和可靠的--其每一個有效資料包都必須收到對端的ACK確認才算發送成功,因此站在N的角度看待資料接收與發送完成,可以得出以下結論:
其所謂的接收第一個位元組時刻必然是屬于C發向N的第一個TCP有效資料包被接收時刻--不會包括三次握手純SYN/ACK包--除非第三個握手包已經帶了有效資料,
而所謂的發送完最后一個位元組時刻則是N發向C的最后一個有效資料包被接收后,N收到了C的ACK確認時刻,
轉載請注明出處,原文地址: https://www.cnblogs.com/AcAc-t/p/nginx_request_time_upstream_respone_time_analysis.html

參考

http://nginx.org/en/docs/http/ngx_http_log_module.html

簽名:擁抱開源,擁抱自由

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/497682.html

標籤:C

上一篇:C 語言指標

下一篇:IOS OpenGL ES GPUImage 黑白色調模糊 GPUImageOpeningFilter

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more