【brpc学习实践十三】基于brpc的redis client的实现

brpc支持了redis协议,提供了相关redis访问接口,充分利用了bthread,可以坐到比hiredis更高效。

brpc redis与hiredis的对比

相比使用hiredis(官方client)的优势有:

  • 线程安全。用户不需要为每个线程建立独立的client。
  • 支持同步、异步、批量同步、批量异步等访问方式,能使用ParallelChannel等组合访问方式。
  • 支持多种连接方式。支持超时、backup request、取消、tracing、内置服务等一系列RPC基本福利。
  • 一个进程中的所有brpc client和一个redis-server只有一个连接。多个线程同时访问一个redis-server时更高效(见性能)。无论reply的组成多复杂,内存都会连续成块地分配,并支持短串优化(SSO)进一步提高性能。

像http一样,brpc保证在最差情况下解析redis reply的时间复杂度也是O(N),N是reply的字节数,而不是O( N 2 N^2 N2)。当reply是个较大的数组时,这是比较重要的。

加上-redis_verbose后会打印出所有的redis request和response供调试。

访问单台redis

创建一个访问redis的Channel:

#include <brpc/redis.h>
#include <brpc/channel.h>brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_REDIS;
brpc::Channel redis_channel;
if (redis_channel.Init("0.0.0.0:6379", &options) != 0) {  // 6379是redis-server的默认端口LOG(ERROR) << "Fail to init channel to redis-server";return -1;
}
...

执行SET后再INCR:

std::string my_key = "my_key_1";
int my_number = 1;
...
// 执行"SET <my_key> <my_number>"
brpc::RedisRequest set_request;
brpc::RedisResponse response;
brpc::Controller cntl;
set_request.AddCommand("SET %s %d", my_key.c_str(), my_number);
redis_channel.CallMethod(NULL, &cntl, &set_request, &response, NULL/*done*/);
if (cntl.Failed()) {LOG(ERROR) << "Fail to access redis-server";return -1;
}
// 可以通过response.reply(i)访问某个reply
if (response.reply(0).is_error()) {LOG(ERROR) << "Fail to set";return -1;
}
// 可用多种方式打印reply
LOG(INFO) << response.reply(0).c_str()  // OK<< response.reply(0)          // OK<< response;                  // OK
...

执行INCR <my_key>

brpc::RedisRequest incr_request;
incr_request.AddCommand("INCR %s", my_key.c_str());
response.Clear();
cntl.Reset();
redis_channel.CallMethod(NULL, &cntl, &incr_request, &response, NULL/*done*/);
if (cntl.Failed()) {LOG(ERROR) << "Fail to access redis-server";return -1;
}
if (response.reply(0).is_error()) {LOG(ERROR) << "Fail to incr";return -1;
}
// 可用多种方式打印结果
LOG(INFO) << response.reply(0).integer()  // 2<< response.reply(0)            // (integer) 2<< response;                    // (integer) 2

批量执行incr或decr

brpc::RedisRequest request;
brpc::RedisResponse response;
brpc::Controller cntl;
request.AddCommand("INCR counter1");
request.AddCommand("DECR counter1");
request.AddCommand("INCRBY counter1 10");
request.AddCommand("DECRBY counter1 20");
redis_channel.CallMethod(NULL, &cntl, &request, &response, NULL/*done*/);
if (cntl.Failed()) {LOG(ERROR) << "Fail to access redis-server";return -1;
}
CHECK_EQ(4, response.reply_size());
for (int i = 0; i < 4; ++i) {CHECK(response.reply(i).is_integer());CHECK_EQ(brpc::REDIS_REPLY_INTEGER, response.reply(i).type());
}
CHECK_EQ(1, response.reply(0).integer());
CHECK_EQ(0, response.reply(1).integer());
CHECK_EQ(10, response.reply(2).integer());
CHECK_EQ(-10, response.reply(3).integer());

RedisRequest

一个RedisRequest可包含多个Command,调用AddCommand*增加命令,成功返回true,失败返回false并会打印调用处的栈

bool AddCommand(const char* fmt, ...);
bool AddCommandV(const char* fmt, va_list args);
bool AddCommandByComponents(const butil::StringPiece* components, size_t n);

格式和hiredis基本兼容:即%b对应二进制数据(指针+length),其他和printf的参数类似。对一些细节做了改进:当某个字段包含空格时,使用单引号或双引号包围起来会被视作一个字段。比如AddCommand(“Set ‘a key with space’ ‘a value with space as well’”)中的key是a key with space,value是a value with space as well。在hiredis中必须写成redisvCommand(…, “SET %s %s”, “a key with space”, “a value with space as well”);

AddCommandByComponents类似hiredis中的redisCommandArgv,用户通过数组指定命令中的每一个部分。这个方法对AddCommand和AddCommandV可能发生的转义问题免疫,且效率最高。如果你在使用AddCommand和AddCommandV时出现了“Unmatched quote”,“无效格式”等问题且无法定位,可以试下这个方法。

如果AddCommand*失败,后续的AddCommand*和CallMethod都会失败。一般来说不用判AddCommand*的结果,失败后自然会通过RPC失败体现出来。

command_size()可获得(成功)加入的命令个数。

调用Clear()后可重用RedisRequest

RedisResponse

RedisResponse可能包含一个或多个RedisReply,reply_size()可获得reply的个数,reply(i)可获得第i个reply的引用(从0计数)。注意在hiredis中,如果请求包含了N个command,获取结果也要调用N次redisGetReply。但在brpc中这是不必要的,RedisResponse已经包含了N个reply,通过reply(i)获取就行了。只要RPC成功,response.reply_size()应与request.command_size()相等,除非redis-server有bug,redis-server工作的基本前提就是reply和command按序一一对应。

每个reply可能是:

  • REDIS_REPLY_NIL:redis中的NULL,代表值不存在。可通过is_nil()判定。
  • REDIS_REPLY_STATUS:在redis文档中称为Simple String。一般是操作的返回状态,比如SET返回的OK。可通过is_string()判定(和string相同),c_str()或data()获得值。
  • REDIS_REPLY_STRING:在redis文档中称为Bulk String。大多数值都是这个类型,包括incr返回的。可通过is_string()判定,c_str()或data()获得值。
  • REDIS_REPLY_ERROR:操作出错时的返回值,包含一段错误信息。可通过is_error()判定,error_message()获得错误信息。
  • REDIS_REPLY_INTEGER:一个64位有符号数。可通过is_integer()判定,integer()获得值。
  • REDIS_REPLY_ARRAY:另一些reply的数组。可通过is_array()判定,size()获得数组大小,[i]获得对应的子reply引用。

如果response包含三个reply,分别是integer,string和一个长度为2的array。那么可以分别这么获得值:response.reply(0).integer(),response.reply(1).c_str(), repsonse.reply(2)[0]和repsonse.reply(2)[1]。如果类型对不上,调用处的栈会被打印出来,并返回一个undefined的值。

response中的所有reply的ownership属于response。当response析构时,reply也析构了。

调用Clear()后RedisResponse可以重用。

访问redis集群

建立一个使用一致性哈希负载均衡算法(c_md5或c_murmurhash)的channel就能访问挂载在对应命名服务下的redis集群了。注意每个RedisRequest应只包含一个操作或确保所有的操作是同一个key。如果request包含了多个操作,在当前实现下这些操作总会送向同一个server,假如对应的key分布在多个server上,那么结果就不对了,这个情况下你必须把一个request分开为多个,每个包含一个操作。

或者你可以沿用常见的twemproxy方案。这个方案虽然需要额外部署proxy,还增加了延时,但client端仍可以像访问单点一样的访问它。

查看发出的请求和收到的回复

打开-redis_verbose即看到所有的redis request和response,注意这应该只用于线下调试,而不是线上程序。

打开-redis_verbose_crlf2space可让打印内容中的CRLF (\r\n)变为空格,方便阅读。

NameValueDescriptionDefined At
redis_verbosefalse[DEBUG] Print EVERY redis request/responsesrc/brpc/policy/redis_protocol.cpp
redis_verbose_crlf2spacefalse[DEBUG] Show \r\n as a spacesrc/brpc/redis.cpp

另外,brpc还提供了一个类似与redis-cli的访问工具

Command Line Interface

example/redis_c++/redis_cli是一个类似于官方CLI的命令行工具,以展示brpc对redis协议的处理能力。当使用brpc访问redis-server出现不符合预期的行为时,也可以使用这个CLI进行交互式的调试。

和官方CLI类似,redis_cli <command>也可以直接运行命令,-server参数可以指定redis-server的地址。

$ ./redis_cli __          _     __/ /_  ____ _(_)___/ /_  __      _________  _____/ __ \/ __ `/ / __  / / / /_____/ ___/ __ \/ ___// /_/ / /_/ / / /_/ / /_/ /_____/ /  / /_/ / /__  /_.___/\__,_/_/\__,_/\__,_/     /_/  / .___/\___/  /_/            
This command-line tool mimics the look-n-feel of official redis-cli, as a 
demostration of brpc's capability of talking to redis server. The 
o

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

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

相关文章

智慧机场视频监控系统方案:AI智能助力机场智慧运营

一、方案背景 随着人们生活物质水平的上升&#xff0c;人们对机场的需求也日益增多&#xff0c;在民航新建、迁建、扩建机场项目猛增的同时&#xff0c;也需同步配备相应的安防监控系统&#xff0c;以满足民航机场安全管理要求和机场运营业务的高速发展。 二、方案概述 智慧机…

C# 适配器模式

适配器模式是一种结构型设计模式&#xff0c;它可以将一个或多个不兼容的接口适配成客户端期望的接口。在 C# 中&#xff0c;适配器模式通常采用类适配器或对象适配器的方式实现。 下面是一个简单的类适配器示例&#xff1a; 首先&#xff0c;定义一个客户端期望的目标接口&a…

【2023第十二届“认证杯”数学中国数学建模国际赛】A题 太阳黑子预报完整解题思路

A题 太阳黑子预报 题目任务思路分析第一问第二问第三问 题目 太阳黑子是太阳光球上的一种现象&#xff0c;表现为比周围区域更暗的临时斑点。它们是由于磁通量集中而导致表面温度降低的区域&#xff0c;磁通量的集中抑制了对流。太阳黑子出现在活跃区域内&#xff0c;通常成对…

MySQL 教程 1.4

MySQL 连接 使用mysql二进制方式连接 您可以使用MySQL二进制方式进入到mysql命令提示符下来连接MySQL数据库。 实例 以下是从命令行中连接mysql服务器的简单实例&#xff1a; [roothost]# mysql -u root -p Enter password:****** 在登录成功后会出现 mysql> 命令提示窗…

Redis7--基础篇6(复制replica)

1. 复制(replica)介绍 Redis数据库支持主从复制&#xff0c;master以写为主&#xff0c;slave以读为主&#xff0c;当master数据变化的时候&#xff0c;自动将新的数据异步同步到slave数据库。 实现读写分离、容灾恢复、数据备份、水平扩容支撑高并发。 2. 案例演示 2.1 架构…

知虾如何选品丨如何使用知虾进行选品和优化策略

在Shopee平台上作为卖家&#xff0c;如何进行选品和优化策略是一个至关重要的问题。而知虾作为一个为Shopee卖家提供数据分析的工具&#xff0c;可以帮助您更好地进行选品和优化策略。在本文中&#xff0c;我们将介绍如何使用知虾进行选品&#xff0c;并提供一些建议和步骤。 知…

powershell获取微软o365 21v日志

0x00 背景 o365 21v为o365的大陆版本&#xff0c;主要给国内用户使用。微软提供了powershell工具和接口获取云上日志。微软o365国内的代理目前是世纪互联。本文介绍如何用powershell和配置证书拉取云上日志。 0x01 实践 第一步&#xff0c;ip权限开通&#xff1a; 由世纪互联…

enumerate函数(python)

enumerate()函数是Python内置函数之一&#xff0c;用于给可迭代对象&#xff08;如列表、元组、字符串等&#xff09;添加一个索引&#xff0c;并以迭代器的形式返回。 enumerate()函数的语法如下&#xff1a; enumerate(iterable, start0)参数说明&#xff1a; iterable&am…

MFC、VC++操作excel后,excel程序进程无法正常退出的非暴力处理方法

先说处理方式 1、最low的方式&#xff1a;强制结束进程 //打开进程得到进程句柄 HANDLE hProcessOpenProcess(PROCESS_ALL_ACCESS,FALSE,Pid); if(hProcess!NULL) { //结束进程 if (TerminateProcess(hProcess,0)){printf("结束进程成功\n");return 0;} }这种方式…

Linux中的Swap和Mem:有什么区别?

在Linux系统中&#xff0c;内存管理是操作系统的重要部分。在内存管理方面&#xff0c;Swap和Mem是两种不同的内存类型&#xff0c;它们在Linux系统中发挥着不同的作用。本文将解释Swap和Mem的区别以及它们在Linux系统中的作用。 一、Swap Swap是Linux系统中的交换分区&#…

Python datetime 字符串 相互转 datetime

字符串转 datetime from datetime import datetime# 定义要转换的日期时间字符串 dt_str "2021-09-30 15:48:36"# 使用datetime.strptime()函数进行转换 dt_obj datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S") print(dt_obj)datetime 转字符串 from …

python 制作3d立体隐藏图

生成文件的3d图&#xff0c;例子&#xff1a; 文字&#xff1a; 隐藏图&#xff1a; 使用建议&#xff1a; &#xff11;、建议不用中文&#xff0c;因为中文太复杂&#xff0c;生成立体图效果不好。 &#xff12;、需要指定FONT_PATH&#xff0c;为一个ttf文件&#xff0c;…

【微服务 SpringCloudAlibaba】实用篇 · Nacos配置中心

微服务&#xff08;6&#xff09; 文章目录 微服务&#xff08;6&#xff09;1. 统一配置管理1.1 在nacos中添加配置文件1.2 从微服务拉取配置 2. 配置热更新2.1 方式一2.2 方式二 3. 配置共享1&#xff09;添加一个环境共享配置2&#xff09;在user-service中读取共享配置3&am…

【ICCV2023论文阅读】XNet(能跑通代码)

这里写目录标题 论文阅读摘要介绍方法overviewwhy use wavelet transform?融合方法用于全监督分割和半监督分割可行性分析 效果局限性总结 代码跑通去掉分布式训练生成低频和高频图片产生数据集改读取数据的位置损失函数添加自己数据集的信息结果 ps:我现在不知道自己研究方向…

用分布函数定义的随机变量的独立性的合理性

随机变量的独立性是这样定义的&#xff1a; 如果对任意 x , y x, y x,y 都有 P { X ≤ x , Y ≤ y } P { X ≤ x } P { Y ≤ y } P\{X\leq x,Y\leq y\} P\{X\leq x \}P\{Y\leq y\} P{X≤x,Y≤y}P{X≤x}P{Y≤y} 即 F ( x , y ) F X ( x ) F Y ( y ) F(x,y)F_X(x)F_Y(y) F…

Codeforces Round 911 (Div. 2)(C~E)(DFS、数论(容斥)、SCC缩点 + DAG图上DP)

​​​​​​1900C - Anjis Binary Tree 题意&#xff1a; 凯克西奇一直被安吉冷落。通过一个共同的朋友&#xff0c;他发现安吉非常喜欢二叉树&#xff0c;于是决定解决她的问题&#xff0c;以引起她的注意。Anji 给了 Keksic 一棵有 n个顶点的二叉树。顶点 1 是根&#xff…

【错误记录】Python 错误集合

Python 错误集合 文章目录 Python 错误集合TypeError: list object cannot be interpreted as an integer python 常见错误集合&#xff1a;持续更新 TypeError: ‘list’ object cannot be interpreted as an integer map [ [1, 1, 1, 1, 1, 1, 1, 0, 1, 1],[1, 0,…

利用异或、取反、自增bypass_webshell_waf

目录 引言 利用异或 介绍 eval与assert 蚁剑连接 进阶题目 利用取反 利用自增 引言 有这样一个waf用于防御我们上传的文件&#xff1a; function fun($var): bool{$blacklist ["\$_", "eval","copy" ,"assert","usort…

linux配置ssh无密码登录失败的一种原因

先说一下步骤&#xff0c;如下 参考&#xff1a;https://www.cnblogs.com/rickiyang/p/11074203.html 1、安装ssh 直接 sudo apt-get install openssh-server2、查看ssh运行状态 ps -e | grep ssh 如果发现 sshd 和 ssh-agent 即表明 ssh服务基本运行正常3、生成公钥和私钥…

大模型中的数据

简介&#xff1a; 数据是基础大模型的生命线;这些模型的训练数据在很大程度上决定了这些模型可以获得哪些功能。数据的中心性并不是基础大模型所独有的;最近对以数据为中心的人工智能的呼吁表明了管理、理解和记录用于训练机器学习模型的数据的普遍重要性。具体而言&am…