主頁 > 企業開發 > CesiumJS 2022^ 原始碼解讀[8] - 資源封裝與多執行緒

CesiumJS 2022^ 原始碼解讀[8] - 資源封裝與多執行緒

2022-07-13 09:31:57 企業開發

目錄
  • 1. 資源封裝與請求封裝
    • 1.1. 請求的封裝 - Request 與其調度器
    • 1.2. 資源類 - Resource
    • 1.3. 延遲請求與最大請求個數限制
    • 1.4. 常用請求方法
    • 1.5. 舉例
  • 2. 多執行緒技術
    • 2.1. 跳轉器
    • 2.2. 基本用法
    • 2.3. 使用 WebAssembly
    • ① 例:解碼 draco 壓縮的幾何資料
    • ② 例:處理幾何資料


CesiumJS 對需要網路請求的一切資源都進行了統一的封裝,也就是 Resource 類,

在 XHR 技術橫行的年代,就出現過 ajax 這種神器,但是 Cesium 團隊選擇了自己封裝 XHR,后來 ES6 出現了 Promise API,axios 再次封裝了 XHR,但是 Cesium 團隊對這種底層的改動非常敏感,也是最近一年(2021~2022年)才把 var 改為了 const/let,把 when.js 改為了原生 Promise,把 ""/'' 字串部分改為了 `` 這種反引號字串,因此自封裝的 XHR 就沒有改動,

所以,雖然可能不太常用,我認為還是可以了解了解這套 Resource API 的,

1. 資源封裝與請求封裝

Resource 集成了一些通用的請求方法,以及一些輔助的函式,譬如判斷 blob 的支持、處理 Url(修改QueryString、獲取基地址等)等,不過,真正發起請求,還是得從 RequestRequestScheduler 這兩個類說起,

1.1. 請求的封裝 - Request 與其調度器

Request 代表一個具體的請求,RequestScheduler 則是調度器,有人說為什么要整個調度類和調度器類,直接讓 Resource 發起 XHR 請求不就行了嗎?

這與 CesiumJS 的資料調度演算法有關,有的請求并不是馬上隨更新程序就發出的,有的是需要延遲請求的(優先級不同),這時候請求調度器 RequestScheduler 就凸顯了作用,

Request 類一般以 Resource 物件欄位存在:

function Resource(options) {
  /* ... */
  this.request = defaultValue(options.request, new Request());
  /* ... */
}

在需要使用的時候,會把 Resource 物件上的資訊交給 Request 物件:

Resource.prototype.fetch = function (options) {
  options = defaultClone(options, {});
  options.method = "GET";

  return this._makeRequest(options);
};

Resource.prototype._makeRequest = function (options) {
  /* ... */
  request.url = resource.url;
  request.requestFunction = function () {/* ... */};
  const promise = RequestScheduler.request(request);
  /* ...回傳請求的資料... */
};

1.2. 資源類 - Resource

你可以用很多東西來實體化一個 Resource,你也可以在公開的檔案中看到很多引數是“Resource”型別的,例如幾個很常見的資料類:

/**
 * @param {Resource|String|Promise<Resource>|Promise<String>} options.url The url to a tileset JSON file.
 */
function Cesium3DTileset(options) {/* ... */}

/**
 * @param {String|Resource} options.url The url to the .gltf or .glb file.
 */
ModelExperimental.fromGltf = function (options) {/* ... */}

/**
 * @param {Resource|String|Object} data A url, GeoJSON object, or TopoJSON object to be loaded.
 */
GeoJsonDataSource.load = function (data, options) {/* ... */}

你用這些資訊實體化一個 Resource

  • 資源的網路相對/絕對路徑
  • Resource 實體本身
  • base64 字串(DataUri) / blob 字串

在各種資料的 API 中也允許你傳入不同的引數,例如 glTF 資料允許你傳遞檔案路徑、glTF JSON 本身甚至是自己請求下來的 gltf/glb 檔案的二進制流,詳見本系列文章的第 6 篇,

Resource 類有很多個發起請求的方法,有實體上的,也有靜態的,在 1.4 小節會列舉,靜態方法會 new 一個 Resource 實體,然后呼叫其對應的實體方法:

Resource.fetchJson = function (options) {
  const resource = new Resource(options);
  return resource.fetchJson(); // 回傳 Promise<object>
};

在現在 axios 或瀏覽器原生 Fetch API 已經如此通用的環境下,已經很少有需要創建 Resource 物件的需求了,原始碼中一般會使用 Resource.createIfNeeded() 來創建資源物件,在測驗用例中,創建 ktx2 檔案資源的代碼如下:

const resource = Resource.createIfNeeded("./Data/Images/Green4x4.ktx2");

最后說說觸發請求后的呼叫鏈,

以請求 JSON 為例:

Resource.prototype.fetchJson
  ┕ Resource.prototype.fetch
     ┕ Resource.prototype._makeRequest
       [Module RequestScheduler.js]
       ┕ RequestScheduler.request
         ┕ fn startRequest
           [Module Request.js]
           ┕ Request.prototype.requestFunction
             [Module Resource.js]
             ┕ Resource._Implementations.loadWithXhr

_makeRequest 方法會為 Resource 物件的 request 成員注冊請求方法 requestFunction,隨后讓 RequestScheduler 發起請求,一波周轉后,還是會執行這個注冊了的 requestFunction 的,內部會呼叫 Resource._Implementations.loadWithXhr 這個方法,也就是發起 XHR 請求,回傳一個 Promise,

1.3. 延遲請求與最大請求個數限制

按理說,一般是不需要去修改 RequestRequestScheduler 上的資訊的,這兩個在 CesiumJS 封裝的請求功能中屬于底層,用 Resource 暴露出來的請求 API 即可,不過,我仍然覺得有兩個地方值得分享,

雖然 RequestScheduler 是公開的 API,在 2019 年一個 PR CesiumGS/cesium#8549 中被公開出來了

一個是 RequestScheduler 上的靜態引數 maximumRequestsmaximumRequestsPerServer,這兩個數值代表的意義是允許開發者設定的最大并發請求數量、每個服務器允許的最大請求數量,默認分別是 50、6,

如果你的服務器允許,那么你可以稍微把這個數值改大一些(譬如并發用戶數不多的時候,私網環境),在請求 3DTiles 瓦片、地球瓦片時能同時多請求一些資源,

另一個是 RequestScheduler 上的延遲請求機制,允許創建 Request 時指定 throttle 引數為 true,在使用 RequestScheduler.request 發出請求時,先暫存起來:

RequestScheduler.request = function (request) {
  /* 前面的代碼是非延遲請求的處理 */
  const removedRequest = requestHeap.insert(request);
  return issueRequest(request);
};

此處 requestHeap 是 CesiumJS 自己制作的一個堆資料結構,

然后在 Scene 的渲染函式執行完畢后,在 postPassesUpdate 函式中呼叫 RequestScheduler.update 將延遲的請求統一再發出:

// Scene.js
function postPassesUpdate(scene) {
  /* ... */
  RequestScheduler.update();
}

Scene.prototype.render = function (time) {
  /* ... */
  if (shouldRender) {
    /* ... */
    tryAndCatchError(this, render);
  }
  /* ... */
  tryAndCatchError(this, postPassesUpdate);
  /* ... */
};

這樣就實作了延遲請求,減輕大量請求可能造成的主執行緒壓力 —— 這也就是 RequestScheduler 的職能所在,

想知道哪些資料類具備延遲請求行為?只需在 Source 檔案夾下全代碼搜索 throttle: true 即可,例如 Multiple3DTileContent 在請求內部瓦片內容時,就用了延遲請求;有幾個影像、地形供給器類也用了延遲請求,

1.4. 常用請求方法

Cesium 封裝了 HTTP 常用的請求方法:

  • Resource.fetch(這個對應 GET 請求)
  • Resource.head
  • Resource.patch
  • Resource.post
  • Resource.delete
  • Resource.options
  • Resource.put

同時,對常用的檔案/資料格式也做了簡易的封裝,如果你不想用 axios 或 Fetch API,而且能用 async/await 語法,那么直接 await 它們的執行結果也是不錯的,回傳的就是你所需要的資料結果:

  • Resource.fetchJson
  • Resource.fetchXML
  • Resource.fetchImage
  • Resource.fetchJsonp
  • Resource.fetchText
  • Resource.fetchBlob

1.5. 舉例

這里就不再贅述,給出兩個源代碼中的資源創建、請求例子,

一個是 3DTiles 的 tileset.json 入口檔案,用到了 Resource.prototype.fetchJson 方法:

// Cesium3DTileset.js
function Cesium3DTileset(options) {
  this._readyPromise = Promise.resolve(options.url)
    .then(function (url) {
      /* ... */
      resource = Resource.createIfNeeded(url);
      /* ... */
      return Cesium3DTileset.loadJson(resource);
  })
  /* ... */
}

Cesium3DTileset.loadJson = function (tilesetUrl) {
  const resource = Resource.createIfNeeded(tilesetUrl);
  return resource.fetchJson();
};

另一個是 ImageryProvider 的靜態函式,請求影像瓦片圖片,用到了 Resource.prototype.loadImage 方法:

ImageryProvider.loadImage = function (imageryProvider, url) {
  /* ... */

  const resource = Resource.createIfNeeded(url);

  if (ktx2Regex.test(resource.url)) {
    return loadKTX2(resource);
  } else if (
    defined(imageryProvider) &&
    defined(imageryProvider.tileDiscardPolicy)
  ) {
    return resource.fetchImage({
      preferBlob: true,
      preferImageBitmap: true,
      flipY: true,
    });
  }

  return resource.fetchImage({
    preferImageBitmap: true,
    flipY: true,
  });
};

其余的請讀者自行研究學習,

2. 多執行緒技術

CesiumJS 使用了 WebWorker 多執行緒技術,

什么功能要用到 WebWorker 多執行緒呢?畢竟子執行緒與主執行緒的通信成本、能傳遞的資料型別都是對程式有影響的,WebWorker 技術發布之后,在實踐中發現比較合適的任務是資料編解碼或有阻礙你主執行緒效率的任務(尤其是資料解碼,使用 WASM 輔助更佳),

CesiumJS 有兩類任務需要剝離主執行緒,不影響主執行緒的邏輯判斷:資料解碼、幾何資料的處理,資料解碼主要是 basisu 紋理的檔案解碼、draco 幾何壓碩訓沖資料的解碼;幾何資料的處理主要是少量在 Primitive API 中用到的部分 Geometry 合并操作,

而管理起這些 WebWorker 的管理器是 TaskProcessor 類,

2.1. 跳轉器

在介紹 TaskProcessor 類之前,要先介紹一個 叫做 cesiumWorkerBootstrapper 的東西,你可能打開瀏覽器看報錯、網路抓包時,在源代碼頁面看到過這個東西:

image

它是 TaskProcessor 創建的一個最基本的子執行緒(子 Worker):

// TaskProcessor.js

function getBootstrapperUrl() {
  if (!defined(bootstrapperUrlResult)) {
    bootstrapperUrlResult = getWorkerUrl("Workers/cesiumWorkerBootstrapper.js");
  }
  return bootstrapperUrlResult;
}

function createWorker(processor) {
  const worker = new Worker(getBootstrapperUrl());
  /* ... */

  const bootstrapMessage = {
    loaderConfig: {
      paths: {
        Workers: buildModuleUrl("Workers"),
      },
      baseUrl: buildModuleUrl.getCesiumBaseUrl().url,
    },
    workerModule: processor._workerPath,
  };

  worker.postMessage(bootstrapMessage);
  worker.onmessage = function (event) {
    completeTask(processor, event.data);
  };

  return worker;
}

而這個 Workers/cesiumWorkerBootstrapper.js 檔案中的 Worker,直接封裝了一個 RequireJS 庫,使用 require 函式去異步請求了當前 TaskProcessor 需要的真正 Worker,真正的 Worker 的 onmessage 就會替換掉 cesiumWorkerBootstapper 的 onmessage,

RequireJS 是 ESModule 尚未完全定稿前社區提供的一種模塊化方案,即著名的“異步模塊定義”的一種實作,有更高級的封裝庫(如 dojo.js),dojo.js 是 ArcGIS JsAPI 的底層依賴,

簡而言之,這個就是個跳轉器,方便接入 CesiumJS 其它模塊,做個簡單的橋梁,

image

2.2. 基本用法

在需要使用多執行緒的地方,需要實體化一個 TaskProcessor

const processor = new TaskProcessor(
  "path/to/your-worker.js", // worker 的路徑
  4 // 你希望這個 taskProcessor 最多激活多少個 WebWorker 在運行
);

// 當你需要用的時候,以傳遞 ArrayBuffer 的所有權情況為例
const needToDecodeData = https://www.cnblogs.com/onsummer/p/new Float32Array(/* ... */)
processor
  .scheduleTask(
    needToDecodeData,
    [needToDecodeData.buffer]
  )
  .then(result => {
    // result 上就有 taskProcessor 處理后的資料
  })

路徑如果相對于 Cesium 運行時的 Worker 目錄,如果你所處的環境能用 async/await 會更直觀:

const result = await processor.scheduleTask(
  typedArrayData, [typedArrayData.buffer]
)

TaskProcessor.prototype.scheduleTask 就是觸發 Worker 執行的方法,

2.3. 使用 WebAssembly

TaskProcessor 有一個方法用來初始化 WebAssembly 模塊:

processor.initWebAssemblyModule({
  modulePath: "ThirdParty/Workers/basis_transcoder.js",
  wasmBinaryFile: "ThirdParty/basis_transcoder.wasm",
}) // 回傳	

有一些任務,例如 draco 壓縮資料的解碼或者 basisu 紋理的解碼,需要依賴 wasm 模塊,必須等待 wasm 決議、創建完成才能執行 WebWorker,所以一般 TaskProcessor.prototype.initWebAssemblyModule 方法執行之后,才會呼叫 TaskProcessor.prototype.scheduleTask,見下面 Draco 決議的例子,

實際上,現在要使用 wasm 決議的資料也不過是 basisu 紋理與 draco 壓縮幾何資料而已,

① 例:解碼 draco 壓縮的幾何資料

Draco 壓縮幾何資料是一片以字串 "DRACO" 起頭的二進制資料,Draco 則是由 Google 開源的一套使用 C++ 開發的幾何資料壓縮庫,利用了熵編碼相關的演算法,

glTF 2.0 允許使用 Draco 擴展,3DTiles 的點云格式也允許使用這個擴展,

解碼 Draco 資料是由 DracoLoader.js 模塊匯出的“靜態類”DracoLoader 完成的,DracoLoader 上有幾個 decode 方法供使用,

如果是 glTF 中的 draco 資料,那么是由 ResourceCache.loadDraco 觸發的:

ResourceCache.loadDraco = function (options) {
  let dracoLoader = ResourceCache.get(cacheKey);
  
  dracoLoader = new GltfDracoLoader(/* ... */);

  ResourceCache.load({
    resourceLoader: dracoLoader,
  });
  /* ... */
};

// ========= 

GltfDracoLoader.prototype.load = function () {
  /* ... */
  
  // 層級較深,多重 Promise,見原始碼,這行不代表真實縮進
  const decodePromise = DracoLoader.decodeBufferView(decodeOptions);
  
  /* ... */
};

如果是 3DTiles 的點云格式,則是由 PntsLoader.prototype.load 方法觸發的:

function decodeDraco(loader, context) {
  const parsedContent = loader._parsedContent;
  const draco = parsedContent.draco;
  
  let decodePromise;
  /* ... */
  decodePromise = DracoLoader.decodePointCloud(draco, context);
  /* ... */
  
  /* ...后續處理... */
}

PntsLoader.prototype.load = function () {
  /* ... */
  const loader = this;
  this._promise = new Promise(function (resolve, reject) {
    /* ... */
    decodeDraco(loader, frameState.context).then(resolve).catch(reject);
    /* ... */
  });
};

以常見的 glTF 決議為例,也就是從 glTF 的 bufferView 中獲取的 draco 壓縮資料:

DracoLoader.decodeBufferView = function (options) {
  const decoderTaskProcessor = DracoLoader._getDecoderTaskProcessor();
  if (!DracoLoader._taskProcessorReady) {
    // The task processor is not ready to schedule tasks
    return;
  }

  return decoderTaskProcessor.scheduleTask(options, [options.array.buffer]);
};

DracoLoader._getDecoderTaskProcessor = function () {
  if (!defined(DracoLoader._decoderTaskProcessor)) {
    const processor = new TaskProcessor(
      "decodeDraco",
      DracoLoader._maxDecodingConcurrency
    );
    processor
      .initWebAssemblyModule({
        modulePath: "ThirdParty/Workers/draco_decoder_nodejs.js",
        wasmBinaryFile: "ThirdParty/draco_decoder.wasm",
      })
      .then(function () {
        DracoLoader._taskProcessorReady = true;
      });
    DracoLoader._decoderTaskProcessor = processor;
  }
  return DracoLoader._decoderTaskProcessor;
};

總會有那么一幀,DracoLoader 的靜態欄位 _taskProcessorReady 會在 wasm 模塊創建完成后被標記為 true,進而觸發 decoderTaskProcessor.scheduleTask 方法,啟動 draco_decoder_nodejs.js Worker 的解碼任務,

② 例:處理幾何資料

Primitive API 中的幾何體與 Globe/QuadtreePrimitive API 用到的地形網格(由高程瓦片采樣計算成幾何網格)都用到了 TaskProcessor 進行多執行緒處理幾何資料,

在幾個地形資料模塊中,可以看到 TaskProcessor 的使用,例如經典的 QuantizedMeshTerrainData

// 模塊作用域下
const createMeshTaskName = "createVerticesFromQuantizedTerrainMesh";
const createMeshTaskProcessorNoThrottle = new TaskProcessor(createMeshTaskName);
const createMeshTaskProcessorThrottle = new TaskProcessor(
  createMeshTaskName,
  TerrainData.maximumAsynchronousTasks
);

QuantizedMeshTerrainData.prototype.createMesh = function (options) {
  /* ... */
  
  const createMeshTaskProcessor = throttle
    ? createMeshTaskProcessorThrottle
    : createMeshTaskProcessorNoThrottle;
  const verticesPromise = createMeshTaskProcessor.scheduleTask(/* ... */)
  /* ...進一步處理多執行緒處理后的資料... */
};

Primitive API 的更新程序也用到了:

Primitive.prototype.update = function (frameState) {
  /* ... */
  if (this.asynchronous) {
    loadAsynchronous(this, frameState);
  } else { /* ... */ }
  /* ... */
};

let createGeometryTaskProcessors;
const combineGeometryTaskProcessor = new TaskProcessor("combineGeometry");
  
function loadAsynchronous(primitive, frameState) {
  if (primitive._state === PrimitiveState.READY) {
    if (!defined(createGeometryTaskProcessors)) {
      createGeometryTaskProcessors = new Array(numberOfCreationWorkers);
      for (i = 0; i < numberOfCreationWorkers; i++) {
        createGeometryTaskProcessors[i] = new TaskProcessor("createGeometry");
      }
    }
    /* ...進一步呼叫 taskProcessor 的 scheduleTask 方法... */
  } else if (primitive._state === PrimitiveState.CREATED) {
    /* ... */
    const promise = combineGeometryTaskProcessor.scheduleTask(/* ... */);
    /* ... */
  }
};

loadAsynchronous 這個函式中,會呼叫 Geometry 所需的 _workerName 創建 TaskProcessor

function loadAsynchronous(primitive, frameState) {
  /* ... */
  if (primitive._state === PrimitiveState.READY) {
    /* ... */
    let subTasks = [];
    for (i = 0; i < length; ++i) {
      /* ... */
      subTasks.push({
        moduleName: geometry._workerName,
        geometry: geometry,
      });
    }
    /* ...之后就會使用 subTasks 陣列并發啟動 TaskProcessor...  */
  } /* ... */
}

每一種 Geometry 都有一個自己的私有欄位 _workerName,指向運行時 WebWorker 目錄下的 ${_workerName}.js 檔案,例如:

function PolygonGeometry(options) {
  /* ... */
  this._workerName = "createPolygonGeometry";
  /* ... */
}

這里對多執行緒的介紹僅此一斑,但是差不多也講到了應用的大致方面,希望對讀者有所指引,

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

標籤:GIS

上一篇:OL Search - 一個 Openlayers API 快速訪問拓展

下一篇:ArcGIS工具 - 匯出資料庫結構

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

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

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

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more