用C、python手写redis客户端,兼容redis集群 (-MOVED和-ASK),快速搭建redis集群
redis通信协议
回复(服务端可客户端恢复的协议)
Bulk Strings(批量回复)
二进制
redis集群
超简单搭建redis集群
Redis 集群的数据分片
Redis 集群协议中的客户端和服务器端
-MOVED
C语言实现redis客户端
代码如下:
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/poll.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h> ssize_t sock_write_loop( int fd, const void *vptr, size_t n )
{
size_t nleft = ;
ssize_t nwritten = ;
const char *ptr; ptr = (char *) vptr;
nleft = n; while( nleft > )
{
if( (nwritten = write(fd, ptr, nleft) ) <= )
{
if( errno == EINTR )
{
nwritten = ; //再次调用write
}
else
{
return -;
}
}
nleft = nleft - nwritten;
ptr = ptr + nwritten;
}
return(n);
} int sock_read_wait( int fd, int timeout )
{
struct pollfd pfd; pfd.fd = fd;
pfd.events = POLLIN;
pfd.revents = ; timeout *= ; for (;;)
{
switch( poll(&pfd, , timeout) )
{
case -:
if( errno != EINTR )
{
return (-);
}
continue; case :
errno = ETIMEDOUT;
return (-); default:
if( pfd.revents & POLLIN )
return ();
else
return (-);
}
} } ssize_t sock_read_tmo( int fd, void *vptr, size_t len, int timeout )
{
if( timeout > && sock_read_wait(fd, timeout) < )
return (-);
else
return (read(fd, vptr, len)); } int sock_connect_nore(const char *IPaddr , int port , int timeout)
{
// char temp[4096];
int sock_fd = , n = , errcode = ;
struct sockaddr_in servaddr; if( IPaddr == NULL )
{
return -;
} if( (sock_fd = socket(AF_INET, SOCK_STREAM, ) ) < )
{
return -;
} memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port); //changed by navy 2003.3.3 for support domain addr
//if( (servaddr.sin_addr.s_addr = inet_addr(IPaddr) ) == -1 )
if( (errcode = inet_pton(AF_INET, IPaddr, &servaddr.sin_addr) ) <= )
{
//added by navy 2003.3.31 for support domain addr
struct hostent* pHost = NULL, host;
char sBuf[], sHostIp[];
int h_errnop = ; memset(&host, , sizeof(host));
memset(sBuf, , sizeof(sBuf));
memset(sHostIp, , sizeof(sHostIp));
pHost = &host; #ifdef _SOLARIS_PLAT
//solaris
if( (gethostbyname_r(IPaddr, pHost, sBuf, sizeof(sBuf), &h_errnop) == NULL) ||
#else
//linux
if( (gethostbyname_r(IPaddr, pHost, sBuf, sizeof(sBuf), &pHost, &h_errnop) != ) ||
#endif
(pHost == NULL) )
{
close(sock_fd);
return -;
} if( pHost->h_addrtype != AF_INET && pHost->h_addrtype != AF_INET6 )
{
close(sock_fd);
return -;
} //目前仅取第一个IP地址
if( (inet_ntop(pHost->h_addrtype, *(pHost->h_addr_list), sHostIp, sizeof(sHostIp)) ) == NULL )
{
close(sock_fd);
return -;
} if( (errcode = inet_pton(AF_INET, sHostIp, &servaddr.sin_addr) ) <= )
{
close(sock_fd); return -;
}
//end added by navy 2003.3.31
} if( (errcode = sock_timed_connect(sock_fd, (struct sockaddr *)&servaddr,
sizeof(servaddr), timeout) ) < )
{
close(sock_fd); return -;
} return sock_fd;
} int sock_connect(const char *IPaddr , int port , int timeout)
{
char temp[];
int sock_fd = , n = , errcode = ;
struct sockaddr_in servaddr; if( IPaddr == NULL )
{
return -;
} if( (sock_fd = socket(AF_INET, SOCK_STREAM, ) ) < )
{
return -;
} memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port); //changed by navy 2003.3.3 for support domain addr
//if( (servaddr.sin_addr.s_addr = inet_addr(IPaddr) ) == -1 )
if( (errcode = inet_pton(AF_INET, IPaddr, &servaddr.sin_addr) ) <= )
{
//added by navy 2003.3.31 for support domain addr
struct hostent* pHost = NULL, host;
char sBuf[], sHostIp[];
int h_errnop = ; memset(&host, , sizeof(host));
memset(sBuf, , sizeof(sBuf));
memset(sHostIp, , sizeof(sHostIp));
pHost = &host; #ifdef _SOLARIS_PLAT
//solaris
if( (gethostbyname_r(IPaddr, pHost, sBuf, sizeof(sBuf), &h_errnop) == NULL) ||
#else
//linux
if( (gethostbyname_r(IPaddr, pHost, sBuf, sizeof(sBuf), &pHost, &h_errnop) != ) ||
#endif
(pHost == NULL) )
{
close(sock_fd);
return -;
} if( pHost->h_addrtype != AF_INET && pHost->h_addrtype != AF_INET6 )
{
close(sock_fd);
return -;
} //目前仅取第一个IP地址
if( (inet_ntop(pHost->h_addrtype, *(pHost->h_addr_list), sHostIp, sizeof(sHostIp)) ) == NULL )
{
close(sock_fd);
return -;
} if( (errcode = inet_pton(AF_INET, sHostIp, &servaddr.sin_addr) ) <= )
{
close(sock_fd); return -;
}
//end added by navy 2003.3.31
} if( (errcode = sock_timed_connect(sock_fd, (struct sockaddr *)&servaddr,
sizeof(servaddr), timeout) ) < )
{
close(sock_fd); return -;
} n = sock_read_tmo(sock_fd, temp, , timeout); //一般错误
if( n <= )
{
close(sock_fd); sock_fd = -;
} return sock_fd;
} int sock_non_blocking(int fd, int on)
{
int flags; if ((flags = fcntl(fd, F_GETFL, )) < ){
return -;
}
if (fcntl(fd, F_SETFL, on ? flags | O_NONBLOCK : flags & ~O_NONBLOCK) < ){
return -;
}
return ;
} int sock_write_wait(int fd, int timeout)
{
struct pollfd pfd; pfd.fd = fd;
pfd.events = POLLOUT;
pfd.revents = ; timeout *= ; for (;;)
{
switch( poll(&pfd, , timeout) )
{
case -:
if( errno != EINTR )
{ return (-);
}
continue; case :
errno = ETIMEDOUT;
return (-); default:
if( pfd.revents & POLLOUT )
return ();
else
return (-);
}
} } int sock_timed_connect(int sock, struct sockaddr * sa, int len, int timeout)
{
int error = ;
socklen_t error_len; sock_non_blocking(sock, ); if( connect(sock, sa, len) == )
{
sock_non_blocking(sock, );
return ();
} if( errno != EINPROGRESS )
{
sock_non_blocking(sock, );
return (-);
} /*
* A connection is in progress. Wait for a limited amount of time for
* something to happen. If nothing happens, report an error.
*/
if( sock_write_wait(sock, timeout) != )
{
sock_non_blocking(sock, );
return (-);
} /*
* Something happened. Some Solaris 2 versions have getsockopt() itself
* return the error, instead of returning it via the parameter list.
*/
error = ;
error_len = sizeof(error); if( getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &error, &error_len) != )
{
sock_non_blocking(sock, );
return (-);
}
if( error )
{
errno = error;
sock_non_blocking(sock, );
return (-);
} sock_non_blocking(sock, );
/*
* No problems.
*/
return (); } static int check_ip_in_list(const char *ip, char *iplist)
{
char *token = NULL;
char *saveptr = NULL;
token = strtok_r(iplist, ",", &saveptr);
while(token != NULL)
{
char *ptmp = NULL;
char *ip_mask = strtok_r(token, "/", &ptmp);
if(!ip_mask)
return -; char *ip_bit = strtok_r(NULL, "/", &ptmp); if(ip_bit)
{
int mask_bit = atoi(ip_bit);
if(mask_bit < || mask_bit >)
continue; unsigned long addr[] = { };
sscanf( ip_mask, "%lu.%lu.%lu.%lu", addr, addr + , addr + , addr + );
unsigned long vl1 = addr[] << | addr[] << | addr[] << | addr[]; sscanf( ip, "%lu.%lu.%lu.%lu", addr, addr + , addr + , addr + );
unsigned long vl2 = addr[] << | addr[] << | addr[] << | addr[]; vl1 = ( vl1 >> ( - mask_bit ) );
vl2 = ( vl2 >> ( - mask_bit ) ); if( vl1 == vl2 )
return ;
}
else
{
if(strcmp(ip,ip_mask) == )
return ;
} token = strtok_r(NULL, ",", &saveptr);
} return ;
} static int check_ip_in_redis(const char *redis_host, const char *ip,const char *rq_pro)
{
char buf[];
int loops = ; strcpy(buf, redis_host); do
{
loops ++;
char *ptmp = NULL;
char *host = strtok_r(buf, ":", &ptmp);
if(!host) return -;
char *s_port = strtok_r(NULL, ":", &ptmp);
if(!s_port) return -;
int port = atoi(s_port);
char respone[] = {}; int sock_fd = -;
if((sock_fd = sock_connect_nore(host, port, ))<)
return -; if(sock_write_loop(sock_fd, rq_pro, strlen(rq_pro)) != strlen(rq_pro))
{
close(sock_fd);
return -;
} if(sock_read_tmo(sock_fd, respone, sizeof(respone)-, )<=)
{
close(sock_fd);
return -;
} if(strncmp(":0", respone, ) == )
{
close(sock_fd);
return ;
}
else if(strncmp(":1", respone, ) == )
{
close(sock_fd);
return ;
}
else if(strncmp("$", respone, ) == )
{
int data_size = ;
int ret = ; char *data_line = strstr(respone,"\r\n");
if(!data_line)
{
close(sock_fd);
return -;
}
data_line = data_line+; data_size = atoi(respone+);
if(data_size == -)
{
close(sock_fd);
return ;
}
if(strlen(data_line) == data_size+)
{
printf("line = %d, data_line = %s\n",__LINE__,data_line);
ret=check_ip_in_list(ip, data_line);
close(sock_fd);
return ret;
}
char *data = calloc(data_size+,);
if(!data)
{
close(sock_fd);
return -;
}
strcpy(data,data_line);
int read_size = strlen(data);
int left_size = data_size + - read_size;
while(left_size > )
{
int nread = sock_read_tmo(sock_fd, data+read_size, left_size, );
if(nread<=)
{
free(data);
close(sock_fd);
return -;
}
read_size += nread;
left_size -= nread;
}
close(sock_fd);
printf("line = %d, data = %s\n",__LINE__,data);
ret=check_ip_in_list(ip, data);
free(data);
return ret;
}
else if(strncmp("-MOVED", respone, ) == )
{
close(sock_fd);
char *p = strchr(respone, ' ');
if(p == NULL)
return -; p = strchr(p+, ' ');
if(p == NULL)
return -; strcpy(buf, p+);
}
else
{
close(sock_fd);
return -;
} }while(loops < ); return -;
} int main(int argc,char *argv[])
{
if(argc != )
{
printf("please input ip\n");
return -;
}
const char *redis_ip = "127.0.0.1:7002";
const char *domain = "test.com"; char exist_pro[] = {};
char get_pro[] = {};
snprintf(exist_pro,sizeof(exist_pro),"EXISTS test|%s|%s\r\n",domain,"127.0.0.1");
snprintf(get_pro,sizeof(get_pro),"GET test_%s\r\n",domain);
int loops = ;
int ret = ;
do
{
loops ++;
ret = check_ip_in_redis(redis_ip, argv[],exist_pro);
if(ret == )
ret = check_ip_in_redis(redis_ip, argv[],get_pro);
}while(loops < && ret < ); printf("line = %d, ret = %d\n",__LINE__,ret); return ret;
}
c_redis_cli.c
主要看这个check_ip_in_redis函数就行了,其它都是一些socket的封装。
python实现redis客户端
#!/usr/bin/python import sys
import socket def main(argv):
if(len(argv) != 3):
print "please input domain ip!"
return
host = "192.168.188.47"
port = 7002
while 1:
s = socket.socket()
s.connect((host, port)) cmd = 'set %s_white_ip %s\r\n' % (argv[1],argv[2])
s.send(cmd)
res = s.recv(32)
s.close() if res[0] == "+":
print "set domain white ip suc!"
return
elif res[0:6] == "-MOVED":
list = res.split(" ")
ip_list = list[2].split(":")
host = ip_list[0]
port = int(ip_list[1])
else:
print "set domain white ip error!"
return if __name__ == "__main__":
main(sys.argv)
总结
多去学一些,想想redis客户端怎么实现的,对redis的理解会更加深入,写了这部分之后,对redis集群有了更加深入的了解了
用C、python手写redis客户端,兼容redis集群 (-MOVED和-ASK),快速搭建redis集群的更多相关文章
- python手写bp神经网络实现人脸性别识别1.0
写在前面:本实验用到的图片均来自google图片,侵删! 实验介绍 用python手写一个简单bp神经网络,实现人脸的性别识别.由于本人的机器配置比较差,所以无法使用网上很红的人脸大数据数据集(如lf ...
- 使用java语言基于SMTP协议手写邮件客户端
使用java语言基于SMTP协议手写邮件客户端 1. 说明 电子邮件是互联网上常见的应用,他是互联网早期的产品,直至今日依然受到广大用户的喜爱(在中国可能因为文化背景不同,电子邮件只在办公的时候常用) ...
- [Windows Azure] 使用 Windows Azure 快速搭建 Redis 服务器
[Windows Azure] 使用 Windows Azure 快速搭建 Redis 服务器 Redis相信玩开源,大数据的朋友们并不陌生,大家最熟悉的使用者就是新浪微博,微博的整体数据缓存都是 ...
- centos7 快速搭建redis集群环境
本文主要是记录一下快速搭建redis集群环境的方式. 环境简介:centos 7 + redis-3.2.4 本次用两个服务6个节点来搭建:192.168.116.120 和 192.168.1 ...
- Python 手写数字识别-knn算法应用
在上一篇博文中,我们对KNN算法思想及流程有了初步的了解,KNN是采用测量不同特征值之间的距离方法进行分类,也就是说对于每个样本数据,需要和训练集中的所有数据进行欧氏距离计算.这里简述KNN算法的特点 ...
- python手写神经网络实现识别手写数字
写在开头:这个实验和matlab手写神经网络实现识别手写数字一样. 实验说明 一直想自己写一个神经网络来实现手写数字的识别,而不是套用别人的框架.恰巧前几天,有幸从同学那拿到5000张已经贴好标签的手 ...
- redis客户端修改了key-value对之后有时会报MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist o...错误,不能持久化
解决方案,连接redis客户端 redis目录下:redis-cli -h 127.0.0.1 -p 6379-h后为redis服务器ip,-p后为端口号进入redis-client之后输入命令 co ...
- 快速搭建Redis缓存数据库
之前一篇随笔——Redis安装及主从配置已经详细的介绍过Redis的安装于配置.本文要讲的是如何在已经安装过Redis的机器上快速的创建出一个新的Redis缓存数据库. 一.环境介绍 1) Linux ...
- 快速搭建redis单机版和redis集群版
单机版 第一步:需要安装redis所需的C语言环境,若虚拟机联网,则执行 yum install gcc-c++ 第二步:redis的源码包上传到linux系统 第三步:解压缩redis tar ...
随机推荐
- BN系列-未完待续
BN.LN.IN.GN Cross-Iteration Batch Normalization 因为有时候我们的计算能力有限,所以BN设置的比较小,这样BN效果就比较差. 因此我们将最近几次的batc ...
- ResNeSt:Split attention
https://www.cnblogs.com/xiximayou/p/12728644.html 下面是SE和SK这两个网络,兄弟俩很相似 下面是具体的每个cardinal(翻译为枢纽)网络,和SK ...
- java final关键字与static关键字
一 final关键字 1.final修饰类不可以被继承,但是可以继承其他类. 例如: class Yy {} final class Fu extends Yy{} //可以继承Yy类 class ...
- 洛谷P1048 采药 二维dp化一维
题目描述 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师.为此,他想拜附近最有威望的医师为师.医师为了判断他的资质,给他出了一个难题.医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个 ...
- K8s集群verification error" while trying to verify candidate authority certificate "kubernetes"
问题内容 because of "crypto/rsa: verification error" while trying to verify candidate authorit ...
- css中关于:nth-child()和:nth-of-type()的深入理解
css中关于:nth-child()和:nth-of-type()的深入理解 在css中有这样一类属性,是以:nth-开头的,其中最常见的就是:nth-child() 和 :nth-of-type() ...
- Object.prototype.__proto__, [[prototype]] 和 prototype
Object.prototype.__proto__ , [[prototype]] 和 prototype Object.prototype.__proto__ 是什么? __proto__ 是一个 ...
- Petya and Graph/最大权闭合子图、最小割
原题地址:https://codeforces.com/contest/1082/problem/G G. Petya and Graph time limit per test 2 seconds ...
- window下git多账户管理
前言 一般情况下,我们都是一台电脑配置一个Git账号,使用如下命令: git config --global user.name "your name" git config -- ...
- psutil 简单使用!
psutil.cpu_percent() cpu 百分比 mem = psutil.virtual_memory()mem.total,mem.used mem.free psutil.cpu_cou ...