C++编写Redis客户端

目录

安装redis-plus-plus库

​编辑

编译C++redis客户端

redis的通用命令使用 

get/set

exists

del

keys

expire /ttl

type

string类型核心操作 

set和get

set带有超时时间

set带有NX

string带有XX

mset

mget

getrange和setrange

incr和decr 

list类型核心操作

lpush和lrange

 lpop和rpop

brpop

llen

set类型核心操作

sadd和smembers

sismember

 scard

spop

sinter

sinterstore

hash类型核心操作

hset和hget

 hexists和hdel和hlen

hmset和hmget

hkeys hvals

zset类型核心操作

zadd和zrange

zscore 

zrank


安装redis-plus-plus库

C++操作redis的库有很多,这里使用redis-plus-plus,我们需要在githb上下载。

github地址:GitHub - sewenew/redis-plus-plus: Redis client written in C++

github在国内的时候有时候会抽风,网不好,怎么呢?fq当然可以打开github,但fq是违法行为。

那有没有合法的方式打开呢?我们可以下载Watt Toolkit软件,这玩意其实就是一个游戏加速器。

 打开之后点击网络加速,这里面可以对github进行加速。

打开github后往下面翻,可以看到下载,要下载redis-plus-plus要先安装hiredis。

 安装hiredis

ubuntu

apt install libhiredis-dev

 

 安装redis-plus-plus

下载redis-plus-plus源

git clone https://github.com/sewenew/redis-plus-plus.git

安装好源之后,按照下面的命令执行即可 

cd redis-plus-plus

mkdir build  // 创建一个 build、 目录是习惯用法,让编译临时生成的文件放在改目录下

cd build

cmake ..      // 生成makefile

make           // 进行编译

make install // 把库考到系统目录

cd ..

安装完毕后,头文件会装到 /usr/local/include/sw/的redis++。

编译C++redis客户端

要写redis代码需要包含一下头文件<sw/redis++/redis++.h>。

当我们创建好Redis对象之后,执行的各种命令,其实就是该类中调用各种方法即可。

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sw/redis++/redis++.h>using  namespace std;int main()
{// 创建redis对象的时候需要再构造函数中指定 redis 服务器的地址和端口 由于我们redis客户端和服务器在同一个主机,写本地回环即可sw::redis::Redis redis("tcp://127.0.0.1:6379"); // 如果是不同主机,就写对应主机的ip即可,6379是端口号// 测试是否连接成功 让客户端给服务器发送一个 ping 服务器会返回一个 PONG,通过返回值获取到string result = redis.ping();cout << result << endl;return 0;}

使用makefile编译文件

编译程序的时候,需要引入库文件。

1. redis++自己的静态库  

我们可以使用find命令查找。

/usr/local/lib/libredis++.a

2.hiredis的静态库

/usr/lib/x86_64-linux-gnu/libhiredis.a 

3.线程库 

-pthread直接写就可以

hello:01.hello.ccg++ -o $@ $^ -std=c++17 /usr/local/lib/libredis++.a  /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread.PHONY:clean
clean:rm -f hello

redis的通用命令使用 

如果会使用redis的命令,那么使用C++编写redis客户端是非常简单的,很多都是接口都是一样的设计,命令的参数怎么写的的函数的参数就怎么传递就行。下面我并不会介绍每个函数的作用是啥,因为默认各位都是对命令了然于胸的,如果看到哪一个命令想不起来它的作用,随便上网查一下即可,或者是看下代码应该就能想起来,并且我不会把所有的命令都写出来,因为有些命令是非常简单的。

get/set


void test1(sw::redis::Redis &redis)
{cout << "get 和 set 的使用" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");redis.set("key2", "222");redis.set("key3", "333");// 使用 get 获取 key// get的返回值是sw::redis::OptionalString类型auto value1 = redis.get("key1");// optional 可以隐式转换成 bool 类型,可以直接在 if中判断,如果是无效元素,就返回false// 使用之前判断是否存在if (value1){// get的返回值是sw::redis::OptionalString类型,cout不支持该类型的运算符重载// 此处把sw::redis::OptionalString里面包含的值取出来就行,OptionalString只包含一个元素的容器// 可以使用value()方法取出来cout << "key1: " << value1.value() << endl;}auto value2 = redis.get("key2");if (value2){cout << "key2: " << value2.value() << endl;}auto value3 = redis.get("key3");if (value3){cout << "key3: " << value3.value() << endl;}// redis中并不存在key4auto value4 = redis.get("key4");if (value4){cout << "key4: " << value4.value() << endl;}
}

 

exists

// exists
void test2(sw::redis::Redis &redis)
{cout << "exists" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");// 存在的key// 返回值是一个long long类型,表示有几个key存在auto ret = redis.exists("key1");cout << ret << endl;// 不存在的keyret = redis.exists("key2");cout << ret << endl;// 判定多个 keyredis.set("key3", "333");ret = redis.exists({"key1", "key2", "key3"});cout << ret << endl;
}

del

void test3(sw::redis::Redis &redis)
{cout << "del" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");redis.set("key2", "222");// 删除一个key// 返回值是成功删除的个数auto ret = redis.del("key1");cout << ret << endl; // key1 存在返回值为1 表示删除1个// 删除多个keyret = redis.del({"key2", "key3"});cout << ret << endl; // key2 存在 key3 不存在 返回值2 表示删除2个// 判定被删除的key是否存在ret = redis.exists({"key1", "key2"}); // key1 key2都被删除了,因此存在个数0cout << ret << endl;
}

keys


void test4(sw::redis::Redis &redis)
{cout << "keys" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");redis.set("key2", "222");redis.set("key3", "333");redis.set("key4", "444");redis.set("key5", "555");redis.set("key6", "666");// keys第一个参数匹配规则// keys第二个参数是一个"插入迭代器",需要先准备好一个保存结果的容器// 接下来在创建一个插入迭代器指向容器的位置,就可以吧keys获取到的结果依次通过刚才的插入迭代器插入到容器指定位置中vector<string> v;// 这个auto的类型是 back_inserter<std::vector<std::string>>// 此处的it就指向v的起始地址auto it = back_inserter(v);// 这个地方是把keys查询的所有结果插入到v这个容器中redis.keys("*", it);// keys查询的key都放到v这个容器中// 遍历vector,查看结果for (auto &elem : v){cout << elem << endl;}
}

expire /ttl

void test5(sw::redis::Redis &redis)
{cout << "expire and ttl" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");// 设置key的过期时间redis.expire("key1", 10);                  // 这里可以直接跟字面值表示10sredis.expire("key1", chrono::seconds(10)); // 建议使用chrono的库函数,更加直观// 先休眠一会再查看过期时间this_thread::sleep_for(chrono::seconds(5)); // 休眠5s// 获取key的过期时间long long time = redis.ttl("key1");cout << time << endl; // 5s后,key1的过期时间还剩5s
}

type

void test6(sw::redis::Redis &redis)
{cout << "type" << endl;// 1.清空一下数据库redis.flushall();// 2. string 类型redis.set("key1", "111");string type = redis.type("key1");cout << "key1: " << type << endl;// 3. list 类型redis.lpush("key2", "111");type = redis.type("key2");cout << "key2: " << type << endl;// 4. hash 类型redis.hset("key3", "field1", "111");type = redis.type("key3");cout << "key3: " << type << endl;// 5. set 类型redis.sadd("key4", "111");type = redis.type("key4");cout << "key4: " << type << endl;// 6. zset 类型redis.zadd("key5", "吕布", 99);type = redis.type("key5");cout << "key5: " << type << endl;
}

string类型核心操作 

set和get

void test1(sw::redis::Redis &redis)
{cout << "get 和 set 的使用" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();redis.set("key", "111");// value是optionalString类型,需要使用value()方法取出来auto value = redis.get("key");if (value){std::cout << "value: " << value.value() << std::endl;}// 修改key的valueredis.set("key", "222");value = redis.get("key");if (value){std::cout << "value: " << value.value() << std::endl;}}

set带有超时时间

void test2(sw::redis::Redis &redis)
{cout << "set 带有超时时间" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 给key设置超时时间为9sredis.set("key", "111", chrono::seconds(9));// 休息6sthis_thread::sleep_for(chrono::seconds(6));// 获取超时时间auto time = redis.ttl("key");cout << "time: " << time << endl;
}

 

set带有NX

void test3(sw::redis::Redis &redis)
{cout << "set NX 和 XX" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 包含该命名空间可以直接使用 0s 这样的字面值常量using namespace std::chrono_literals;// set的重载版本中,没有单独提供 NX 和 XX版本,必须搭配过期时间使用,不需要过期时间就设置为0s// 不存在就设定redis.set("key", "111", 0s, sw::redis::UpdateType::NOT_EXIST);auto value = redis.get("key");if (value){cout << "value: " << value.value() << endl;}else{cout << "key 不存在!" << endl;}
}

void test3(sw::redis::Redis &redis)
{cout << "set NX 和 XX" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 包含该命名空间可以直接使用 0s 这样的字面值常量using namespace std::chrono_literals;redis.set("key", "111");// set的重载版本中,没有单独提供 NX 和 XX版本,必须搭配过期时间使用,不需要过期时间就设置为0s// 上面已经设置了key,所有下面set不会设置成功redis.set("key", "222", 0s, sw::redis::UpdateType::NOT_EXIST);auto value = redis.get("key");if (value){// 打印的结果是111cout << "value: " << value.value() << endl;}else{cout << "key 不存在!" << endl;}
}

string带有XX

void test3(sw::redis::Redis &redis)
{cout << "set NX 和 XX" << endl;// 1.清空一下数据库,以免影响我们使用redis.flushall();// 包含该命名空间可以直接使用 0s 这样的字面值常量using namespace std::chrono_literals;redis.set("key", "111");// set的重载版本中,没有单独提供 NX 和 XX版本,必须搭配过期时间使用,不需要过期时间就设置为0s// 已经存在才能设置成功,上面存在,因此可以设置成功redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);auto value = redis.get("key");if (value){// 打印的结果是222cout << "value: " << value.value() << endl;}else{cout << "key 不存在!" << endl;}
}

mset

void test4(sw::redis::Redis &redis)
{cout << "mset" << endl;redis.flushall();// 第一种写法,使用初始化列表描述多个键值对redis.mset({make_pair("key1", "111"),make_pair("key2", "222")});// 第二种写法,把多个键值对提前放到容器中,以迭代器的形式告诉msetvector<pair<string, string>> keys;keys.push_back(make_pair("key3", "333"));keys.push_back(make_pair("key4", "444"));redis.mset(keys.begin(), keys.end());auto value = redis.get("key1");if (value){cout << "value: " << value.value() << endl;}value = redis.get("key2");if (value){cout << "value: " << value.value() << endl;}value = redis.get("key3");if (value){cout << "value: " << value.value() << endl;}value = redis.get("key4");if (value){cout << "value: " << value.value() << endl;}
}

mget

void test5(sw::redis::Redis &redis)
{cout << "mget" << endl;redis.flushall();vector<pair<string, string>> keys;keys.push_back(make_pair("key1", "111"));keys.push_back(make_pair("key2", "222"));keys.push_back(make_pair("key3", "333"));redis.mset(keys.begin(), keys.end());vector<sw::redis::OptionalString> values;auto it = std::back_inserter(values);redis.mget({"key1", "key2", "key3", "key4"}, it);for (auto &elem : values){if (elem)cout << elem.value() << endl;elsecout << "元素无效" << endl;}
}

getrange和setrange


void test6(sw::redis::Redis &redis)
{cout << "getrange and setrange " << endl;redis.flushall();redis.set("key", "hello world");// 获取到下标2-5的字符string ret = redis.getrange("key", 2, 5);cout << "ret: " << ret << endl;// 将下标2后面的字符修改为musicredis.setrange("key", 2, "music");auto value = redis.get("key");if (value)cout << "value: " << value.value() << endl;
}

incr和decr 

void test7(sw::redis::Redis &redis)
{cout << "incr end decr" << endl;redis.flushall();redis.set("key", "100");// 自增long long ret = redis.incr("key");auto value = redis.get("key");if (value)cout << "value: " << value.value() << endl;// 自减ret = redis.decr("key");value = redis.get("key");if (value)cout << "value: " << value.value() << endl;
}

list类型核心操作

lpush和lrange

void test1(Redis &redis)
{cout << "lpush 和 lrange" << endl;redis.flushall();//  插入单个元素redis.lpush("key", "111");// 插入一组元素,基于初始化列表redis.lpush("key", {"222", "333", "444"});// 插入一组元素,基于迭代器vector<string> values = {"555", "666", "777"};redis.lpush("key", values.begin(), values.end());// lrange 获取到列表中的元素vector<string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);for (auto &elem : results){cout << elem << endl;}
}

lpush是头插,因此越后面的元素在前面。 rpush和lpush的用法是一模一样的,这里就不介绍了。

 lpop和rpop

void test3(Redis &redis)
{cout << "lpop 和 rpop" << endl;redis.flushall();// 构造一个 listredis.rpush("key", {"111", "222", "333", "444", "555"});// lpop 从左边弹出一个元素auto value = redis.lpop("key");if (value)cout << value.value() << endl;// rpop 从右边弹出一个元素value = redis.rpop("key");if (value)cout << value.value() << endl;
}

brpop

阻塞删除一个key

void test4(Redis &redis)
{cout << "blpop" << endl;redis.flushall();// key不存在一定会阻塞// 返回值是 OptionalStringPair 是一个pair// pair的first是来自哪一个key// pair的second是哪个元素被删了auto result = redis.blpop("key");if (result){cout << "key:" << result.value().first << endl;cout << "elem:" << result.value().second << endl;}else{cout << "result 无效!" << endl;}
}

阻塞删除多个key 并设置超时时间

void test4(Redis &redis)
{using namespace std::chrono_literals;cout << "blpop" << endl;redis.flushall();// key不存在一定会阻塞// 返回值是 OptionalStringPair 是一个pair// pair的first是来自哪一个key// pair的second是哪个元素被删了auto result = redis.blpop({"key", "key1", "key2"}, 10s);if (result){cout << "key:" << result.value().first << endl;cout << "elem:" << result.value().second << endl;}else{cout << "result 无效!" << endl;}
}

llen


void test5(Redis &redis)
{cout << "llen" << endl;redis.flushall();redis.lpush("key", {"111", "222", "333", "444", "555"});long long len = redis.llen("key");cout << "len: " << len << endl;
}

redis-plus-plus这个库,接口风格设计是非常统一的。

当一个函数,参数需要传递多个值的时候,往往都是支持初始化列表或者是一对迭代器的方式来进行实现。

当一个函数,返回值需要表示多个数据的时候,往往也会借助插入迭代器,来实现往一个容器中添加元素的效果。

当某些场景涉及到无效值的时候,往往会搭配std::optional来进行使用。

set类型核心操作

sadd和smembers

void test1(Redis &redis)
{cout << "sadd 和 smembers" << endl;redis.flushall();// 一次添加一个元素redis.sadd("key", "111");// 一次添加多个元素 使用初始化列表redis.sadd("key", {"222", "333", "444"});// 一次添加多个元素 使用迭代器// 使用什么容器都可以,set、vector、list...set<string> elems = {"555", "666", "777"};redis.sadd("key", elems.begin(), elems.end());// 获取集合中的所有元素vector<string> result;auto it = std::back_inserter(result);redis.smembers("key", it);for (auto &e : result){cout << e << endl;}
}

sismember

void test2(Redis &redis)
{cout << "sismember" << endl;redis.flushall();redis.sadd("key", {"111", "222", "333"});// 判断元素是否存在bool ret1 = redis.sismember("key", "111");bool ret2 = redis.sismember("key", "444");cout << "ret1: " << ret1 << endl;cout << "ret2: " << ret2 << endl;
}

 scard


void test3(Redis &redis)
{cout << "scard" << endl;redis.flushall();redis.sadd("key", {"111", "222", "333"});// 获取集合中元素的个数int ret = redis.scard("key");cout << "ret: " << ret << endl;
}

spop

void test4(Redis &redis)
{cout << "spop" << endl;redis.flushall();redis.sadd("key", {"111", "222", "333"});// 随机删除元素auto result = redis.spop("key");if (result){cout << "result: " << result.value() << endl;}else{cout << "result 无效 !" << endl;}
}

sinter

void test5(Redis &redis)
{cout << "sinter" << endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});set<string> result;auto it = std::inserter(result, result.end());redis.sinter({"key1", "key2"}, it);for (auto &e : result){cout << e << endl;}
}

 

sinterstore

void test6(Redis &redis)
{cout << "sinterstore" << endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});long long len = redis.sinterstore("key3", {"key1", "key2"});cout << "len: " << len << endl;set<string> result;auto it = std::inserter(result, result.end());redis.smembers("key3", it);for (auto &e : result){cout << e << endl;}
}

求并集和求交集基本一致,不多赘述。

hash类型核心操作

hset和hget

void test1(Redis &redis)
{cout << "hset 和 hget" << endl;redis.flushall();// 一次添加一个元素redis.hset("key", "f1", "111");redis.hset("key", make_pair("f2", "222"));// 一次添加多个元素(初始化列表)redis.hset("key", {make_pair("f3", "333"), make_pair("f4", "444")});// 一次添加多个元素(迭代器)vector<pair<string, string>> fileds = {make_pair("f5", "555"), make_pair("f6", "666")};redis.hset("key", fileds.begin(), fileds.end());auto value = redis.hget("key", "f1");if (value){cout << value.value() << endl;}
}

 hexists和hdel和hlen

void test2(Redis &redis)
{cout << "hexists hdel hlen" << endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");bool ret1 = redis.hexists("key", "f1");bool ret2 = redis.hexists("key", "f3");cout << "ret1: " << ret1 << endl;cout << "ret2: " << ret2 << endl;// 一次删除一个filedlong long r = redis.hdel("key", "f1");cout << "删除了" << r << "个" << endl;// 一次删除多个filedr = redis.hdel("key", {"f2", "f3"});cout << "删除了" << r << "个" << endl;long long len = redis.hlen("key");cout << "len: " << len << endl;
}

hmset和hmget

void test4(Redis &redis)
{cout << "hmset hmget" << endl;redis.flushall();// 初始化列表redis.hmset("key", {make_pair("f1", "111"), make_pair("f2", "222")});// 迭代器vector<pair<string, string>> filed = {make_pair("f3", "333"), make_pair("f4", "444")};redis.hmset("key", filed.begin(), filed.end());vector<string> result;auto it = std::back_inserter(result);redis.hmget("key", {"f1", "f2", "f3"}, it);for (auto &e : result)cout << e << endl;
}

hkeys hvals

void test3(Redis &redis)
{cout << "heys 和 hvals" << endl;redis.flushall();redis.hset("key", "f1", "111");redis.hset("key", "f2", "222");redis.hset("key", "f3", "333");vector<string> fileds;auto itfileds = std::back_inserter(fileds);redis.hkeys("key", itfileds);for (auto &e : fileds)cout << e << endl;vector<string> values;auto itvalues = std::back_inserter(values);redis.hvals("key", itvalues);for (auto &e : values)cout << e << endl;
}

zset类型核心操作

zadd和zrange

oid test1(Redis &redis)
{cout << "zadd 和 zrange" << endl;redis.flushall();redis.zadd("key", "吕布", 99);redis.zadd("key", {make_pair("赵云", 98),make_pair("关羽", 97)});vector<pair<string, double>> members;members.push_back(make_pair("张飞", 96));members.push_back(make_pair("马超", 95));redis.zadd("key", members.begin(), members.end());// zrange支持两种风格的查询:// 1.只查询member,不带score// 2.查询member 带 score// 关键在于迭代器指向的是只包含一个string,就只包含memeber// 迭代器指向的是一个 pair,里面包含了 string double 就查询member和scorevector<string> memberResults;auto it = std::back_inserter(memberResults);redis.zrange("key", 0, -1, it);for (auto &m : memberResults)cout << m << endl;vector<pair<string, double>> membersWithScore;auto it2 = std::back_inserter(membersWithScore);redis.zrange("key", 0, -1, it2);for (auto &m : membersWithScore)cout << m.first << " " << m.second << endl;
}

zcar和zrem


void test2(Redis &redis)
{cout << "zcard 和 zrem" << endl;redis.flushall();redis.zadd("key", "music", 99);redis.zadd("key", "english", 69);redis.zadd("key", "math", 98);redis.zrem("key", "english");long long len = redis.zcard("key");cout << "len: " << len << endl;
}

zscore 

void test3(Redis &redis)
{cout << "zscore" << endl;redis.flushall();redis.zadd("key", "music", 99);redis.zadd("key", "english", 69);redis.zadd("key", "math", 98);auto score = redis.zscore("key", "music");if (score){cout << "score: " << score.value() << endl;}else{cout << "score 无效!" << endl;}
}

zrank

void test4(Redis &redis)
{cout << "zrank" << endl;redis.flushall();redis.zadd("key", "music", 99);redis.zadd("key", "english", 69);redis.zadd("key", "math", 98);auto rank = redis.zrank("key", "music");if (rank){cout << "rank: " << rank.value() << endl;}else{cout << "rank 无效!" << endl;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/29829.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始实现大语言模型(十三):预训练大语言模型GPTModel

1. 前言 使用梯度下降算法通过下一个token预测任务预训练大语言模型GPTModel&#xff0c;前向传播流程每次会输入一个batch的长度均为context_len的训练样本&#xff0c;执行 batch_size context_len \text{batch\_size}\times\text{context\_len} batch_sizecontext_len次下…

JavaScript(最后一个元素的索引就是数组的长度减 1)array.length - 1

在不同的编程语言中&#xff0c;表示数组中最后一个元素的方法略有不同&#xff0c;但基本思路都是利用数组的长度或索引来实现。 以下是一些常见编程语言中获取数组最后一个元素的方法&#xff1a; 1. JavaScript: 使用 array.length - 1 索引: 这是最常见和传统的方法。Java…

RV1126+FFMPEG多路码流监控项目

一.项目介绍&#xff1a; 本项目采用的是易百纳RV1126开发板和CMOS摄像头&#xff0c;使用的推流框架是FFMPEG开源项目。这个项目的工作流程如下(如上图)&#xff1a;通过采集摄像头的VI模块&#xff0c;再通过硬件编码VENC模块进行H264/H265的编码压缩&#xff0c;并把压缩后的…

Python组合数据类型(一)

目录 一、数据类型 1、基本数据类型 2、组合数据类型 二、介绍两个函数 1、 isinstance函数 2、len函数 三、Python指针 1、指针 2、is运算符和的区别 3、列表的指针 四、函数参数的传递 1、例子一 2、例子二 五、字符串详解 1、转义字符 2、字符串的切片 3、字…

Doris vs ClickHouse 企业级实时分析引擎怎么选?

Apache Doris 与 ClickHouse 同作为OLAP领域的佼佼者&#xff0c;在企业级实时分析引擎该如何选择呢。本文将详细介绍 Doris 的优势&#xff0c;并通过直观对比展示两者的关键差异&#xff0c;同时分享一个企业成功用 Doris 替换 ClickHouse 的实践案例&#xff0c;帮助您做出明…

【ThreeJS Basics 09】Debug

文章目录 简介从 dat.GUI 到 lil-gui例子安装 lil-gui 并实例化不同类型的调整改变位置针对非属性的调整复选框颜色 功能/按钮调整几何形状文件夹调整 GUI宽度标题关闭文件夹隐藏按键切换 结论 简介 每一个创意项目的一个基本方面是能够轻松调整。开发人员和参与项目的其他参与…

Android Native 之 文件系统挂载

一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知&#xff0c;init进程为android系统的第一个进程&#xff0c;也是native世界的开端&#xff0c;要想让整个android世界能够稳定的运行&#xff0c;文件系统的创建和初始化是必不可少的&#xff…

Chain of Draft: 借鉴人类草稿思维让大型语言模型更快地思考

这个研究探讨了大型语言模型&#xff08;LLMs&#xff09;在执行复杂推理任务时面临的计算资源消耗与响应延迟问题。研究特别聚焦于思维链&#xff08;Chain-of-Thought, CoT&#xff09;提示范式的效率局限性。CoT虽然有效&#xff0c;但在推理过程中需要生成冗长、详尽的逐步…

《A++ 敏捷开发》- 18 软件需求

需求并不是关于需求 (Requirements are not really about requirements) 大家去公共图书馆寄存物品&#xff0c;以前都是扫二维码开箱&#xff0c;有些图书馆升级了使用指纹识别。 “是否新方法比以前好&#xff1f;”我问年轻的开发人员。 “当然用指纹识别好。新技术&#x…

【智能体架构:Agent】LangChain智能体类型ReAct、Self-ASK的区别

1. 什么是智能体 将大语言模型作为一个推理引擎。给定一个任务&#xff0c; 智能体自动生成完成任务所需步骤&#xff0c; 执行相应动作&#xff08;例如选择并调用工具&#xff09;&#xff0c; 直到任务完成。 2. 先定义工具&#xff1a;Tools 可以是一个函数或三方 API也…

Vue进阶之Vue3源码解析(一)

Vue3源码解析 目录结构编译compiler-corepackage.jsonsrc/index.ts 入口文件src/compile.ts生成ASTsrc/parse.ts 代码转换src/transform.ts几种策略模式src/transforms/transformElement.tssrc/transforms/transformText.tssrc/transforms/transformExpression.ts 代码生成src/…

servlet tomcat

在spring-mvc demo程序运行到DispatcherServlet的mvc处理 一文中&#xff0c;我们实践了浏览器输入一个请求&#xff0c;然后到SpringMvc的DispatcherServlet处理的整个流程. 设计上这些都是tomcat servlet的处理 那么究竟这是怎么到DispatcherServlet处理的&#xff0c;本文将…

【我的待办(MyTodolists)-免费无内购的 IOS 应用】

我的待办&#xff08;MyTodolists&#xff09; 我的待办&#xff1a;智能任务管理助手应用说明主要功能为什么选择"我的待办"&#xff1f;隐私保障使用截图 我的待办&#xff1a;智能任务管理助手 应用说明 "我的待办"是一款智能化的任务管理应用&#x…

GCC RISCV 后端 -- C语言语法分析过程

在 GCC 编译一个 C 源代码时&#xff0c;先会通过宏处理&#xff0c;形成 一个叫转译单元&#xff08;translation_unit&#xff09;&#xff0c;接着进行语法分析&#xff0c;C 的语法分析入口是 static void c_parser_translation_unit(c_parser *parser); 接着就通过类似递…

Vim复制内容到系统剪切板

参考链接 【Vim】Vim 中将文件内容复制到系统剪切板的方法_vi 复制到系统剪贴板-CSDN博客 [转]vim如何复制到系统剪贴板 - biiigwang - 博客园 1. 确定Vim是否支持复制到系统剪切板 输入命令 vim --version | grep clipboard 如果是开头&#xff0c;说明支持系统剪切板&…

测试用大模型组词

已经把hanzi-writer的js的调用、hanzi-writer调用的数千个汉字的json文件&#xff0c;全都放在本地了。虽然用的办法还是比较笨的。我注意到 大模型也可以部署本地&#xff0c;虽然使用频率低的情况下不划算。 尝试直接通过html的javascript通过api key调用大语言模型&#x…

华为eNSP:配置单区域OSPF

一、什么是OSPF&#xff1f; OSPF&#xff08;Open Shortest Path First&#xff0c;开放最短路径优先&#xff09;是一种链路状态路由协议&#xff0c;属于内部网关协议&#xff08;IGP&#xff09;&#xff0c;主要用于在单一自治系统&#xff08;AS&#xff09;内部动态发现…

P62 线程

这篇文章我们来讲一下线程。截止到目前&#xff0c;我们的代码都是在单线程上运行的&#xff0c;现在看起来没有什么问题&#xff0c;但是目前所有的计算机几乎都不只有一个逻辑线程&#xff0c;所以如果我们一直使用单线程运行&#xff0c;这样的话效率会很低。尤其是如果我们…

Android AudioFlinger(五)—— 揭开AudioMixer面纱

前言&#xff1a; 在 Android 音频系统中&#xff0c;AudioMixer 是音频框架中一个关键的组件&#xff0c;用于处理多路音频流的混音操作。它主要存在于音频回放路径中&#xff0c;是 AudioFlinger 服务的一部分。 上一节我们讲threadloop的时候&#xff0c;提到了一个函数pr…

im即时聊天客服系统SaaS还是私有化部署:成本、安全与定制化的权衡策略

随着即时通讯技术的不断发展&#xff0c;IM即时聊天客服系统已经成为企业与客户沟通、解决问题、提升用户体验的重要工具。在选择IM即时聊天客服系统时&#xff0c;企业面临一个重要决策&#xff1a;选择SaaS&#xff08;软件即服务&#xff09;解决方案&#xff0c;还是进行私…