主頁 > 前端設計 > 七天學會NodeJS(四)一邊讀取一邊輸出回應、守護行程(child process、spawn)、功能-性能-穩定性-代碼部署

七天學會NodeJS(四)一邊讀取一邊輸出回應、守護行程(child process、spawn)、功能-性能-穩定性-代碼部署

2021-09-01 14:42:59 前端設計

七天學會NodeJS(四)一邊讀取一邊輸出回應、守護行程(child process、spawn)、功能-性能-穩定性-代碼部署

文章目錄

  • 七天學會NodeJS(四)一邊讀取一邊輸出回應、守護行程(child process、spawn)、功能-性能-穩定性-代碼部署
    • 1. 大示例
      • 需求
      • 第一次迭代
        • 設計
        • 實作
      • 第二次迭代
        • 設計
        • 實作
      • 第三次迭代
        • 設計
        • 實作
      • 第四次迭代
        • 設計
        • 實作
      • 后續迭代
      • 小結

總結:

  • 合并靜態檔案示例

    • 本章小結

    • process補充

      • SIGKILL 是告訴行程要立即終止的信號,理想情況下,其行為類似于 process.exit()

      • SIGTERM 是告訴行程要正常終止的信號,它是從行程管理者(如 upstartsupervisord)等發出的信號,

      • 可以從程式內部另一個函式中發送此信號:

        process.kill(process.pid, 'SIGTERM')
        

        或從另一個正在運行的 Node.js 程式、或從系統中運行的其他任何的應用程式(能知道要終止的行程的 PID),

    • Child Process

      • 官方檔案: http://nodejs.org/api/child_process.html
      • 使用child_process模塊可以創建和控制子行程,
      • 該模塊提供的API中最核心的是.spawn,其余API都是針對特定使用場景對它的進一步封裝,算是一種語法糖,
        • 使用.spawn(exec, args, options)方法,創建子行程,該方法支持三個引數,
        • 第一個引數是執行檔案路徑,可以是執行檔案的相對或絕對路徑,也可以是根據PATH環境變數能找到的執行檔案名,
        • 第二個引數中,陣列中的每個成員都按順序對應一個命令列引數,
        • 第三個引數可選,用于配置子行程的執行環境與行為,
    • 第一次迭代(功能

      • 服務器會首先分析URL,得到請求的檔案的路徑和型別(MIME),

        • 使用命令列引數傳遞JSON組態檔路徑,入口函式負責讀取配置并創建服務器,
        • 入口函式完整描述了程式的運行邏輯,其中決議URL和合并檔案的具體實作封裝在其它兩個函式里,
        • 決議URL時先將普通URL轉換為了檔案合并URL,使得兩種URL的處理方式可以一致,
      • 然后,服務器會讀取請求的檔案,并按順序合并檔案內容,

        • 合并檔案時使用異步API讀取檔案,避免服務器因等待磁盤IO而發生阻塞,
      • 最后,服務器回傳回應,完成對一次請求的處理,

      • 邏輯缺陷:

         http://assets.example.com/foo/bar.js,foo/baz.js
        

        經過分析之后我們會發現問題出在/被自動替換/??這個行為上,而這個問題我們可以到第二次迭代時再解決,

    • 第二次迭代(性能

      • 第二版代碼在檢查了請求的所有檔案是否有效之后,立即就輸出了回應頭,并接著一邊按順序讀取檔案一邊輸出回應內容,
      • 并且,在讀取檔案時,第二版代碼直接使用了只讀資料流來簡化代碼,
      • 服務器本身的功能和性能已經得到了初步滿足
    • 第三次迭代(穩定性

      • 從工程角度上講,沒有絕對可靠的系統,即使第二次迭代的代碼經過反復檢查后能確保沒有bug,也很難說是否會因為NodeJS本身,或者是作業系統本身,甚至是硬體本身導致我們的服務器程式在某一天掛掉,因此一般生產環境下的服務器程式都配有一個守護行程,在服務掛掉的時候立即重啟服務,一般守護行程的代碼會遠比服務行程的代碼簡單,從概率上可以保證守護行程更難掛掉,如果再做得嚴謹一些,甚至守護行程自身可以在自己掛掉時重啟自己,從而實作雙保險,
      • 因此在本次迭代時,我們先利用NodeJS的行程管理機制,將守護行程作為父行程,將服務器程式作為子行程,并讓父行程監控子行程的運行狀態,在其例外退出時重啟子行程,
      • 步驟
        • 把守護行程的代碼保存為daemon.js,之后我們可以通過node daemon.js config.json啟動服務,而守護行程會進一步啟動和監控服務器行程
        • 讓守護行程在接收到SIGTERM信號時終止服務器行程
        • 而在服務器行程這一端,同樣在收到SIGTERM信號時先停掉HTTP服務再正常退出,
    • 第四次迭代(代碼部署及服務器控制)

1. 大示例

學習講究的是學以致用和融會貫通,至此我們已經分別介紹了NodeJS的很多知識點,本章作為最后一章,將完整地介紹一個使用NodeJS開發Web服務器的示例,

需求

我們要開發的是一個簡單的靜態檔案合并服務器,該服務器需要支持類似以下格式的JS或CSS檔案合并請求,

http://assets.example.com/foo/??bar.js,baz.js

在以上URL中,??是一個分隔符,之前是需要合并的多個檔案的URL的公共部分,之后是使用,分隔的差異部分,因此服務器處理這個URL時,回傳的是以下兩個檔案按順序合并后的內容,

/foo/bar.js
/foo/baz.js

另外,服務器也需要能支持類似以下格式的普通的JS或CSS檔案請求,

http://assets.example.com/foo/bar.js

以上就是整個需求,

第一次迭代

快速迭代是一種不錯的開發方式,因此我們在第一次迭代時先實作服務器的基本功能,

設計

簡單分析了需求之后,我們大致會得到以下的設計方案,

           +---------+   +-----------+   +----------+
request -->|  parse  |-->|  combine  |-->|  output  |--> response
           +---------+   +-----------+   +----------+

也就是說,服務器會首先分析URL,得到請求的檔案的路徑和型別(MIME),然后,服務器會讀取請求的檔案,并按順序合并檔案內容,最后,服務器回傳回應,完成對一次請求的處理

另外,服務器在讀取檔案時需要有個根目錄,并且服務器監聽的HTTP埠最好也不要寫死在代碼里,因此服務器需要是可配置的,

實作

根據以上設計,我們寫出了第一版代碼如下,

var fs = require('fs'),
    path = require('path'),
    http = require('http');

var MIME = {
    '.css': 'text/css',
    '.js': 'application/javascript'
};

//合并函式,服務器會讀取請求的檔案,并按順序合并檔案內容
function combineFiles(pathnames, callback) {
    var output = [];

    (function next(i, len) {
        if (i < len) {
            fs.readFile(pathnames[i], function (err, data) {
                if (err) {
                    callback(err);
                } else {
                    output.push(data);
                    next(i + 1, len);
                }
            });
        } else {
            callback(null, Buffer.concat(output));
        }
    }(0, pathnames.length));
}

//1.入口函式main
//2.命令列中輸入的是`node server.js config.json`第一個引數是 `node` 命令的完整路徑,第二個引數是正被執行的檔案的完整路徑,所有其他的引數從第三個位置開始
function main(argv) {
    //argv[0]是`node` 命令的完整路徑
    var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')),
        //服務器在讀取檔案時需要有個根目錄,并且服務器監聽的HTTP埠最好也不要寫死在代碼里,因此服務器需要是可配置的,
        root = config.root || '.',
        port = config.port || 80;
	
    //創建了一個HTTP服務器并監聽port埠
    http.createServer(function (request, response) {
        //呼叫URL決議函式,得到請求的檔案的路徑和型別(MIME)
        var urlInfo = parseURL(root, request.url);
		
        //呼叫函式,服務器會讀取請求的檔案,并按順序合并檔案內容
        combineFiles(urlInfo.pathnames, function (err, data) {
            if (err) {
                response.writeHead(404);
                response.end(err.message);
            } else {
                //最后,服務器回傳回應,完成對一次請求的處理,
                response.writeHead(200, {
                    'Content-Type': urlInfo.mime
                });
                response.end(data);
            }
        });
    }).listen(port);
}

//URL決議函式,得到請求的檔案的路徑和型別(MIME)
function parseURL(root, url) {
    var base, pathnames, parts;

    if (url.indexOf('??') === -1) {
        url = url.replace('/', '/??');
    }

    parts = url.split('??');
    base = parts[0];
    pathnames = parts[1].split(',').map(function (value) {
        return path.join(root, base, value);
    });

    return {
        mime: MIME[path.extname(pathnames[0])] || 'text/plain',
        pathnames: pathnames
    };
}

main(process.argv.slice(2));

以上代碼完整實作了服務器所需的功能,并且有以下幾點值得注意:

  1. 使用命令列引數傳遞JSON組態檔路徑,入口函式負責讀取配置并創建服務器,
  2. 在命令列中輸入的是node server.js config.json第一個引數是 node 命令的完整路徑,第二個引數是正被執行的檔案的完整路徑,所有其他的引數從第三個位置開始
  3. 入口函式完整描述了程式的運行邏輯,其中決議URL和合并檔案的具體實作封裝在其它兩個函式里,
  4. 決議URL時先將普通URL轉換為了檔案合并URL,使得兩種URL的處理方式可以一致,
  5. 合并檔案時使用異步API讀取檔案,避免服務器因等待磁盤IO而發生阻塞,

我們可以把以上代碼保存為server.js,之后就可以通過node server.js config.json命令啟動程式,于是我們的第一版靜態檔案合并服務器就順利完工了,

另外,以上代碼存在一個不那么明顯的邏輯缺陷,例如,使用以下URL請求服務器時會有驚喜,

    http://assets.example.com/foo/bar.js,foo/baz.js

經過分析之后我們會發現問題出在/被自動替換/??這個行為上,而這個問題我們可以到第二次迭代時再解決,

第二次迭代

在第一次迭代之后,我們已經有了一個可作業的版本,滿足了功能需求,接下來我們需要從性能的角度出發,看看代碼還有哪些改進余地,

設計

map方法換成for回圈或許會更快一些,但第一版代碼最大的性能問題存在于從讀取檔案到輸出回應的程序當中,我們以處理/??a.js,b.js,c.js這個請求為例,看看整個處理程序中耗時在哪兒,

 發送請求       等待服務端回應         接收回應
---------+----------------------+------------->
         --                                        決議請求
           ------                                  讀取a.js
                 ------                            讀取b.js
                       ------                      讀取c.js
                             --                    合并資料
                               --                  輸出回應

可以看到,第一版代碼依次把請求的檔案讀取到記憶體中之后,再合并資料和輸出回應,這會導致以下兩個問題:

  1. 當請求的檔案比較多比較大時,串行讀取檔案會比較耗時,從而拉長了服務端回應等待時間,
  2. 由于每次回應輸出的資料都需要先完整地快取在記憶體里,當服務器請求并發數較大時,會有較大的記憶體開銷,

對于第一個問題,很容易想到把讀取檔案的方式從串行改為并行,但是別這樣做,因為對于機械磁盤而言,因為只有一個磁頭,嘗試并行讀取檔案只會造成磁頭頻繁抖動,反而降低IO效率,而對于固態硬碟,雖然的確存在多個并行IO通道,但是對于服務器并行處理的多個請求而言,硬碟已經在做并行IO了,對單個請求采用并行IO無異于拆東墻補西墻,因此,正確的做法不是改用并行IO,而是一邊讀取檔案一邊輸出回應,把回應輸出時機提前至讀取第一個檔案的時刻,這樣調整后,整個請求處理程序變成下邊這樣,

發送請求 等待服務端回應 接收回應
---------+----+------------------------------->
         --                                        決議請求
           --                                      檢查檔案是否存在
             --                                    輸出回應頭
               ------                              讀取和輸出a.js
                     ------                        讀取和輸出b.js
                           ------                  讀取和輸出c.js

按上述方式解決第一個問題后,因為服務器不需要完整地快取每個請求的輸出資料了,第二個問題也迎刃而解,

實作

根據以上設計,第二版代碼按以下方式調整了部分函式,

function main(argv) {
    var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')),
        root = config.root || '.',
        port = config.port || 80;

    http.createServer(function (request, response) {
        var urlInfo = parseURL(root, request.url);

        validateFiles(urlInfo.pathnames, function (err, pathnames) {
            if (err) {
                response.writeHead(404);
                response.end(err.message);
            } else {
                response.writeHead(200, {
                    'Content-Type': urlInfo.mime
                });
                outputFiles(pathnames, response);
            }
        });
    }).listen(port);
}

//并接著一邊按順序讀取檔案一邊輸出回應內容
function outputFiles(pathnames, writer) {
    (function next(i, len) {
        if (i < len) {
            var reader = fs.createReadStream(pathnames[i]);
			//直接使用了只讀資料流來簡化代碼
            reader.pipe(writer, { end: false });
            reader.on('end', function() {
                next(i + 1, len);
            });
        } else {
            writer.end();
        }
    }(0, pathnames.length));
}

//檢查了請求的所有檔案是否有效之后,立即就輸出了回應頭
function validateFiles(pathnames, callback) {
    (function next(i, len) {
        if (i < len) {
            fs.stat(pathnames[i], function (err, stats) {
                if (err) {
                    callback(err);
                } else if (!stats.isFile()) {
                    callback(new Error());
                } else {
                    next(i + 1, len);
                }
            });
        } else {
            callback(null, pathnames);
        }
    }(0, pathnames.length));
}

可以看到,第二版代碼在檢查了請求的所有檔案是否有效之后,立即就輸出了回應頭,并接著一邊按順序讀取檔案一邊輸出回應內容,并且,在讀取檔案時,第二版代碼直接使用了只讀資料流來簡化代碼,

第三次迭代

第二次迭代之后,服務器本身的功能和性能已經得到了初步滿足,接下來我們需要從穩定性的角度重新審視一下代碼,看看還需要做些什么,

設計

從工程角度上講,沒有絕對可靠的系統,即使第二次迭代的代碼經過反復檢查后能確保沒有bug,也很難說是否會因為NodeJS本身,或者是作業系統本身,甚至是硬體本身導致我們的服務器程式在某一天掛掉,因此一般生產環境下的服務器程式都配有一個守護行程,在服務掛掉的時候立即重啟服務,一般守護行程的代碼會遠比服務行程的代碼簡單,從概率上可以保證守護行程更難掛掉,如果再做得嚴謹一些,甚至守護行程自身可以在自己掛掉時重啟自己,從而實作雙保險,

因此在本次迭代時,我們先利用NodeJS的行程管理機制,將守護行程作為父行程,將服務器程式作為子行程,并讓父行程監控子行程的運行狀態,在其例外退出時重啟子行程,

實作

根據以上設計,我們撰寫了守護行程需要的代碼,

var cp = require('child_process');

var worker;

function spawn(server, config) {
    worker = cp.spawn('node', [ server, config ]);
    worker.on('exit', function (code) {
        if (code !== 0) {
            spawn(server, config);
        }
    });
}

function main(argv) {
    spawn('server.js', argv[0]);
    //守護行程在接收到`SIGTERM`信號時終止服務器行程
    process.on('SIGTERM', function () {
        worker.kill();
        process.exit(0);
    });
}

main(process.argv.slice(2));

此外,服務器代碼本身的入口函式也要做以下調整,

function main(argv) {
    var config = JSON.parse(fs.readFileSync(argv[0], 'utf-8')),
        root = config.root || '.',
        port = config.port || 80,
        server;

    server = http.createServer(function (request, response) {
        ...
    }).listen(port);
	
        //而在服務器行程這一端,同樣在收到`SIGTERM`信號時先停掉HTTP服務再正常退出,
    process.on('SIGTERM', function () {
        server.close(function () {
            process.exit(0);
        });
    });
}

我們可以把守護行程的代碼保存為daemon.js,之后我們可以通過node daemon.js config.json啟動服務,而守護行程會進一步啟動和監控服務器行程,此外,為了能夠正常終止服務,我們讓守護行程在接收到SIGTERM信號時終止服務器行程,而在服務器行程這一端,同樣在收到SIGTERM信號時先停掉HTTP服務再正常退出,至此,我們的服務器程式就靠譜很多了,

第四次迭代

在我們解決了服務器本身的功能、性能和可靠性的問題后,接著我們需要考慮一下代碼部署的問題,以及服務器控制的問題,

設計

一般而言,程式在服務器上有一個固定的部署目錄,每次程式有更新后,都重新發布到部署目錄里,而一旦完成部署后,一般也可以通過固定的服務控制腳本啟動和停止服務,因此我們的服務器程式部署目錄可以做如下設計,

- deploy/
    - bin/
        startws.sh
        killws.sh
    + conf/
        config.json
    + lib/
        daemon.js
        server.js

在以上目錄結構中,我們分類存放了服務控制腳本、組態檔和服務器代碼,

實作

按以上目錄結構分別存放對應的檔案之后,接下來我們看看控制腳本怎么寫,首先是start.sh

#!/bin/sh
if [ ! -f "pid" ]
then
    node ../lib/daemon.js ../conf/config.json &
    echo $! > pid
fi

然后是killws.sh

#!/bin/sh
if [ -f "pid" ]
then
    kill $(tr -d '\r\n' < pid)
    rm pid
fi

于是這樣我們就有了一個簡單的代碼部署目錄和服務控制腳本,我們的服務器程式就可以上線作業了,

后續迭代

我們的服務器程式正式上線作業后,我們接下來或許會發現還有很多可以改進的點,比如服務器程式在合并JS檔案時可以自動在JS檔案之間插入一個;來避免一些語法問題,比如服務器程式需要提供日志來統計訪問量,比如服務器程式需要能充分利用多核CPU,等等,而此時的你,在學習了這么久NodeJS之后,應該已經知道該怎么做了,

小結

本章將之前零散介紹的知識點串了起來,完整地演示了一個使用NodeJS開發程式的例子,至此我們的課程就全部結束了,以下是對新誕生的NodeJSer的一些建議,

  • 要熟悉官方API檔案,并不是說要熟悉到能記住每個API的名稱和用法,而是要熟悉NodeJS提供了哪些功能,一旦需要時知道查詢API檔案的哪塊地方,
  • 要先設計再實作,在開發一個程式前首先要有一個全域的設計,不一定要很周全,但要足夠能寫出一些代碼,
  • 要實作后再設計,在寫了一些代碼,有了一些具體的東西后,一定會發現一些之前忽略掉的細節,這時再反過來改進之前的設計,為第二輪迭代做準備,
  • 要充分利用三方包,NodeJS有一個龐大的生態圈,在寫代碼之前先看看有沒有現成的三方包能節省不少時間,
  • 不要迷信三方包,任何事情做過頭了就不好了,三方包也是一樣,三方包是一個黑盒,每多使用一個三方包,就為程式增加了一份潛在風險,并且三方包很難恰好只提供程式需要的功能,每多使用一個三方包,就讓程式更加臃腫一些,因此在決定使用某個三方包之前,最好三思而后行,

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

標籤:其他

上一篇:vuex的基本概念

下一篇:Ureport2原始碼啟動

標籤雲
其他(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)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more