QT聊天项目DAY10
1.封装redis操作类
头文件
#ifndef REDISMANAGE_H
#define REDISMANAGE_H#include "Singletion.h"
#include "GlobalHead.h"class RedisManage : public Singletion<RedisManage>
{friend class Singletion<RedisManage>;
public:~RedisManage();bool Connect(const string& host, int port); // 连接redisbool Get(const string& key, string& value); // 获取valuebool Set(const string& key, const string& value); // 设置valuebool Auth(const string& password); // 验证密码bool LPush(const string& key, const string& value); // 左侧pushbool LPop(const string& key, string& value); // 左侧popbool RPush(const string& key, const string& value); // 右侧pushbool RPop(const string& key, string& value); // 右侧popbool Del(const string& key); // 删除keybool ExitsKey(const string& key); // 判断key是否存在void Close(); // 关闭连接/* 将名为key的hash表中的field设置为Value */bool HSet(const string& key, const string& field, const string& value); bool HSet(const char* key, const char* field, const char* value, size_t valuelen);/* 获取名为key的hash表中field对应的value */string HGet(const string& key, const string& field); private:RedisManage();private:redisContext* _connect; // redis连接redisReply* _reply; // redis响应对象
};
#endif // REDISMANAGE_H
实现文件
#include "RedisManage.h"RedisManage::~RedisManage()
{}RedisManage::RedisManage()
{}/* 连接redis */
bool RedisManage::Connect(const string& host, int port)
{_connect = redisConnect(host.c_str(), port);if (_connect == NULL || _connect->err){cout << "connect error " << _connect->errstr << endl;return false;}return true;
}/* 获取value */
bool RedisManage::Get(const string& key, string& value)
{_reply = (redisReply*)redisCommand(_connect, "GET %s", key.c_str());if (_reply == NULL){cout << "[ GET " << key << " ] error" << endl;freeReplyObject(_reply);return false;}/* 不是字符串 */if (_reply->type != REDIS_REPLY_STRING){cout << "[ GET " << key << " ] not string" << endl;freeReplyObject(_reply);return false;}value = _reply->str;freeReplyObject(_reply);cout << "[ GET " << key << " ] success" << endl;return true;
}/* 设置value */
bool RedisManage::Set(const string& key, const string& value)
{_reply = (redisReply*)redisCommand(_connect, "SET %s %s", key.c_str(), value.c_str());if (_reply == NULL){cout << "Execute command [ SET " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}// 执行失败则释放连接if ((_reply->type != REDIS_REPLY_STATUS && _reply->str != "ok") || _reply->str != "ok"){cout << "Execute command [ SET " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ SET " << key << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 验证密码 */
bool RedisManage::Auth(const string& password)
{_reply = (redisReply*)redisCommand(_connect, "AUTH %s", password.c_str());if (_reply->type == REDIS_REPLY_ERROR){cout << "Auth failed" << endl;freeReplyObject(_reply);return false;}else{cout << "Auth success" << endl;freeReplyObject(_reply);return true;}
}/* 左侧插入 */
bool RedisManage::LPush(const string& key, const string& value)
{_reply = (redisReply*)redisCommand(_connect, "LPUSH %s %s", key.c_str(), value.c_str());if (_reply == NULL){cout << "Execute command [ LPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}if (_reply->type != REDIS_REPLY_INTEGER || _reply->integer <= 0){cout << "Execute command [ LPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ LPush " << key << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 左侧弹出 */
bool RedisManage::LPop(const string& key, string& value)
{_reply = (redisReply*)redisCommand(_connect, "LPOP %s", key.c_str());if (_reply == NULL || _reply->type == REDIS_REPLY_NIL){cout << "Execute command [ LPop " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}value = _reply->str;freeReplyObject(_reply);cout << "Execute command [ LPop " << key << " ] success" << endl;return true;
}/* 右侧插入 */
bool RedisManage::RPush(const string& key, const string& value)
{_reply = (redisReply*)redisCommand(_connect, "RPUSH %s %s", key.c_str(), value.c_str());if (_reply == NULL){cout << "Execute command [ RPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}if (_reply->type != REDIS_REPLY_INTEGER || _reply->integer <= 0){cout << "Execute command [ RPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ RPush " << key << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 右侧弹出 */
bool RedisManage::RPop(const string& key, string& value)
{_reply = (redisReply*)redisCommand(_connect, "RPOP %s", key.c_str());if (_reply == NULL || _reply->type == REDIS_REPLY_NIL){cout << "Execute command [ RPop " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}value = _reply->str;freeReplyObject(_reply);cout << "Execute command [ RPop " << key << " ] success" << endl;return true;
}/* 将名为key的hash表中的field设置为Value */
bool RedisManage::HSet(const string& key, const string& field, const string& value)
{/* 负责传输命令 */_reply = (redisReply*)redisCommand(_connect, "HSET %s %s %s", key.c_str(), field.c_str(), value.c_str());if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER){cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}bool RedisManage::HSet(const char* key, const char* field, const char* value, size_t valuelen)
{const char* argv[4];size_t argvlen[4];argv[0] = "HSET";argvlen[0] = 4;argv[1] = key;argvlen[1] = strlen(key);argv[2] = field;argvlen[2] = strlen(field);argv[3] = value;argvlen[3] = valuelen;/* 负责传输二进制,比如图片之类的*/_reply = (redisReply*)redisCommandArgv(_connect, 4, argv, argvlen);if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER){cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 获取名为key的hash表中field对应的value */
string RedisManage::HGet(const string& key, const string& field)
{const char* argv[3];size_t argvlen[3];argv[0] = "HGET";argvlen[0] = 4;argv[1] = key.c_str();argvlen[1] = key.length();argv[2] = field.c_str();argvlen[2] = field.length();_reply = (redisReply*)redisCommandArgv(_connect, 3, argv, argvlen);if (_reply == NULL || _reply->type == REDIS_REPLY_NIL){cout << "Execute command [ HGet " << key << " " << field << " ] failed" << endl;freeReplyObject(_reply);return "";}string value = _reply->str;freeReplyObject(_reply);cout << "Execute command [ HGet " << key << " " << field << " ] success" << endl;return value;
}/* 删除key */
bool RedisManage::Del(const string& key)
{_reply = (redisReply*)redisCommand(_connect, "DEL %s", key.c_str());if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER){cout << "Execute command [ DEL " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ DEL " << key << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 判断key是否存在 */
bool RedisManage::ExitsKey(const string& key)
{_reply = (redisReply*)redisCommand(_connect, "EXISTS %s", key.c_str());if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER || _reply->integer == 0){cout << "Execute command [ ExitsKey " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ ExitsKey " << key << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 关闭连接 */
void RedisManage::Close()
{redisFree(_connect);
}
1.2 编译测试
测试一下代码
void TestRedisManage() {assert(RedisManage::GetInstance()->Connect("127.0.0.1", 6380));assert(RedisManage::GetInstance()->Auth("123456"));assert(RedisManage::GetInstance()->Set("blogwebsite", "llfc.club"));std::string value = "";assert(RedisManage::GetInstance()->Get("blogwebsite", value));assert(RedisManage::GetInstance()->Get("nonekey", value) == false);assert(RedisManage::GetInstance()->HSet("bloginfo", "blogwebsite", "llfc.club"));assert(RedisManage::GetInstance()->HGet("bloginfo", "blogwebsite") != "");assert(RedisManage::GetInstance()->ExitsKey("bloginfo"));assert(RedisManage::GetInstance()->Del("bloginfo"));assert(RedisManage::GetInstance()->Del("bloginfo"));assert(RedisManage::GetInstance()->ExitsKey("bloginfo") == false);assert(RedisManage::GetInstance()->LPush("lpushkey1", "lpushvalue1"));assert(RedisManage::GetInstance()->LPush("lpushkey1", "lpushvalue2"));assert(RedisManage::GetInstance()->LPush("lpushkey1", "lpushvalue3"));assert(RedisManage::GetInstance()->RPop("lpushkey1", value));assert(RedisManage::GetInstance()->RPop("lpushkey1", value));assert(RedisManage::GetInstance()->LPop("lpushkey1", value));assert(RedisManage::GetInstance()->LPop("lpushkey2", value) == false);RedisManage::GetInstance()->Close();
}
执行失败
set指令出问题了
指令是执行成功了的
对于char只能使用strcmp的方式去比较
2. Redis连接池
之前写了事件循环池和GRPC连接池,事件循环池中是专门处理客户端和服务器之间的通讯的,如果客户端发来数据需要存储起来的话,服务器就需要调用我们刚刚封装的RedisManage获取它的单例然后去向redis数据库中写东西,这时候就带来一个问题,如果两个线程同时写,就会产生资源竞争而如果使用互斥锁,多线程的优势又没有完全展现出来,所用封装多个redis连接的池性组件,这样就能够充分发挥多线程的优势了
从原本的一个连接,变成多个连接
池性组件中的核心function就是获取连接和还回连接
/* Redis 连接池 */
class RedisPool
{
public:RedisPool(size_t PoolSize, const char* host, int port, const char* password = "123456"): _PoolSize(PoolSize), _host(host), _port(port), bIsStop(false){for (size_t i = 0; i < PoolSize; i++){redisContext* redis = redisConnect(_host, _port);if (redis == NULL || redis->err != 0){if (redis){redisFree(redis);}continue;}redisReply* ret = (redisReply*)redisCommand(redis, "AUTH %s", password);if (ret->type == REDIS_REPLY_ERROR){cout << "Redis Auth Error: " << ret->str << endl;freeReplyObject(ret);redisFree(redis);continue;}freeReplyObject(ret);cout << "Redis Connect Success!" << endl;_pool.push(redis);}}~RedisPool(){lock_guard<mutex> lock(_mutex);bIsStop = true;_cond.notify_all(); // 唤醒所有线程while (!_pool.empty()){redisContext* redis = _pool.front();_pool.pop();redisFree(redis);}}/* 获取一个redis 连接 */redisContext* GetConnect(){unique_lock<mutex> lock(_mutex);_cond.wait(lock, [this]() {return!_pool.empty() || !bIsStop; });redisContext* redisConnection = _pool.front();_pool.pop();return redisConnection;}/* 归还一个redis 连接 */void ReturnConnect(redisContext* redis){lock_guard<mutex> lock(_mutex);if(bIsStop)return;_pool.push(redis);_cond.notify_one(); // 唤醒沉睡的线程}private:atomic<bool> bIsStop; // 停止标志size_t _PoolSize; // 连接池大小const char* _host; // redis主机地址int _port; // redis端口/* 队列, 锁, 条件变量 */queue<redisContext*> _pool; // 连接池mutex _mutex; // 互斥锁condition_variable _cond; // 条件变量
};
重新修改的redis连接类
头文件
class RedisManage : public Singletion<RedisManage>
{friend class Singletion<RedisManage>;
public:~RedisManage();bool Get(const string& key, string& value); // 获取valuebool Set(const string& key, const string& value); // 设置valuebool Auth(const string& password); // 验证密码bool LPush(const string& key, const string& value); // 左侧pushbool LPop(const string& key, string& value); // 左侧popbool RPush(const string& key, const string& value); // 右侧pushbool RPop(const string& key, string& value); // 右侧popbool Del(const string& key); // 删除keybool ExitsKey(const string& key); // 判断key是否存在void Close(); // 关闭连接/* 将名为key的hash表中的field设置为Value */bool HSet(const string& key, const string& field, const string& value); bool HSet(const char* key, const char* field, const char* value, size_t valuelen);/* 获取名为key的hash表中field对应的value */string HGet(const string& key, const string& field); private:RedisManage();private:RedisPool* _redisPool; // redis连接池redisReply* _reply; // redis响应对象
};
#endif // REDISMANAGE_H
实现文件
#include "RedisManage.h"
#include "ServerStatic.h"RedisManage::~RedisManage()
{}RedisManage::RedisManage()
{const char* ip = get<string>(ServerStatic::ParseConfig("Redis", "Host")).c_str();int Port = get<int>(ServerStatic::ParseConfig("Redis", "Port"));_redisPool = new RedisPool(5,ip, Port);
}/* 获取value */
bool RedisManage::Get(const string& key, string& value)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "GET %s", key.c_str());if (_reply == NULL){cout << "[ GET " << key << " ] error" << endl;freeReplyObject(_reply);return false;}/* 不是字符串 */if (_reply->type != REDIS_REPLY_STRING){cout << "[ GET " << key << " ] not string" << endl;freeReplyObject(_reply);return false;}value = _reply->str;freeReplyObject(_reply);cout << "[ GET " << key << " ] success" << endl;return true;
}/* 设置value */
bool RedisManage::Set(const string& key, const string& value)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "SET %s %s", key.c_str(), value.c_str());if (_reply == NULL){cout << "Execute command [ SET " << key << " " << value << " ] failed _reply == NULL" << endl;freeReplyObject(_reply);return false;}// 执行失败则释放连接if ((_reply->type != REDIS_REPLY_STATUS && strcmp(_reply->str, "OK") != 0) || strcmp(_reply->str, "OK") != 0){cout << "Execute command [ SET " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ SET " << key << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 验证密码 */
bool RedisManage::Auth(const string& password)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "AUTH %s", password.c_str());if (_reply->type == REDIS_REPLY_ERROR){cout << "Auth failed" << endl;freeReplyObject(_reply);return false;}else{cout << "Auth success" << endl;freeReplyObject(_reply);return true;}
}/* 左侧插入 */
bool RedisManage::LPush(const string& key, const string& value)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "LPUSH %s %s", key.c_str(), value.c_str());if (_reply == NULL){cout << "Execute command [ LPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}if (_reply->type != REDIS_REPLY_INTEGER || _reply->integer <= 0){cout << "Execute command [ LPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ LPush " << key << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 左侧弹出 */
bool RedisManage::LPop(const string& key, string& value)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "LPOP %s", key.c_str());if (_reply == NULL || _reply->type == REDIS_REPLY_NIL){cout << "Execute command [ LPop " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}value = _reply->str;freeReplyObject(_reply);cout << "Execute command [ LPop " << key << " ] success" << endl;return true;
}/* 右侧插入 */
bool RedisManage::RPush(const string& key, const string& value)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "RPUSH %s %s", key.c_str(), value.c_str());if (_reply == NULL){cout << "Execute command [ RPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}if (_reply->type != REDIS_REPLY_INTEGER || _reply->integer <= 0){cout << "Execute command [ RPush " << key << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ RPush " << key << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 右侧弹出 */
bool RedisManage::RPop(const string& key, string& value)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "RPOP %s", key.c_str());if (_reply == NULL || _reply->type == REDIS_REPLY_NIL){cout << "Execute command [ RPop " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}value = _reply->str;freeReplyObject(_reply);cout << "Execute command [ RPop " << key << " ] success" << endl;return true;
}/* 将名为key的hash表中的field设置为Value */
bool RedisManage::HSet(const string& key, const string& field, const string& value)
{/* 负责传输命令 */_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "HSET %s %s %s", key.c_str(), field.c_str(), value.c_str());if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER){cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}bool RedisManage::HSet(const char* key, const char* field, const char* value, size_t valuelen)
{const char* argv[4];size_t argvlen[4];argv[0] = "HSET";argvlen[0] = 4;argv[1] = key;argvlen[1] = strlen(key);argv[2] = field;argvlen[2] = strlen(field);argv[3] = value;argvlen[3] = valuelen;/* 负责传输二进制,比如图片之类的*/_reply = (redisReply*)redisCommandArgv(_redisPool->GetConnect(), 4, argv, argvlen);if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER){cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ HSet " << key << " " << field << " " << value << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 获取名为key的hash表中field对应的value */
string RedisManage::HGet(const string& key, const string& field)
{const char* argv[3];size_t argvlen[3];argv[0] = "HGET";argvlen[0] = 4;argv[1] = key.c_str();argvlen[1] = key.length();argv[2] = field.c_str();argvlen[2] = field.length();_reply = (redisReply*)redisCommandArgv(_redisPool->GetConnect(), 3, argv, argvlen);if (_reply == NULL || _reply->type == REDIS_REPLY_NIL){cout << "Execute command [ HGet " << key << " " << field << " ] failed" << endl;freeReplyObject(_reply);return "";}string value = _reply->str;freeReplyObject(_reply);cout << "Execute command [ HGet " << key << " " << field << " ] success" << endl;return value;
}/* 删除key */
bool RedisManage::Del(const string& key)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "DEL %s", key.c_str());if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER){cout << "Execute command [ DEL " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ DEL " << key << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 判断key是否存在 */
bool RedisManage::ExitsKey(const string& key)
{_reply = (redisReply*)redisCommand(_redisPool->GetConnect(), "EXISTS %s", key.c_str());if (_reply == NULL || _reply->type != REDIS_REPLY_INTEGER || _reply->integer == 0){cout << "Execute command [ ExitsKey " << key << " ] failed" << endl;freeReplyObject(_reply);return false;}cout << "Execute command [ ExitsKey " << key << " ] success" << endl;freeReplyObject(_reply);return true;
}/* 关闭连接 */
void RedisManage::Close()
{redisFree(_redisPool->GetConnect());
}
重新修改读取配置文件类,采用c++17的variant来返回
static variant<int, string> ParseConfig(string blockName, string key, string configPath = "./Config/config.json");/* 解析配置文件 */
variant<int, string>ServerStatic::ParseConfig(string blockName, string key, string configPath)
{ifstream file(configPath, ifstream::binary);if (!file.is_open()){cerr << "Failed to open config file: " << configPath << endl;return {};}Json::Value jsonResult;Json::Reader reader;if (!reader.parse(file, jsonResult)){cout << "Failed to parse config file: " << configPath << endl;return {};}if (!jsonResult.isMember(blockName)){cout << "Failed to find block: " << blockName << endl;return {};}const Json::Value& value = jsonResult[blockName][key];if (value.isInt()){return value.asInt();}else if (value.isString()){return value.asString();}return {};
}
2.2 编译测试
报错,说std::iterator什么什么的
在 C++17 中,标准库弃用了std::iterator,可以定义_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING来屏蔽这个错误
编译成功
redis连接池编写成功
相关文章:
QT聊天项目DAY10
1.封装redis操作类 头文件 #ifndef REDISMANAGE_H #define REDISMANAGE_H#include "Singletion.h" #include "GlobalHead.h"class RedisManage : public Singletion<RedisManage> {friend class Singletion<RedisManage>; public:~RedisMana…...
5.0.5 变换(旋转、缩放、扭曲)
WPF变换可以产生特殊效果,如平移、旋转、扭曲。 变换类 描述TranslateTransform沿着X轴和Y轴平移ScaleTransform 沿着定义的中心点缩放RotateTransform沿着定义的中心点旋转SkewTransform 扭曲元素MatrixTransfrom提供3x3矩阵,用于定义一个自定义变换 1…...
matlab转python
1 matlab2python开源程序 https://blog.csdn.net/qq_43426078/article/details/123384265 2 网址 转换网址:https://app.codeconvert.ai/code-converter?inputLangMatlab&outputLangPython 文件比较网址:https://www.diffchecker.com/text-comp…...
什么是直播美颜SDK?跨平台安卓、iOS美颜SDK开发实战详解
时下,尤其在社交、娱乐、电商等应用场景中,一个流畅且效果自然的美颜功能往往能直接影响用户的留存率和平台的营收。要实现这些效果,美颜SDK是核心工具。那么,什么是直播美颜SDK?它的功能有哪些?如何进行跨…...
大尺寸PCB如何重塑通信与新能源产业格局
在5G通信基站与新能源电站的机房内,一块块面积超过600mm600mm的PCB板正悄然推动着技术革命。作为电子设备的核心载体,大尺寸PCB凭借其高密度集成与复杂工艺,成为通信、能源等领域的“隐形功臣”。以猎板PCB为代表的厂商,凭借宽幅曝…...
JavaSE核心知识点02面向对象编程02-04(包和导入)
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 JavaSE核心知识点02面向对象编程02-04&#…...
【漫话机器学习系列】249.Word2Vec自然语言训练模型
【自然语言处理】用 Word2Vec 将词语映射到向量空间详解 一、背景介绍 在自然语言处理(NLP)领域,我们常常需要将文本信息转化为机器能够理解和处理的形式。传统的方法,如 one-hot编码,虽然简单,但存在严重…...
CSS transition过渡属性
transition 是 CSS 中用于创建平滑动画效果的属性,它允许元素在两个状态之间平滑过渡,而不是立即改变。通过定义过渡的属性、持续时间和速度曲线,你可以实现丰富的交互体验,如悬停效果、状态切换动画等。 核心作用 平滑过渡&…...
U9C对接飞书审批流完整过程
U9C虽然很强大,但是移动办公和审批流功能并不好用,为了弥补U9C这种不足,很多的企业在使用U9C的同时再开通钉钉、飞书、企业微信这种OA管理系统,两套系统并行使用,就需要考虑U9C和OA系统数据同步的问题,最简…...
阿里云 SLS 多云日志接入最佳实践:链路、成本与高可用性优化
作者:裘文成(翊韬) 摘要 随着企业全球化业务的扩展,如何高效、经济且可靠地将分布在海外各地的应用与基础设施日志统一采集至阿里云日志服务 (SLS) 进行分析与监控,已成为关键挑战。 本文聚焦于阿里云高性能日志采集…...
从0开始学习大模型--Day04--大模型的框架以及基本元素
Agent框架与策略分析 计划与执行(planning-and-Execute) 该框架侧重于先规划一系列的行动,然后执行。这个框架可以使大模型能够先综合考虑任务的多个方面,然后按照计划进行行动,比较适合应用在较复杂的项目管理中或者…...
FPGA实战项目2———多协议通信控制器
1. 多协议通信控制器模块 (multi_protocol_controller) 简要介绍 这是整个设计的顶层模块,承担着整合各个子模块的重要任务,是整个系统的核心枢纽。它负责协调 UART、SPI、I2C 等不同通信协议模块以及 DMA 模块的工作,同时处理不同时钟域之间的信号交互,确保各个模块能够…...
strings.Builder 使用详解
目录 1. 官方包 2. 支持版本 3. 官方说明 官方示例 方法 func (b *Builder) Cap() int func (b *Builder) Grow(n int) func (b *Builder) Len() int func (b *Builder) Reset() func (b *Builder) String() string func (b *Builder) Write(p []byte) (int, error)…...
数巅智能携手北京昇腾创新中心深耕行业大模型应用
当前,AI技术正在加速向各行业深度渗透,成为驱动产业转型和社会经济发展的重要引擎。构建开放协作的AI应用生态体系、推动技术和应用深度融合,已成为行业发展的重要趋势。 近日,数巅智能与北京昇腾人工智能计算中心(北京昇腾创新中…...
【嵌入式系统设计师(软考中级)】第二章:嵌入式系统硬件基础知识——⑤电源及电路设计
文章目录 7. 嵌入式系统电源分类及管理7.1 嵌入式系统电源分类7.2 电源管理技术7.3 电源完整性设计 8. 电子电路设计8.1 电子电路设计基础知识8.1.1 电子电路设计原理8.1.2 电子电路设计方法及步骤8.1.3 电子电路可靠性设计 8.2 PCB设计基础知识8.2.1 PCB设计原理8.2.2 PCB设计…...
排序算法-希尔排序
希尔排序是插入排序的改进版,通过将原始数组分成多个子序列进行间隔插入排序,逐步缩小间隔直至为1,最终完成整体排序。它也被称为缩小增量排序。 希尔排序步骤 选择增量序列(Gap Sequence):确定一个递减的…...
JAVA继承中变量和方法的存储和方法中访问变量的顺序
一、变量归属与内存位置 static 变量:属于类,只存在一份,保存在方法区(或元空间)。 实例变量(非static):属于对象,每个对象单独一份,保存在堆内存中。 二、…...
【PhysUnits】3.3 SI 基础量纲单位(units/base.rs)
一、源码 这段代码定义了一系列基础物理量纲的类型别名,并使用标记 trait Canonical 来表示它们是国际单位制(SI)中的基本单位。 use crate::Dimension; use typenum::{P1, Z0};/// 标记特质,表示基础量纲单位 pub trait Canoni…...
stm32F103芯片 实现PID算法控制温度例程
目录 硬件需求 软件需求 步骤 1. 配置STM32CubeMX 2. 编写PID控制代码 3. 编译和烧录 4. 测试 注意事项 要在STM32F103芯片上实现PID算法控制温度,首先需要确保你有一套完整的硬件和软件开发环境。这里,我将给你一个简化的例程,展示如何使用PID控制算法来调节一个假…...
Java学习手册:微服务设计原则
一、单一职责原则 每个微服务应该专注于一个特定的业务功能,具有清晰的职责边界。这有助于保持服务的简洁性,降低服务之间的耦合度,提高服务的可维护性和可扩展性。例如,可以将用户管理、订单管理、支付管理等功能分别设计为独立…...
【挑战项目】 --- 微服务编程测评系统(在线OJ系统)(二)
三十二、Swagger介绍&使用 官网:https://swagger.io/ 什么是swagger Swagger是一个接口文档生成工具,它可以帮助开发者自动生成接口文档。当项目的接口发生变更时,Swagger可以实时更新文档,确保文档的准确性和时效性。Swagger还内置了测试功能,开发者可以直接在文档中…...
Unity背景随着文字变化而变化
组件结构: 背景(父)需要添加如下两个组件 根据具体情况选择第一个组件水平还是垂直,一般垂直用的比较多 效果展示: 此时在文本框中改变内容背景图都会随着变化,动态的...
Elasticsearch内存管理与JVM优化:原理剖析与最佳实践
#作者:孙德新 文章目录 一、Elasticsearch缓存分类1、Node Query Cache:2、Shard Request Cache:3、Fielddata Cache: 三、内存常见的问题案例一案例二案例三案例四 四、内参分配最佳实践1、jvm heap分配2、将机器上少于一半的内…...
快速开发-基于Gin框架搭建web应用
一、概述 Go 语言的 Gin 框架是一个用 Go (Golang) 编写的 Web 框架,它旨在提供一种快速、简洁且高效的方式来构建 Web 应用程序。Gin 框架以其高性能和易用性而闻名,非常适合构建 API 服务、Web 服务和其他需要高性能的 Web 应用。 二、Gin框架…...
【RAG】RAG系统——langchain 的用法(说人话版与专业版)
说人话版: RAG就是一句话:对数据设置索引,用问题去检索,用llm生成回答 首先,做本地知识库 注意: py 3.10以上 配置环境变量,安装库 load外部数据,存储到本地的一个index里(这是最…...
pycharm无法直接识别wsl
原因是我的2020 无法支持这个版本的wsl 我就换成2024版 添加中可以看到 on wsl 如果你想切到自己创建的虚拟环境 你在下面这个界面选择conda就好 这样就可以切换成你想要的环境了...
数据结构每日一题day17(链表)★★★★★
题目描述:假设有两个按元素值递增次排列的线性表,均以单链表形式存储。请编与算法将这两个单链表归并为一个按元素值依次递减排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。 算法思想: 1.初始化: 创建一个新…...
遗传算法(GA)
基本原理 算法介绍 遗传算法(Genetic Algorithm,简称GA)是一种基于自然选择和遗传学原理的搜索和优化技术。它模拟了生物进化过程,通过选择、交叉(重组)和变异等操作,逐步优化问题的解。 遗传…...
EPS三维测图软件
EPS三维测图软件EPS2016...
设计模式-命令模式
写在前面 Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正! 一、什么是命令模式? 命令模式是行为模式中的一种,通过将请求封装成对象,使开发者可…...
深入理解主从数据库架构与主从复制
目录 前言1. 主从数据库概述1.1 什么是主从数据库?1.2 主从数据库的应用场景 2. 主从数据库的工作原理2.1 主从数据库的读写分离2.2 数据同步机制2.3 异步与同步复制模式 3. 主从复制的实现步骤3.1 配置主库3.2 配置从库 4. 主从数据库架构的优缺点4.1 优点4.2 缺点…...
【C】初阶数据结构15 -- 计数排序与稳定性分析
本文主要讲解七大排序算法之外的另一种排序算法 -- 计数排序 目录 1 计数排序 1) 算法思想 2) 代码 3) 时间复杂度与空间复杂度分析 (1) 时间复杂度 (2) 空间复杂度 4) 计…...
@PostConstruct @PreDestroy
PostConstruct 是 Java EE(现 Jakarta EE)中的一个注解,用于标记一个方法在对象初始化完成后立即执行。它在 Spring 框架、Java Web 应用等场景中广泛使用,主要用于资源初始化、依赖注入完成后的配置等操作。 1. 基本作用 执行时…...
2025数维杯数学建模A题完整限量论文:空中芭蕾——蹦床运动的力学行为分析
2025数维杯数学建模A题完整限量论文:空中芭蕾——蹦床运动的力学行为分析 ,先到先得 A题完整论文https://www.jdmm.cc/file/2712067/ 蹦床( Trampoline )是一项运动员利用蹦床的反弹,在空中展示技能 技巧的竞技运动&…...
Kubernetes Gateway API 部署详解:从入门到实战
引言 在 Kubernetes 中管理网络流量一直是一个复杂而关键的任务。传统的 Ingress API 虽然广泛使用,但其功能有限且扩展性不足。Kubernetes Gateway API 作为新一代标准,提供了更强大的路由控制能力,支持多协议、跨命名空间路由和细粒度的流量管理。本文将带你从零开始部署…...
移动设备常用电子屏幕类型对比
概述 LCD 家族 (TN、STN、TFT、IPS、VA)依赖背光,性能差异主要来自液晶排列和驱动方式。OLED 以自发光为核心优势,但成本与寿命限制其普及。E-Paper 专为低功耗静态显示设计,与传统屏幕技术差异显著。 参数LCD&#…...
HarmonyOS开发-组件市场
1. HarmonyOS开发-组件市场 HarmonyOS NEXT开源组件市场是一个独立的插件,需通过DevEco Studio进行安装,可以点击下载,无需解压,直接通过zip进行安装,具体安装和使用方法可参考HarmonyOsNEXT组件市场使用说明。Harmony…...
效果图云渲染:价格、优势与使用技巧
对于做3D设计来说,渲染效果图会占用设计电脑的资源,如果能免去这个环节就好了。用设计电脑渲不仅拖慢电脑速度,遇到紧急情况无法快速渲染出来还可能耽误进度。而云渲染的出现,正是针对这个点——渲的快,价格便宜&#…...
OptiStruct实例:声振耦合超单元应用
如图10-11所示,本例采用一个简化的整车模型,模型分为车身(含声腔)与底盘两部分。首先分别运用CMS与CDS方法对车身(含声腔)模型进行缩聚,生成.h3d格式的CMS超单元和cps超单元,然后进行…...
排序算法-插入排序
插入排序是一种简单直观的排序算法,其核心思想是将未排序部分的元素逐个插入到已排序部分的正确位置,类似于整理扑克牌。 插入排序步骤 初始化:将序列的第一个元素视为已排序部分,其余为未排序部分。 选择元素:从未排…...
Uniapp Android/IOS 获取手机通讯录
介绍 最近忙着开发支付宝小程序和app,下面给大家介绍一下 app 获取通讯录的全部过程吧,也是这也是我app开发中的一项需求吧。 效果图如下 勾选配置文件 使用uniapp开发的童鞋都知道有一个配置文件 manifest.json 简单的说一下,就是安卓/ios/…...
【RAG】index环节中 关于嵌入模型和 ColBERT
1. 什么是嵌入模型?是不是把数据源转换为向量表示的模型? 是的,嵌入模型(Embedding Model)的核心功能就是将各种类型的数据(例如,文本、图像、音频等)转换成低维、稠密的向量表示。…...
一文掌握 LVGL 9 的源码目录结构
文章目录 📂 一文掌握 LVGL 9 的源码目录结构🧭 顶层目录概览📁 1. src/ — LVGL 的核心源码(🔥重点)📁 2. examples/ — API 示例📁 3. demos/ — 综合演示项目📁 4. do…...
ROS1 和 ROS2 在同一个系统中使用
一、环境变量设置 echo "ros noetic(1) or ros2 foxy(2)?" read edition if [ "$edition" -eq "1" ]; thensource /opt/ros/noetic/setup.bash elsesource /opt/ros/foxy/setup.bash fi 二、针对不同的ROS版本创建不同的工作空间work space...
Redis 8.0携新功能,重新开源
01 引言 Redis从7.4版本起,将开源许可证改成 RSALv2(Redis 源代码可用许可证)与 SSPLv1(服务器端公共许可证)的双重授权策略。简单来说,就是不能随意商用。为了抵制Redis,Redis的替代品Valkey、…...
AD原理图复制较多元器件时报错:“InvalidParameter Exception Occurred In Copy”
一、问题描述 AD原理图复制较多元器件时报错:AD原理图复制较多元器件时报错:“InvalidParameter Exception Occurred In Copy”。如下图 二、问题分析 破解BUG。 三、解决方案 1、打开参数配置 2、打开原理图优先项中的通用配置,取消勾选G…...
【wpf】12 在WPF中实现HTTP通信:封装HttpClient的最佳实践
一、背景介绍 在现代桌面应用开发中,网络通信是不可或缺的能力。WPF作为.NET平台下的桌面开发框架,可通过HttpClient轻松实现与后端API的交互。本文将以一个实际的HttpsMessages工具类为例,讲解如何在WPF中安全高效地封装HTTP通信模块。 二、…...
从概念表达到安全验证:智能驾驶功能迎来系统性规范
随着辅助驾驶事故频发,监管机制正在迅速补位。面对能力表达、使用责任、功能部署等方面的新要求,行业开始重估技术边界与验证能力,数字样机正成为企业合规落地的重要抓手。 2025年以来,围绕智能驾驶功能的争议不断升级。多起因辅…...
金贝灯光儿童摄影3大布光方案,解锁专业级童趣写真
随着亲子消费的持续升温,儿童摄影行业对高效、专业、灵活的专业灯光需求日益迫切。为精准解决儿童拍摄中孩子好动难配合、氛围单调、出片效率低下等痛点,深耕影像光源行业三十年,拥有丰富的商业人像摄影灯光解决方案的金贝品牌,近…...
双端口ram与真双端口ram的区别
端口独立性 真双端口RAM:拥有两个完全独立的读写端口(Port A和Port B),每个端口都有自己的地址总线、数据总线、时钟、使能信号和写使能信号。这意味着两个端口可以同时进行读写操作,且互不干扰。 伪双端口RAM&…...