【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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;如何有效量…