成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

在 WASM WASI 上運(yùn)行 Rust 的九條規(guī)則,你知道哪條?

開發(fā) 前端
如果 WASM+WASI 在 2008 年就存在,我們就無需創(chuàng)建 Docker。這就是它如此重要的原因。服務(wù)器上的 Webassembly 是計(jì)算的未來。標(biāo)準(zhǔn)化的系統(tǒng)接口是缺失的一環(huán)。讓我們希望 WASI 能勝任這項(xiàng)任務(wù)。

在受限環(huán)境中運(yùn)行 Rust 會(huì)帶來挑戰(zhàn)。你的代碼可能無法訪問完整的操作系統(tǒng),例如 Linux、Windows 或 macOS。你可能對(duì)文件、網(wǎng)絡(luò)、時(shí)間、隨機(jī)數(shù)甚至內(nèi)存的訪問權(quán)限有限(或根本沒有)。我們將探索解決方法和解決方案。

本文的第一部分重點(diǎn)介紹在 “WASM WASI” 上運(yùn)行代碼,這是一種類似容器的環(huán)境。我們將看到 WASM WASI 本身可能(也可能不)有用。但是,它作為在瀏覽器或嵌入式系統(tǒng)中運(yùn)行 Rust 的第一步很有價(jià)值。

將代碼移植到 WASM WASI 上需要許多步驟和選擇。瀏覽這些選擇可能很耗時(shí)。錯(cuò)過一步會(huì)導(dǎo)致失敗。我們將通過提供九條規(guī)則來減少這種復(fù)雜性,我們將在后面詳細(xì)探討:

規(guī)則 1:準(zhǔn)備好失望:WASM WASI 很容易,但 - 現(xiàn)在 - 基本上沒用 - 除了作為墊腳石。

2019 年,Docker 聯(lián)合創(chuàng)始人 Solomon Hykes 發(fā)布了一條推文[1]:

如果 WASM+WASI 在 2008 年就存在,我們就無需創(chuàng)建 Docker。這就是它如此重要的原因。服務(wù)器上的 Webassembly 是計(jì)算的未來。標(biāo)準(zhǔn)化的系統(tǒng)接口是缺失的一環(huán)。讓我們希望 WASI 能勝任這項(xiàng)任務(wù)。

如今,如果你關(guān)注科技新聞,你就會(huì)看到像這樣的樂觀標(biāo)題:

如果 WASM WASI 真正準(zhǔn)備就緒并有用,每個(gè)人都會(huì)使用它。我們不斷看到這些標(biāo)題的事實(shí)表明它還沒有準(zhǔn)備好。換句話說,如果 WASM WASI 真的準(zhǔn)備好了,他們就不需要不斷強(qiáng)調(diào)它已經(jīng)準(zhǔn)備好了。

截至 WASI 預(yù)覽版 1,現(xiàn)狀如下:你可以訪問一些文件操作、環(huán)境變量,并可以訪問時(shí)間和隨機(jī)數(shù)生成。但是,不支持網(wǎng)絡(luò)功能。

WASM WASI 可能 對(duì)某些 AWS Lambda 風(fēng)格的 Web 服務(wù)有用,但即使那也還不確定。因?yàn)椋c WASM WASI 相比,你難道不更愿意將你的 Rust 代碼本地編譯并以一半的成本運(yùn)行兩倍的速度嗎?

也許 WASM WASI 對(duì)插件和擴(kuò)展有用。在基因組學(xué)領(lǐng)域,我有一個(gè)用于 Python 的 Rust 擴(kuò)展,我為 25 種不同的組合編譯它(5 個(gè)版本的 Python 跨 5 個(gè)操作系統(tǒng)目標(biāo))。即使這樣,我也沒有涵蓋所有可能的操作系統(tǒng)和芯片系列。我能用 WASM WASI 替換這些操作系統(tǒng)目標(biāo)嗎?不能,它會(huì)太慢。我能將 WASM WASI 作為一個(gè)第六個(gè)“萬能”目標(biāo)添加進(jìn)去嗎?也許可以,但如果我真的需要可移植性,我已經(jīng)被要求支持 Python,應(yīng)該直接使用 Python。

那么,WASM WASI 到底有什么用?目前,它的主要價(jià)值在于它是將代碼運(yùn)行在瀏覽器或嵌入式系統(tǒng)中的第一步。

規(guī)則 2:了解 Rust 目標(biāo)。

在規(guī)則 1 中,我順便提到了“操作系統(tǒng)目標(biāo)”。讓我們更深入地了解 Rust 目標(biāo) - 這不僅對(duì)于 WASM WASI 來說是必要的信息,而且對(duì)于一般的 Rust 開發(fā)也是如此。

在我的 Windows 機(jī)器上,我可以編譯一個(gè) Rust 項(xiàng)目以在 Linux 或 macOS 上運(yùn)行。類似地,從 Linux 機(jī)器上,我可以編譯一個(gè) Rust 項(xiàng)目以針對(duì) Windows 或 macOS。以下是我用于將 Linux 目標(biāo)添加到 Windows 機(jī)器并檢查它的命令:

rustup target add x86_64-unknown-linux-gnu
cargo check --target x86_64-unknown-linux-gnu

旁白:雖然 cargo check 驗(yàn)證代碼是否可以編譯,但構(gòu)建一個(gè)功能齊全的可執(zhí)行文件需要額外的工具。要從 Windows 交叉編譯到 Linux (GNU),你還需要安裝 Linux GNU C/C++ 編譯器和相應(yīng)的工具鏈。這可能很棘手。幸運(yùn)的是,對(duì)于我們關(guān)心的 WASM 目標(biāo),所需的工具鏈很容易安裝。

要查看 Rust 支持的所有目標(biāo),請(qǐng)使用以下命令:

rustc --print target-list

它將列出超過 200 個(gè)目標(biāo),包括 x86_64-unknown-linux-gnu、wasm32-wasip1 和 wasm32-unknown-unknown。

目標(biāo)名稱包含最多四個(gè)部分:CPU 系列、供應(yīng)商、操作系統(tǒng)和環(huán)境(例如,GNU 與 LVMM):

目標(biāo)名稱部分 - 來自作者的圖片

現(xiàn)在我們對(duì)目標(biāo)有所了解,讓我們繼續(xù)安裝我們需要的 WASM WASI 目標(biāo)。

規(guī)則 3:安裝 wasm32-wasip1 目標(biāo)和 WASMTIME,然后創(chuàng)建“Hello, WebAssembly!”。

要將我們的 Rust 代碼在瀏覽器之外的 WASM 上運(yùn)行,我們需要將目標(biāo)設(shè)置為 wasm32-wasip1(使用 WASI 預(yù)覽版 1 的 32 位 WebAssembly)。我們還將安裝 WASMTIME,這是一個(gè)允許我們?cè)跒g覽器之外使用 WASI 運(yùn)行 WebAssembly 模塊的運(yùn)行時(shí)。

rustup target add wasm32-wasip1
cargo install wasmtime-cli

為了測試我們的設(shè)置,讓我們使用 cargo new 創(chuàng)建一個(gè)新的“Hello, WebAssembly!” Rust 項(xiàng)目。這將初始化一個(gè)新的 Rust 包:

cargo new hello_wasi
cd hello_wasi

編輯 src/main.rs 使其內(nèi)容如下:

fn main() {
    #[cfg(not(target_arch = "wasm32"))]
    println!("Hello, world!");
    #[cfg(target_arch = "wasm32")]
    println!("Hello, WebAssembly!");
}

旁白:我們將在規(guī)則 4 中更深入地了解 #[cfg(...)] 屬性,該屬性允許條件編譯。

現(xiàn)在,使用 cargo run 運(yùn)行項(xiàng)目,你應(yīng)該看到 Hello, world! 打印到控制臺(tái)上。

接下來,創(chuàng)建一個(gè) .cargo/config.toml 文件,該文件指定 Rust 在針對(duì) WASM WASI 時(shí)應(yīng)該如何運(yùn)行和測試項(xiàng)目。

[target.wasm32-wasip1]
runner = "wasmtime run --dir ."

旁白:這個(gè) .cargo/config.toml 文件與主 Cargo.toml 文件不同,后者定義了你的項(xiàng)目的依賴項(xiàng)和元數(shù)據(jù)。

現(xiàn)在,如果你輸入:

cargo run --target wasm32-wasip1

你應(yīng)該看到 Hello, WebAssembly!。恭喜!你剛剛成功地在類似容器的 WASM WASI 環(huán)境中運(yùn)行了一些 Rust 代碼。

規(guī)則 4:了解條件編譯。

現(xiàn)在,讓我們研究一下 #[cfg(...)] - 這是在 Rust 中條件編譯代碼的重要工具。在規(guī)則 3 中,我們看到了:

fn main() {
    #[cfg(not(target_arch = "wasm32"))]
    println!("Hello, world!");
    #[cfg(target_arch = "wasm32")]
    println!("Hello, WebAssembly!");
}

#[cfg(...)] 行告訴 Rust 編譯器根據(jù)特定條件包含或排除某些代碼項(xiàng)。一個(gè)“代碼項(xiàng)”指的是代碼單元,例如函數(shù)、語句或表達(dá)式。

使用 #[cfg(…)] 行,你可以條件編譯你的代碼。換句話說,你可以為不同的情況創(chuàng)建代碼的不同版本。例如,在為 wasm32 目標(biāo)編譯時(shí),編譯器會(huì)忽略 #[cfg(not(target_arch = "wasm32"))] 塊,只包含以下內(nèi)容:

fn main() {
    println!("Hello, WebAssembly!");
}

你通過表達(dá)式指定條件,例如 target_arch = "wasm32"。支持的鍵包括 target_os 和 target_arch。有關(guān)支持的鍵的完整列表,請(qǐng)參閱 Rust 參考手冊(cè) 完整列表[2]。你還可以使用 Cargo 功能創(chuàng)建表達(dá)式,我們將在規(guī)則 6 中學(xué)習(xí)。

你可以使用邏輯運(yùn)算符 not、any 和 all 來組合表達(dá)式。Rust 的條件編譯不使用傳統(tǒng)的 if...then...else 語句。相反,你必須使用 #[cfg(...)] 及其否定來處理不同的情況:

#[cfg(not(target_arch = "wasm32"))]
...
#[cfg(target_arch = "wasm32")]
...

要條件編譯整個(gè)文件,請(qǐng)將 #![cfg(...)] 放置在文件的頂部。(注意“!”)。當(dāng)一個(gè)文件只與特定目標(biāo)或配置相關(guān)時(shí),這很有用。

你也可以在 Cargo.toml 中使用 cfg 表達(dá)式來?xiàng)l件包含依賴項(xiàng)。這允許你根據(jù)不同的目標(biāo)定制依賴項(xiàng)。例如,這表示“當(dāng)不針對(duì) wasm32 時(shí),依賴于具有 Rayon 的 Criterion”。

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.5.1", features = ["rayon"] }

規(guī)則 5:運(yùn)行常規(guī)測試,但使用 WASM WASI 目標(biāo)。

現(xiàn)在,讓我們嘗試在 WASM WASI 上運(yùn)行 你的 項(xiàng)目。如規(guī)則 3 中所述,為你的項(xiàng)目創(chuàng)建一個(gè) .cargo/config.toml 文件。它告訴 Cargo 如何在 WASM WASI 上運(yùn)行和測試你的項(xiàng)目。

[target.wasm32-wasip1]
runner = "wasmtime run --dir ."

接下來,你的項(xiàng)目 - 就像所有好的代碼一樣 - 應(yīng)該已經(jīng)包含測試[3]。我的 range-set-blaze 項(xiàng)目包含以下示例測試:

#[test]
fn insert_255u8() {
    let range_set_blaze = RangeSetBlaze::<u8>::from_iter([255]);
    assert!(range_set_blaze.to_string() == "255..=255");
}

現(xiàn)在,讓我們嘗試在 WASM WASI 上運(yùn)行你的項(xiàng)目的測試。使用以下命令:

cargo test --target wasm32-wasip1

如果這能正常工作,你可能就完成了 - 但它可能不會(huì)正常工作。當(dāng)我在 range-set-blaze 上嘗試這個(gè)命令時(shí),我得到了一條錯(cuò)誤消息,抱怨在 WASM 上使用 Rayon。

error: Rayon cannot be used when targeting wasi32. Try disabling default features.
  --> C:\Users\carlk\.cargo\registry\src\index.crates.io-6f17d22bba15001f\criterion-0.5.1\src\lib.rs:31:1
   |
31 | compile_error!("Rayon cannot be used when targeting wasi32. Try disabling default features.");

要修復(fù)此錯(cuò)誤,我們首先需要了解 Cargo 功能。

規(guī)則 6:了解 Cargo 功能。

為了解決像規(guī)則 5 中的 Rayon 錯(cuò)誤這樣的問題,了解 Cargo 功能如何工作非常重要。

在 Cargo.toml 中,一個(gè)可選的 [features] 部分允許你根據(jù)啟用的功能或禁用的功能來定義項(xiàng)目的不同配置或版本。例如,以下是 Criterion 基準(zhǔn)測試項(xiàng)目 的 Cargo.toml 文件的簡化部分:

[features]
default = ["rayon", "plotters", "cargo_bench_support"]
rayon = ["dep:rayon"]
plotters = ["dep:plotters"]
html_reports = []
cargo_bench_support = []
[dependencies]
#...
# 可選依賴項(xiàng)
rayon = { version = "1.3", optional = true }
plotters = { version = "^0.3.1", optional = true, default-features = false, features = [
    "svg_backend",
    "area_series",
    "line_series",
] }

這定義了四個(gè) Cargo 功能:rayon、plotters、html_reports 和 cargo_bench_support。由于每個(gè)功能都可以包含或排除,因此這四個(gè)功能創(chuàng)建了項(xiàng)目的 16 種可能的配置。還要注意特殊的默認(rèn) Cargo 功能。

一個(gè) Cargo 功能可以包含其他 Cargo 功能。在上面的示例中,特殊的 default Cargo 功能包含了另外三個(gè) Cargo 功能 - rayon、plotters 和 cargo_bench_support。

一個(gè) Cargo 功能可以包含一個(gè)依賴項(xiàng)。上面的 rayon Cargo 功能包含 rayon 箱子作為依賴包。

此外,依賴包可能擁有自己的 Cargo 功能。例如,上面的 plotters Cargo 功能包含 plotters 依賴包,并啟用了以下 Cargo 功能:svg_backend、area_series 和 line_series。

你可以在運(yùn)行 cargo check、cargo build、cargo run 或 cargo test 時(shí)指定要啟用或禁用的 Cargo 功能。例如,如果你正在使用 Criterion 項(xiàng)目并只想檢查 html_reports 功能,而不使用任何默認(rèn)功能,你可以運(yùn)行:

cargo check --no-default-features --features html_reports

此命令告訴 Cargo 不要默認(rèn)包含任何 Cargo 功能,而是專門啟用 html_reports Cargo 功能。

在你的 Rust 代碼中,你可以根據(jù)啟用的 Cargo 功能包含/排除代碼項(xiàng)。語法使用 #cfg(…),如規(guī)則 4 所示:

#[cfg(feature = "html_reports")]
SOME_CODE_ITEM

了解了 Cargo 功能之后,我們現(xiàn)在可以嘗試修復(fù)在 WASM WASI 上運(yùn)行測試時(shí)遇到的 Rayon 錯(cuò)誤。

規(guī)則 7:更改你能更改的東西:通過選擇 Cargo 功能解決依賴問題,64 位/32 位問題。

當(dāng)我們嘗試運(yùn)行 cargo test --target wasm32-wasip1 時(shí),錯(cuò)誤消息的一部分指出:Criterion ... Rayon cannot be used when targeting wasi32. Try disabling default features. 這表明我們應(yīng)該在針對(duì) WASM WASI 時(shí)禁用 Criterion 的 rayon Cargo 功能。

為此,我們需要在 Cargo.toml 中進(jìn)行兩個(gè)更改。首先,我們需要在 [dev-dependencies] 部分禁用 Criterion 的 rayon 功能。因此,這個(gè)起始配置:

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }

變成了這個(gè),我們顯式地關(guān)閉 Criterion 的默認(rèn)功能,然后啟用除 rayon 之外的所有 Cargo 功能。

[dev-dependencies]
criterion = { version = "0.5.1", features = [
        "html_reports",
        "plotters",
        "cargo_bench_support"
      ],
      default-features = false }

接下來,為了確保 rayon 仍然用于非 WASM 目標(biāo),我們?cè)?Cargo.toml 中添加了一個(gè)條件依賴項(xiàng),如下所示:

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.5.1", features = ["rayon"] }

一般來說,在針對(duì) WASM WASI 時(shí),你可能需要修改你的依賴項(xiàng)及其 Cargo 功能以確保兼容性。有時(shí)這個(gè)過程很簡單,但有時(shí)它可能很困難 - 甚至不可能,正如我們將在規(guī)則 8 中討論的那樣。

旁白:在本系列的下一篇文章中 - 關(guān)于瀏覽器中的 WASM - 我們將更深入地探討修復(fù)依賴項(xiàng)的策略。

再次運(yùn)行測試后,我們?cè)竭^了之前的錯(cuò)誤,卻遇到了一個(gè)新的錯(cuò)誤,這是一種進(jìn)步!

#[test]
fn test_demo_i32_len() {
    assert_eq!(demo_i32_len(i32::MIN..=i32::MAX), u32::MAX as usize + 1);
                                                  ^^^^^^^^^^^^^^^^^^^^^ attempt to compute
`usize::MAX + 1_usize`, which would overflow
}

編譯器抱怨 u32::MAX as usize + 1 溢出了。在 64 位 Windows 上,該表達(dá)式不會(huì)溢出,因?yàn)?usize 與 u64 相同,并且可以容納 u32::MAX as usize + 1。但是,WASM 是一個(gè) 32 位環(huán)境,因此 usize 與 u32 相同,該表達(dá)式大了一個(gè)。

這里的解決方法是用 u64 替換 usize,確保表達(dá)式不會(huì)溢出。更一般地說,編譯器不會(huì)總是捕獲這些問題,因此審查你對(duì) usize 和 isize 的使用非常重要。如果你指的是 Rust 數(shù)據(jù)結(jié)構(gòu)的大小或索引,usize 是正確的。但是,如果你處理的值超過了 32 位限制,你應(yīng)該使用 u64 或 i64。

旁白:在 32 位環(huán)境中,Rust 數(shù)組、Vec、BTreeSet 等只能容納最多 232?1=4,294,967,295 個(gè)元素。

因此,我們已經(jīng)解決了依賴問題并解決了 usize 溢出問題。但是,我們能修復(fù)所有問題嗎?不幸的是,答案是否定的。

規(guī)則 8:接受你無法更改所有東西:網(wǎng)絡(luò)、Tokio、Rayon 等。

WASM WASI 預(yù)覽版 1(當(dāng)前版本)支持文件訪問(在指定目錄內(nèi))、讀取環(huán)境變量以及處理時(shí)間和隨機(jī)數(shù)。但是,與你可能從完整操作系統(tǒng)中期望的功能相比,它的功能有限。

如果你的項(xiàng)目需要訪問網(wǎng)絡(luò)、使用 Tokio 進(jìn)行異步任務(wù)或使用 Rayon 進(jìn)行多線程,不幸的是,這些功能在預(yù)覽版 1 中不受支持。

幸運(yùn)的是,WASM WASI 預(yù)覽版 2 預(yù)計(jì)將改進(jìn)這些限制,提供更多功能,包括對(duì)網(wǎng)絡(luò)和可能異步任務(wù)的更好支持。

規(guī)則 9:將 WASM WASI 添加到你的 CI(持續(xù)集成)測試中。

因此,你的測試在 WASM WASI 上通過了,你的項(xiàng)目也成功運(yùn)行了。你完成了?還沒有。因?yàn)?,正如我喜歡說的:

如果不在 CI 中,它就不存在。

持續(xù)集成 (CI) 是一個(gè)系統(tǒng),它可以在你每次更新代碼時(shí)自動(dòng)運(yùn)行你的測試,確保你的代碼能夠繼續(xù)按預(yù)期工作。通過將 WASM WASI 添加到你的 CI 中,你可以保證未來的更改不會(huì)破壞你的項(xiàng)目與 WASM WASI 目標(biāo)的兼容性。

在我的情況下,我的項(xiàng)目托管在 GitHub 上,我使用 GitHub Actions 作為我的 CI 系統(tǒng)。以下是我添加到 .github/workflows/ci.yml 中的配置,用于在我的項(xiàng)目上測試 WASM WASI:

test_wasip1:
      name: Test WASI P1
      runs-on: ubuntu-latest
      steps:
        - name: Checkout
          uses: actions/checkout@v4
        - name: Set up Rust
          uses: dtolnay/rust-toolchain@master
          with:
            toolchain: stable
            targets: wasm32-wasip1
        - name: Install Wasmtime
          run: |
            curl https://wasmtime.dev/install.sh -sSf | bash
            echo "${HOME}/.wasmtime/bin" >> $GITHUB_PATH
        - name: Run WASI tests
          run: cargo test --verbose --target wasm32-wasip1

通過將 WASM WASI 集成到 CI 中,我可以放心地向我的項(xiàng)目添加新代碼。CI 將自動(dòng)測試所有代碼在未來繼續(xù)支持 WASM WASI。

因此,這就是將你的 Rust 代碼移植到 WASM WASI 的九條規(guī)則。以下是我對(duì)移植到 WASM WASI 的感受:

不好之處:

  • 在 WASM WASI 上運(yùn)行在今天幾乎沒有實(shí)用價(jià)值。但是,它有潛力在明天變得有用。
  • 在 Rust 中,有一句常見的說法:“如果它可以編譯,它就可以工作。”不幸的是,這并不總是適用于 WASM WASI。如果你使用了不支持的功能,比如網(wǎng)絡(luò)功能,編譯器將不會(huì)捕獲錯(cuò)誤。相反,它將在運(yùn)行時(shí)失敗。例如,這段代碼可以在 WASM WASI 上編譯和運(yùn)行,但始終返回錯(cuò)誤,因?yàn)椴恢С志W(wǎng)絡(luò)功能。
use std::net::TcpStream;
fn main() {
    match TcpStream::connect("crates.io:80") {
        Ok(_) => println!("Successfully connected."),
        Err(e) => println!("Failed to connect: {e}"),
    }
}

好之處:

  • 在 WASM WASI 上運(yùn)行是將代碼運(yùn)行在瀏覽器和嵌入式系統(tǒng)中的一個(gè)很好的第一步。
  • 你可以在 WASM WASI 上運(yùn)行 Rust 代碼,而無需移植到 no_std。(移植到 no_std 是本系列文章的第三部分的主題。)
  • 你可以在 WASM WASI 上運(yùn)行標(biāo)準(zhǔn)的 Rust 測試,這使得驗(yàn)證你的代碼變得很容易。
  • .cargo/config.toml 文件和 Rust 的 --target 選項(xiàng)使得在不同的目標(biāo)上配置和運(yùn)行你的代碼變得非常簡單 - 包括 WASM WASI。

參考資料

[1] 發(fā)布了一條推文: https://x.com/solomonstre/status/1111004913222324225

[2] 完整列表: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options

[3] 你的項(xiàng)目 - 就像所有好的代碼一樣 - 應(yīng)該已經(jīng)包含測試: https://doc.rust-lang.org/rust-by-example/testing.html

責(zé)任編輯:武曉燕 來源: Rust開發(fā)筆記
相關(guān)推薦

2024-01-05 09:08:48

代碼服務(wù)管理

2020-07-29 07:52:25

編程開發(fā)IT

2024-04-17 08:05:18

C#并發(fā)設(shè)計(jì)

2025-04-27 08:06:50

2023-09-01 10:43:22

IT外包企業(yè)

2025-01-22 00:00:00

異常catch編程

2022-01-04 05:51:03

C++Python開發(fā)

2011-03-31 09:22:56

c++

2019-07-09 13:42:12

數(shù)據(jù)備份云計(jì)算系統(tǒng)

2011-05-16 13:44:11

C++

2011-08-29 16:05:07

高性能SQL語句SQL Server

2019-09-30 08:00:00

圖數(shù)據(jù)庫數(shù)據(jù)庫

2011-03-24 12:32:15

數(shù)據(jù)庫性能優(yōu)化

2022-02-07 11:24:08

云安全云計(jì)算

2023-06-06 07:17:44

云變化管理策略

2015-12-31 10:00:41

Java日志記錄規(guī)則

2016-10-28 13:21:36

2023-11-06 15:04:07

Flutter開發(fā)技巧

2023-11-04 12:08:40

Flutter事件

2020-02-28 15:19:19

CIOCISO安全
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 激情五月婷婷综合 | 一区二区三区免费在线观看 | 国产精品欧美一区二区三区不卡 | 99精品欧美一区二区三区综合在线 | 国产精品欧美大片 | 欧美成人精品一区二区男人看 | 婷婷亚洲综合 | 亚洲一区二区三区在线视频 | 狠狠色综合网站久久久久久久 | 欧美视频免费 | 成人免费在线视频 | 亚洲精品乱码久久久久久按摩观 | 97日韩精品 | 国产日韩欧美 | 在线观看第一页 | 蜜臀网站 | 日韩一区二区在线视频 | 国产精品久久久久久婷婷天堂 | 午夜视频免费在线观看 | www.国产.com | 日本韩国欧美在线观看 | 超碰免费在线观看 | 亚洲成人三区 | 深夜福利影院 | 久久一级 | 久久av一区二区三区 | 婷婷色国产偷v国产偷v小说 | 久久久久久国产精品免费免费男同 | 日本三级全黄三级a | 人人干人人玩 | www日本在线播放 | 亚洲看片网站 | 日韩精品免费在线观看 | 极品电影院 | 中文字幕福利 | 91天堂 | 欧美aaaaa| 91国在线视频 | 成人国产精品色哟哟 | av在线二区| 日韩一二区 |