【C++組件】Elasticsearch 安裝及使用

🌈 個人主頁:Zfox_
🔥 系列專欄:C++框架/庫

目錄

  • 🔥 介紹
  • 🔥 ES 安裝
    • 🦋 安裝 kibana
    • 🦋 ES 客戶端的安裝
  • 🔥 ES 核心概念
    • 🦋 索引(Index)
    • 🦋 類型(Type)
    • 🦋 字段(Field)
    • 🦋 映射(mapping)
    • 🦋 文檔 (document)
  • 🔥 Kibana 訪問 es 進行測試
        • 通過網頁訪問 kibana
  • 🔥 ES 客戶端接口介紹
  • 🔥 入門案例
  • 🔥 ES 客戶端 API 二次封裝思想
  • 🔥 共勉

🔥 介紹

Elasticsearch, 簡稱 ES,它是個開源分布式搜索引擎,它的特點有:分布式,零配置,自動發現,索引自動分片,索引副本機制, restful 風格接口,多數據源,自動搜索負載等。它可以近乎實時的存儲、檢索數據;本身擴展性很好,可以擴展到上百臺服務器,處理 PB 級別的數據。 es 也使用 Java 開發并使用 Lucene 作為其核心來實現所有索引和搜索的功能,但是它的目的是通過簡單的 RESTful API 來隱藏 Lucene 的復雜性,從而讓全文搜索變得簡單。

Elasticsearch 是面向文檔 (document oriented) 的,這意味著它可以存儲整個對象或文檔(document)。然而它不僅僅是存儲,還會索引 (index) 每個文檔的內容使之可以被搜索。在 Elasticsearch 中,你可以對文檔(而非成行成列的數據)進行索引、搜索、排序、過濾。

🔥 ES 安裝

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
# 上邊的添加方式會導致一個 apt-key 的警告,如果不想報警告使用下邊這個
curl -s https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --no-default-keyring --keyring gnupgring:/etc/apt/trusted.gpg.d/icsearch.gpg --import# 添加鏡像源倉庫
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elasticsearch.list
# 更新軟件包列表
sudo apt update
# 安裝 es
sudo apt-get install elasticsearch=7.17.21
# 啟動 es
sudo systemctl start elasticsearch
# 安裝 ik 分詞器插件
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install https://get.infini.cloud/elasticsearch/analysis-ik/7.17.21
# 重啟
sudo systemctl restart elasticsearch
# 開機自啟動
sudo systemctl enable elasticsearch
# 查看 es 服務的狀態
sudo systemctl status elasticsearch.service
# 驗證 es 是否安裝成功
curl -X GET "http://localhost:9200/"

設置外網訪問:如果新配置完成的話,默認只能在本機進行訪問。

vim /etc/elasticsearch/elasticsearch.yml# 新增配置
network.host: 0.0.0.0
http.port: 9200
cluster.initial_master_nodes: ["node-1"]

瀏覽器訪問 http://xxx.xxx.xx.xx:9200/
在這里插入圖片描述

🦋 安裝 kibana

使用 apt 命令安裝 Kibana。
sudo apt install kibana配置 Kibana(可選):
根據需要配置 Kibana。配置文件通常位于 /etc/kibana/kibana.yml。可能需要設置如服務器地址、端口、 Elasticsearch URL 等。
sudo vim /etc/kibana/kibana.yml
例如,你可能需要設置 Elasticsearch 服務的 URL: 大概 32 行左右
elasticsearch.host: "http://localhost:9200"啟動 Kibana 服務:
安裝完成后,啟動 Kibana 服務
sudo systemctl start kibana設置開機自啟(可選):
如果你希望 Kibana 在系統啟動時自動啟動,可以使用以下命令來啟用自啟動
sudo systemctl enable kibana驗證安裝:
使用以下命令檢查 Kibana 服務的狀態
sudo systemctl status kibana訪問 Kibana:
在瀏覽器中訪問 Kibana,通常是 http://<your-ip>:5601

🦋 ES 客戶端的安裝

需要先安裝 MicroHTTPD 庫
不然 make 的時候編譯出錯:這是子模塊 googletest 沒有編譯安裝

sudo apt-get install libmicrohttpd-dev
# 克隆代碼
git clone https://github.com/seznam/elasticlient
# 切換目錄
cd elasticlient
# 更新子模塊
git submodule update --init --recursive
# 編譯代碼
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
# 安裝
make install

🔥 ES 核心概念

🦋 索引(Index)

一個索引就是一個擁有幾分相似特征的文檔的集合。比如說,你可以有一個客戶數據的索引,一個產品目錄的索引,還有一個訂單數據的索引。一個索引由一個名字來標識(必須全部是小寫字母的),并且當我們要對應于這個索引中的文檔進行索引、搜索、更新和刪除的時候,都要使用到這個名字。在一個集群中,可以定義任意多的索引。

🦋 類型(Type)

在一個索引中,你可以定義一種或多種類型。一個類型是你的索引的一個邏輯上的分類/分區,其語義完全由你來定。通常,會為具有一組共同字段的文檔定義一個類型。比如說,我們假設你運營一個博客平臺并且將你所有的數據存儲到一個索引中。在這個索引中,你可以為用戶數據定義一個類型,為博客數據定義另一個類型,為評論數據定義另一個類型…

🦋 字段(Field)

字段相當于是數據表的字段,對文檔數據根據不同屬性進行的分類標識。
在這里插入圖片描述

🦋 映射(mapping)

映射是在處理數據的方式和規則方面做一些限制,如某個字段的數據類型、默認值、分析器、是否被索引等等,這些都是映射里面可以設置的,其它就是處理 es 里面數據的一些使用規則設置也叫做映射,按著最優規則處理數據對性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能對性能更好。

在這里插入圖片描述

🦋 文檔 (document)

一個文檔是一個可被索引的基礎信息單元。比如,你可以擁有某一個客戶的文檔,某一個產品的一個文檔或者某個訂單的一個文檔。文檔以 JSON(Javascript ObjectNotation)格式來表示,而 JSON 是一個到處存在的互聯網數據交互格式。在一個 index/type 里面,你可以存儲任意多的文檔。一個文檔必須被索引或者賦予一個索引的 type。

Elasticsearch 與傳統關系型數據庫相比如下:
在這里插入圖片描述

🔥 Kibana 訪問 es 進行測試

通過網頁訪問 kibana

在這里插入圖片描述

創建索引庫

POST /user/_doc
{"settings" : {"analysis" : {"analyzer" : {"ik" : {"tokenizer" : "ik_max_word"	// 最大粒度分詞  - 你好 你 好 你好}}}},"mappings" : {"dynamic" : true,					// 自動更新"properties" : {"nickname" : {"type" : "text",			// 字段是文本類型"analyzer" : "ik_max_word"	// 使用中文分詞器},"user_id" : {"type" : "keyword",			// 是一個文本類型,但是是關鍵字,不進行分詞"analyzer" : "standard"		// 使用默認標準分詞器},"phone" : {"type" : "keyword","analyzer" : "standard"},"description" : {"type" : "text","enabled" : false			// 僅做存儲,不做搜索},"avatar_id" : {"type" : "keyword","enabled" : false}}}
}

新增數據:

POST /user/_doc/_bulk
{"index":{"_id":"1"}}
{"user_id" : "USER4b862aaa-2df8654a-7eb4bb65-e3507f66","nickname" : "昵稱 1","phone" : "手機號 1","description" : "簽名 1","avatar_id" : "頭像 1"}
{"index":{"_id":"2"}}
{"user_id" : "USER14eeeaa5-442771b9-0262e455-e4663d1d","nickname" : "昵稱 2","phone" : "手機號 2","description" : "簽名 2","avatar_id" : "頭像 2"}
{"index":{"_id":"3"}}
{"user_id" : "USER484a6734-03a124f0-996c169dd05c1869","nickname" : "昵稱 3","phone" : "手機號 3","description" : "簽名 3","avatar_id" : "頭像 3"}
{"index":{"_id":"4"}}
{"user_id" : "USER186ade83-4460d4a6-8c08068f-83127b5d","nickname" : "昵稱 4","phone" : "手機號 4","description" : "簽名 4","avatar_id" : "頭像 4"}
{"index":{"_id":"5"}}
{"user_id" : "USER6f19d074-c33891cf-23bf5a83-57189a19","nickname" : "昵稱 5","phone" : "手機號 5","description" : "簽名 5","avatar_id" : "頭像 5"}
{"index":{"_id":"6"}}
{"user_id" : "USER97605c64-9833ebb7-d0455353-35a59195","nickname" : "昵稱 6","phone" : "手機號 6","description" : "簽名 6","avatar_id" : "頭像 6"}

查看并搜索數據

GET /user/_doc/_search?pretty
{"query" : {"bool" : {"must_not" : [		// 必須不遵循的條件{"terms" : {"user_id.keyword" : ["USER4b862aaa-2df8654a-7eb4bb65-e3507f66","USER14eeeaa5-442771b9-0262e455-e4663d1d","USER484a6734-03a124f0-996c169dd05c1869"]}}],"should" : [		// 應該遵循的條件 有任意一個成功就ok{"match" : {"user_id" : "昵稱"}},{"match" : {"nickname" : "昵稱"}},{"match" : {"phone" : "昵稱"}}]}}
}

terms: 完全匹配
match:分詞匹配

過濾條件,是我的好友就過濾掉,在搜索好友進行添加的時候,就可以過濾掉

"user_id.keyword"  keyword 不進行分詞
"USER4b862aaa-2df8654a-7eb4bb65-e3507f66",
"USER14eeeaa5-442771b9-0262e455-e4663d1d",
"USER484a6734-03a124f0-996c169dd05c1869"

刪除索引:

DELETE /user
POST /user/_doc/_search
{"query": {"match_all": {}}
}

🔥 ES 客戶端接口介紹

/*** Perform search on nodes until it is successful. Throws exception if all nodes* has failed to respond.* \param indexName specification of an Elasticsearch index.* \param docType specification of an Elasticsearch document type.* \param body Elasticsearch request body.* \param routing Elasticsearch routing. If empty, no routing has been used.** \return cpr::Response if any of node responds to request.* \throws ConnectionException if all hosts in cluster failed to respond.*/cpr::Response search(const std::string &indexName,  索引名稱 userconst std::string &docType,	索引類型 docconst std::string &body,		請求正文,json字符串const std::string &routing = std::string());/*** Get document with specified id from cluster. Throws exception if all nodes* has failed to respond.* \param indexName specification of an Elasticsearch index.* \param docType specification of an Elasticsearch document type.* \param id Id of document which should be retrieved.* \param routing Elasticsearch routing. If empty, no routing has been used.** \return cpr::Response if any of node responds to request.* \throws ConnectionException if all hosts in cluster failed to respond.*/cpr::Response get(const std::string &indexName,const std::string &docType,const std::string &id = std::string(),const std::string &routing = std::string());/*** Index new document to cluster. Throws exception if all nodes has failed to respond.* \param indexName specification of an Elasticsearch index.* \param docType specification of an Elasticsearch document type.* \param body Elasticsearch request body.* \param id Id of document which should be indexed. If empty, id will be generated*           automatically by Elasticsearch cluster.* \param routing Elasticsearch routing. If empty, no routing has been used.** \return cpr::Response if any of node responds to request.* \throws ConnectionException if all hosts in cluster failed to respond.*/ 創建索引 新增數據cpr::Response index(const std::string &indexName,		索引名稱const std::string &docType,			類型const std::string &id,				自己制定或者es生成數據idconst std::string &body,			請求正文const std::string &routing = std::string());/*** Delete document with specified id from cluster. Throws exception if all nodes* has failed to respond.* \param indexName specification of an Elasticsearch index.* \param docType specification of an Elasticsearch document type.* \param id Id of document which should be deleted.* \param routing Elasticsearch routing. If empty, no routing has been used.** \return cpr::Response if any of node responds to request.* \throws ConnectionException if all hosts in cluster failed to respond.*/cpr::Response remove(const std::string &indexName,const std::string &docType,const std::string &id,const std::string &routing = std::string());/*** Initialize the Client.* \param hostUrlList  Vector of URLs of Elasticsearch nodes in one Elasticsearch cluster.*  Each URL in vector should ends by "/".* \param timeout      Elastic node connection timeout.*/explicit Client(const std::vector<std::string> &hostUrlList,std::int32_t timeout = 6000);

🔥 入門案例

針對上邊通過 kibana 添加的數據通過客戶端 api 進行一次數據獲取。

#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <iostream>int main() 
{// 1. 構造 ES 客戶端elasticlient::Client client({"http://127.0.0.1:9200/"});// 2. 發起搜索請求try {cpr::Response resp = client.search("user", "_doc", "{\"query\": { \"match_all\": {} }}");// 3. 打印響應狀態碼和響應正文std::cout << resp.status_code << std::endl;std::cout << resp.text << std::endl;} catch (std::exception &e) {std::cout << "請求失敗: " << e.what() << std::endl;return -1;}return 0;
}

🔥 ES 客戶端 API 二次封裝思想

封裝客戶端 api 主要是因為,客戶端只提供了基礎的數據存儲獲取調用功能,無法根據我們的思想完成索引的構建,以及查詢正文的構建,需要使用者自己組織好 json 進行序列化后才能作為正文進行接口的調用。

而封裝的目的就是簡化用戶的操作,將索引的 json 正文構造,以及查詢搜索的正文構造操作給封裝起來,使用者調用接口添加字段就行,不用關心具體的 json 數據格式。

封裝內容:

  • 索引構造過程的封裝
    • 索引正文構造過程,大部分正文都是固定的,唯一不同的地方是各個字段不同的名稱以及是否只存儲不索引這些選項,因此重點關注以下幾個點即可:
  • 字段類型: type : text / keyword (目前只用到這兩個類型)
  • 是否索引: enable : true/false
  • 索引的話分詞器類型: analyzer : ik_max_word / standard
  • 新增文檔構造過程的封裝
    • 新增文檔其實在常規下都是單條新增,并非批量新增,因此直接添加字段和值就行
  • 文檔搜索構造過程的封裝
    • 搜索正文構造過程,我們默認使用條件搜索,我們主要關注的兩個點:
  • 應該遵循的條件是什么: should 中有什么
  • 條件的匹配方式是什么: match 還是 term/terms,還是 wildcard
  • 過濾的條件字段是什么: must_not 中有什么
  • 過濾的條件字段匹配方式是什么: match 還是 wildcard,還是 term/terms

整個封裝的過程其實就是對 Json::Value 對象的一個組織的過程,并無太大的難點。

elasticsearch.hpp

#include <iostream>
#include <memory>
#include <elasticlient/client.h>
#include <json/json.h>
#include <cpr/cpr.h>
#include "logger.hpp"// 實現字符串的序列化
bool Serialize(const Json::Value &val, std::string &body)
{std::stringstream ss;// 先實例化一個工廠類對象Json::StreamWriterBuilder swb;// 再使用工廠類對象來生產派生類std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());int ret = sw->write(val, &ss);if(ret != 0){std::cout << "Json Serialize failed!" << std::endl;return false;}body = ss.str();return true;
}// 實現json字符串的反序列化
bool UnSerialize(const std::string &body, Json::Value &val)
{// 實例化工廠類對象Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());std::string errs;bool ret = cr->parse(body.c_str(), body.c_str() + body.size(), &val, &errs);if(ret == false){std::cout << "Json UnSerialize failed! " << errs << std::endl;return false;}return true;
}// 構造索引
class ESIndex {
public:ESIndex(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) : _name(index_name), _type(type), _client(client){Json::Value analysis;Json::Value analyzer;Json::Value ik;Json::Value tokenizer;tokenizer["tokenizer"] = "ik_max_word";ik["ik"] = tokenizer;analyzer["analyzer"] = ik;analysis["analysis"] = analyzer;_index["settings"] = analysis;}ESIndex& append(const std::string &key, const std::string &type = "text", const std::string &analyzer = "ik_max_word", bool enabled = true) {Json::Value fields;fields["type"] = type;fields["analyzer"] = analyzer;if (enabled == false) fields["enabled"] = enabled;_propertis[key] = fields;return *this;}bool create(const std::string &index_id = "default_index_id") {Json::Value mappings;mappings["dynamic"] = true;mappings["properties"] = _propertis;_index["mappings"] = mappings;std::string body;bool ret = Serialize(_index, body);if (ret == false) {LOG_ERROR("索引序列化失敗!");return false;}// 發起搜索請求try {cpr::Response resp = _client->index(_name, _type, index_id, body);if (resp.status_code < 200 || resp.status_code >= 300) {LOG_ERROR("創建ES索引 {} 失敗, 響應狀態碼異常: {}", _name, resp.status_code);return false;}} catch (std::exception &e) {LOG_ERROR("創建ES索引 {} 失敗: {}", _name, e.what());return false;}return true;}private:std::string _name;std::string _type;Json::Value _propertis;Json::Value _index;std::shared_ptr<elasticlient::Client> _client;
};// 添加數據
class ESInsert {
public:ESInsert(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) : _name(index_name), _type(type), _client(client){}ESInsert& append(const std::string &key, const std::string &val) {_item[key] = val;return *this;}bool insert(const std::string &id = "") {std::string body;bool ret = Serialize(_item, body);if (ret == false) {LOG_ERROR("索引序列化失敗!");return false;}// 發起搜索請求try {cpr::Response resp = _client->index(_name, _type, id, body);if (resp.status_code < 200 || resp.status_code >= 300) {LOG_ERROR("新增數據 {} 失敗, 響應狀態碼異常: {}", body, resp.status_code);return false;}} catch (std::exception &e) {LOG_ERROR("新增數據 {} 失敗: {}", body, e.what());return false;}return true;}private:std::string _name;std::string _type;Json::Value _item;std::shared_ptr<elasticlient::Client> _client;
};// 刪除數據
class ESRemove {public:ESRemove(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) : _name(index_name), _type(type), _client(client){}bool remove(const std::string &id) {// 發起請求try {cpr::Response resp = _client->remove(_name, _type, id);if (resp.status_code < 200 || resp.status_code >= 300) {LOG_ERROR("刪除數據 {} 失敗, 響應狀態碼異常: {}", id, resp.status_code);return false;}} catch (std::exception &e) {LOG_ERROR("刪除數據 {} 失敗: {}", id, e.what());return false;}return true;}private:std::string _name;std::string _type;std::shared_ptr<elasticlient::Client> _client;
};// 搜索數據
class ESSearch {
public:ESSearch(std::shared_ptr<elasticlient::Client> &client, const std::string &index_name, const std::string &type) : _name(index_name), _type(type), _client(client){}// 必須不遵循的條件ESSearch& append_must_not_terms(const std::string &key, const std::vector<std::string> &vals) {Json::Value fields;for (const auto &val : vals) {fields[key].append(val);}Json::Value terms;terms["terms"] = fields;_must_not.append(terms);return *this;}ESSearch& append_should_match(const std::string &key, const std::string &val) {Json::Value field;field[key] = val;Json::Value match;match["match"] = field;_should.append(match);return *this;}Json::Value search() {Json::Value cond;if (!_must_not.empty()) cond["must_not"] = _must_not; if (!_should.empty()) cond["should"] = _should; Json::Value query;query["bool"] = cond;Json::Value root;root["query"] = query;std::string body;bool ret = Serialize(root, body);if (ret == false) {LOG_ERROR("索引序列化失敗!");return Json::Value();}cpr::Response resp;// 發起搜索請求try {resp = _client->search(_name, _type, body);if (resp.status_code < 200 || resp.status_code >= 300) {LOG_ERROR("檢索數據 {} 失敗, 響應狀態碼異常: {}", body, resp.status_code);return Json::Value();}} catch (std::exception &e) {LOG_ERROR("檢索數據 {} 失敗: {}", body, e.what());return Json::Value();}// 反序列化響應正文Json::Value json_res;ret = UnSerialize(resp.text, json_res);if (ret == false) {LOG_ERROR("檢索數據 {} 結果反序列化失敗", resp.text);return Json::Value();}return json_res["hits"]["hits"];}private:std::string _name;std::string _type;Json::Value _must_not;                          // 必須不遵循的條件Json::Value _should;                            // 應該遵循的條件std::shared_ptr<elasticlient::Client> _client;
};

🔥 共勉

😋 以上就是我對 【C++組件】Elasticsearch 安裝及使用 的理解, 覺得這篇博客對你有幫助的,可以點贊收藏關注支持一波~ 😉
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/919792.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/919792.shtml
英文地址,請注明出處:http://en.pswp.cn/news/919792.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

項目:電動車報警器

1.項目需求 點擊遙控器A按鍵&#xff0c;系統進入警戒模式&#xff0c;一旦檢測到震動(小偷偷車)&#xff0c;則喇叭發出聲響報警&#xff0c;嚇退小偷。 點擊遙控器B按鍵&#xff0c;系統退出警戒模式&#xff0c;再怎么搖晃系統都不會報警&#xff0c;否則系統一直發出尖叫&a…

GDSFactory環境配置(PyCharm+Git+KLayout)

1、安裝 PyCharm 和 KLayout 安裝 PyCharm&#xff08;官網社區版即可&#xff09;和 KLayout&#xff08;官網最新版&#xff09;&#xff0c;這兩款軟件均開源&#xff0c;安裝操作簡單&#xff0c;這里不再贅述。&#xff08;注意&#xff1a;PyCharm軟件是否安裝成功以能否…

STM32 定時器(輸出模式)

?? ?一、輸出模式總覽?STM32定時器的輸出比較模式通過比較計數器&#xff08;CNT&#xff09;與捕獲/比較寄存器&#xff08;CCRx&#xff09;的值&#xff0c;控制輸出引腳&#xff08;OCx&#xff09;的電平狀態。六種模式定義如下&#xff1a;?模式宏??觸發動作?&am…

嵌入式硬件篇---手柄

手柄原理&#xff1a;手柄遙控的原理其實可以簡單理解為 “信號的發送與接收”&#xff0c;就像兩個人用對講機聊天&#xff0c;一方說話&#xff08;發送信號&#xff09;&#xff0c;另一方聽話&#xff08;接收信號&#xff09;&#xff0c;然后根據內容行動。下面用通俗的方…

數據庫架構開發知識庫體系

摘要面向初創與企業團隊&#xff0c;系統梳理數據庫與數據平臺從采集、傳輸、存儲、處理、服務化到治理與安全的全鏈路。覆蓋 OLTP/OLAP/HTAP、湖倉一體與實時數據棧&#xff0c;結合國內外工具與方法論&#xff0c;給出架構選型、性能優化、可靠性與合規要點&#xff0c;以及可…

在Excel和WPS表格中合并多個單元格這樣最快

如果要把WPS表格和Excel中多個單元格的數據合成到一個單元格中&#xff0c;不用函數&#xff0c;只需要先寫輸入公式&#xff0c;然后在各個單元格之間輸入&符號即可。&#xff08;當然&#xff0c;&符號不只是連接單元格的數據&#xff0c;也可以直接輸入內容連接&…

在嵌入式上用 C++14實現簡單HSM狀態機

文章目錄概述為什么要遷移到 C&#xff0c;以及 C 的陷阱目標與挑戰為什么不能直接使用 std::function&#xff1f;解決方案&#xff1a;POD 回調與模板 Trampoline核心設計模板 trampoline 實現兩種成員函數綁定策略1. **Per-Transition Context&#xff08;每個狀態轉移綁定一…

【unity】Obfuz加固混淆日志還原解析方案ObfuzResolver

Github | Gitee ObfuzResolver是基于obfuz-tools針對Obfuz的一項輔助工具&#xff0c;方便開發者在unity編輯器中或者運行時快捷將使用Obfuz混淆加固后的日志信息還原為原始信息&#xff0c;以輔助開發者快速定位Bug。 特性 支持unity編輯器模式下還原被加固混淆的日志信息&a…

2025DevOps平臺趨勢解讀:哪些DevOps工具正在引領行業變革?

DevOps平臺已成為企業提升研發效能、實現敏捷交付的核心支柱。2025年DevOps領域正經歷深刻變革&#xff0c;平臺能力正從“工具鏈整合”向“價值流智能中樞”躍升。01. 2025Devops平臺趨勢解讀“全棧式”與“模塊化/可組合”的平衡&#xff1a;企業既需要能覆蓋開發、測試、部署…

第二階段Winform-4:MDI窗口,布局控件,分頁

1_MDI窗口 &#xff08;1&#xff09;MDI是指將多控件窗體在同一窗體中打開,可以設置重疊打開&#xff0c;平捕打開等&#xff0c;MDI窗體&#xff08;Multiple-Document Interface&#xff0c;多文檔界面&#xff09;用于同時顯示多個文檔。在項目中使用MDI窗體時&#xff0c…

實用R語言機器學習指南:從數據預處理到模型實戰(附配套學習資源)

一、為什么需要掌握機器學習建模&#xff1f;在科研與項目實踐中&#xff0c;機器學習已成為數據挖掘的核心工具。本文手把手帶你在R語言中實現7大常用模型&#xff1a;邏輯回歸/正則化回歸決策樹/隨機森林SVM支持向量機XGBoost梯度提升神經網絡全程包含數據標準化→模型訓練→…

go.uber.org/zap 日志庫高性能寫入

使用 go.uber.org/zap 實現日志分割功能 實現按照單個文件最大MB自動分割,最多保留多少天的文件,是否啟用壓縮,按天自動分割日志 核心依賴 go.uber.org/zap:核心日志庫 lumberjack.v2:日志輪轉工具(實現按大小/時間分割) 時間處理依賴標準庫 time 實現步驟 1. 初始化…

電腦端完全免費的動態壁紙和屏保軟件(真正免費、無廣告、無會員)

? 1. Lively Wallpaper&#xff08;強烈推薦&#xff09; 特點&#xff1a;完全免費、開源、無廣告&#xff0c;支持本地視頻/GIF/網頁作為動態壁紙內置資源&#xff1a;12個高質量動態壁紙&#xff08;可自定義&#xff09;屏保功能&#xff1a;支持將動態壁紙一鍵設為屏保系…

C#_組合優于繼承的實際應用

2.2 Composition over Inheritance&#xff1a;組合優于繼承的實際應用 繼承&#xff08;Inheritance&#xff09;是面向對象編程中最容易被過度使用和誤用的特性之一。傳統的教學往往讓人們優先選擇繼承來實現代碼復用和建立“是一個&#xff08;is-a&#xff09;”的關系。然…

Kafka消息丟失的場景有哪些

生產者在生產過程中的消息丟失 broker在故障后的消息丟失 消費者在消費過程中的消息丟失ACK機制 ack有3個可選值&#xff0c;分別是1&#xff0c;0&#xff0c;-1。 ack0&#xff1a;生產者在生產過程中的消息丟失 簡單來說就是&#xff0c;producer發送一次就不再發送了&#…

盼之代售 231滑塊 csessionid 分析

聲明 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01; 逆向分析 部分python代碼 url "…

STL關聯式容器解析:map與set詳解

目錄 1. 關聯式容器 2. 鍵值對 3. 樹形結構的關聯式容器 3.1 set 3.1.2 set的使用 3.2 map 3.2.1 map的介紹 3.2.2 map的使用 3.3 multiset 3.3.1 multiset的介紹 3.3.2 multiset的使用 3.4 multimap 3.4.1 multimap的介紹 3.4.2 multimap的使用 4.紅黑樹模擬實現…

貪吃蛇--C++實戰項目(零基礎)

視頻地址&#xff1a;C語言必學項目&#xff1a;貪吃蛇&#xff01; 貪吃蛇游戲框架 ├── 基礎框架 │ ├── 頭文件引入 │ ├── 常量和宏定義 │ └── 窗口初始化 │ ├── 數據結構系統 │ ├── Pos結構體(位置和顏色) │ ├── Snake結構體(蛇的屬性) │ ├──…

unity資源領取反作弊工具加密器

https://assetstore.unity.com/packages/tools/utilities/anti-cheat-pro-2025-3006260元購碼GUARDINGPEARSOFTWARE

FPGA設計中的信號完整性量化與優化:探索高速數字系統的關鍵路徑

在高速FPGA設計中&#xff0c;信號完整性&#xff08;Signal Integrity, SI&#xff09;已成為確保系統穩定、可靠運行的核心要素之一。隨著數據傳輸速率的不斷提升和電路規模的日益復雜&#xff0c;信號在傳輸過程中受到的干擾和畸變問題日益凸顯。因此&#xff0c;如何有效量…