go语言的redis客户端
redis3.0之后提供了新的HA的解决方案,即Cluster模式,由多个节点组成的集群模式。集群master之间基于crc16算法,对key进行校验,得到的值对16384取余,就是key的hash slot(槽)值,每个节点各自存储一部分的hash槽值,主从节点之间基于异步复制方式同步数据。
基于redis集群的基本原理,gedis需要提供一下方面的能力:
1、统一的客户端Cluster;
2、集群连接池的实现;
3、集群节点的健康检查(后续实现);
4、负载均衡机制实现;
5、协议的封装保证对上层透明。
模型基本设计如下:
基础模型定义
/**
* 节点
* master:主节点ip+port
* slaves:从节点ip+port集合
*/
type Node struct {
Url string
Pwd string
InitActive int
}
type ClusterConfig struct {
Nodes []*Node
HeartBeatInterval int
}
/**
* 集群客户端
* heartBeatInterval 心跳检测时间间隔,单位s
* clusterPool key:连接串 value:连接池
*/
type Cluster struct {
config *ClusterConfig
clusterPool map[string]*ConnPool
}
Cluster初始化
/**
* 初始化Cluster client
*/
func NewCluster(clusterConfig ClusterConfig) *Cluster {
nodes := clusterConfig.Nodes
var cluster Cluster
clusterPool := make(map[string]*ConnPool)
for _, node := range nodes {
var config = ConnConfig{node.Url, node.Pwd}
pool, _ := NewConnPool(node.InitActive, config)
clusterPool[node.Url] = pool
}
cluster.config = &clusterConfig
cluster.clusterPool = clusterPool
//初始化节点健康检测线程
defer func() {
go cluster.heartBeat()
}()
if m==nil {
m = new(sync.RWMutex)
}
return &cluster
}
节点心跳检测
cluster创建后,开启异步线程定时轮询各个节点,向节点发出ping请求,若未响应pong,则表示当前节点异常,然后将当前节点退出连接池,并将该节点加入失败队列,定时轮询队列,检测是否恢复连接,若恢复,则重新创建连接池,从失败队列中退出当前节点。
/**
* 连接池心跳检测,定时ping各个节点,ping失败的,从连接池退出,并将节点加入失败队列
* 定时轮询失败节点队列,检测节点是否已恢复连接,若恢复,则重新创建连接池,并从失败队列中移除
*/
func (cluster *Cluster) heartBeat() {
clusterPool := cluster.GetClusterPool()
interval := cluster.config.HeartBeatInterval
if interval <= 0 {
interval = defaultHeartBeatInterval
}
var nodes = make(map[string]*Node)
for i := 0; i < len(cluster.GetClusterNodesInfo()); i++ {
node := cluster.GetClusterNodesInfo()[i]
nodes[node.Url] = node
}
var failNodes = make(map[string]*Node)
for {
for url, pool := range clusterPool {
result, err := executePing(pool)
if err != nil {
log.Printf("节点[%s] 健康检查异常,原因[%s], 节点将被移除\n", url, err)
//加锁
m.Lock()
time.Sleep(time.Duration(5)*time.Second)
failNodes[url] = nodes[url]
delete(clusterPool, url)
m.Unlock()
} else {
log.Printf("节点[%s] 健康检查结果[%s]\n", url, result)
}
}
//恢复检测
recover(failNodes, clusterPool)
time.Sleep(time.Duration(interval) * time.Second)
}
}
/**
* 检测fail节点是否已恢复正常
*/
func recover(failNodes map[string]*Node, clusterPool map[string]*ConnPool) {
for url,node:=range failNodes{
conn := Connect(url)
if conn != nil {
//节点重连,恢复连接
var config = ConnConfig{url, node.Pwd}
pool, _ := NewConnPool(node.InitActive, config)
//加锁
m.Lock()
clusterPool[node.Url] = pool
delete(failNodes,url)
m.Unlock()
log.Printf("节点[%s] 已重连\n", url)
}
}
}
测试结果:
loadbalance目前仅实现随机模式,每次访问前随机选择一个节点进行通信
func (cluster *Cluster) RandomSelect() *ConnPool {
m.RLock()
defer m.RUnlock()
pools := cluster.GetClusterPool()
for _,pool:= range pools{
if pool !=nil{
return pool
}
}
fmt.Errorf("none pool can be used")
return nil
}
通信模块的大致流程如下:
1、cluster随机选择一个健康的节点,进行访问;
2、如果节点返回业务数据则通信结束;
3、如果节点返回的消息协议上满足“-MOVED”,例如 -MOVED 5678 127.0.0.1,则表明当前数据不在该节点;
4、重定向到redis指定的节点访问;
func (cluster *Cluster) Set(key string, value string) (interface{}, error) {
result, err := executeSet(cluster.RandomSelect(), key, value)
if err.Error() != protocol.MOVED {
return result, err
}
//重定向到新的节点
return executeSet(cluster.SelectOne(result.(string)), key, value)
}
func executeSet(pool *ConnPool, key string, value string) (interface{}, error) {
conn, err := GetConn(pool)
if err != nil {
return nil, fmt.Errorf("get conn fail")
}
defer pool.PutConn(conn)
result := SendCommand(conn, protocol.SET, protocol.SafeEncode(key), protocol.SafeEncode(value))
return handler.HandleReply(result)
}
这样,对于应用层来讲,无论访问的哪个节点,都能得到最终的结果,相对是透明的。
调用测试:
package main
import (
. "client"
"net"
"fmt"
)
func main() {
var node7000 = Node{"127.0.0.1:7000", "123456", 10}
var node7001 = Node{"127.0.0.1:7001", "123456", 10}
var node7002 = Node{"127.0.0.1:7002", "123456", 10}
var node7003 = Node{"127.0.0.1:7003", "123456", 10}
var node7004 = Node{"127.0.0.1:7004", "123456", 10}
var node7005 = Node{"127.0.0.1:7005", "123456", 10}
nodes := []*Node{&node7000, &node7001, &node7002, &node7003, &node7004, &node7005}
var clusterConfig = ClusterConfig{nodes,10}
cluster := NewCluster(clusterConfig)
value,err:=cluster.Get("name")
fmt.Println(value, err)
}
响应结果:
心跳检查和其他loadbalance机制后续补充实现。
项目地址:
https://github.com/zhangxiaomin1993/gedis
go语言的redis客户端的更多相关文章
- Redis客户端——Jedis的使用
本文介绍基于Java语言的Redis客户端——Jedis的使用,包括Jedis简介.获取Jedis.Jedis直连.Jedis连接池以及二者的对比的选择. Jedis简介 Jedis 是 Redis ...
- 用C、python手写redis客户端,兼容redis集群 (-MOVED和-ASK),快速搭建redis集群
想没想过,自己写一个redis客户端,是不是很难呢? 其实,并不是特别难. 首先,要知道redis服务端用的通信协议,建议直接去官网看,博客啥的其实也是从官网摘抄的,或者从其他博客抄的(忽略). 协议 ...
- Redis客户端之Spring整合Jedis,ShardedJedisPool集群配置
Jedis设计 Jedis作为推荐的java语言redis客户端,其抽象封装为三部分: 对象池设计:Pool,JedisPool,GenericObjectPool,BasePoolableObjec ...
- redis客户端(三)
redis客户端 一.>redis自带的客户端 启动 启动客户端命令:[root@ming bin]# ./redis-cli -h xxx.xxx.xx.xxx-p 6379 注意: -h:指 ...
- Java语言访问Redis数据库之Set篇
如果想通过Java语言对Redis数据库进行访问. 首先,需要安装Redis数据库,可以是Windows系统,或者Linux系统.(本文以Windows系统的本地Redis数据库为例,代码说明如何操作 ...
- Redis学习笔记--Redis客户端(三)
1.Redis客户端 1.1 Redis自带的客户端 (1)启动 启动客户端命令:[root@kwredis bin]# ./redis-cli -h 127.0.0.1 -p 6379 -h:指定访 ...
- spring 5.x 系列第8篇 —— 整合Redis客户端 Jedis和Redisson (代码配置方式)
文章目录 一.说明 1.1 Redis 客户端说明 1.2 Redis可视化软件 1.3 项目结构说明 1.3 依赖说明 二.spring 整合 jedis 2.1 新建基本配置文件和其映射类 2.2 ...
- spring 5.x 系列第7篇 —— 整合Redis客户端 Jedis和Redisson (xml配置方式)
文章目录 一.说明 1.1 Redis 客户端说明 1.2 Redis可视化软件 1.3 项目结构说明 1.3 依赖说明 二.spring 整合 jedis 2.1 新建基本配置文件 2.2 单机配置 ...
- 一文彻底理解Redis序列化协议,你也可以编写Redis客户端
前提 最近学习Netty的时候想做一个基于Redis服务协议的编码解码模块,过程中顺便阅读了Redis服务序列化协议RESP,结合自己的理解对文档进行了翻译并且简单实现了RESP基于Java语言的解析 ...
随机推荐
- CSP-S 2019文澜中学游记(11.15~11.17)
前言 今年的\(CSP-S\),本以为自己的实力与去年的\(NOIP\)相比,能有较大的提升的. 没想到,菜是原罪,弱就是弱,依然逃脱不了被吊锤的命运. \(Nov\ 15th\):\(Day\ 0\ ...
- Paper | LISTEN, ATTEND AND SPELL: A NEURAL NETWORK FOR LARGE VOCABULARY CONVERSATIONAL SPEECH RECOGNITION
目录 1. 相关工作 2. 方法细节 2.1 收听器 2.2 注意力和拼写 本文提出了一个基于神经网络的语音识别系统List, Attend and Spell(LAS),能够将语音直接转录为文字. ...
- golang--海量用户即使通讯系统
功能需求: 用户注册 用户登录 显示在线用户列表 群聊 点对点聊天 离线留言
- node 连接 mysql 数据库三种方法------笔记
一.mysql库 文档:https://github.com/mysqljs/mysql mysql有三种创建连接方式 1.createConnection 使用时需要对连接的创建.断开进行管理 2. ...
- Springboot访问静态资源&WebJars&图标&欢迎页面
目录 概述 1.访问WebJar资源 2.访问静态资源 3.favicon.ico图标 4.欢迎页面 概述 使用Springboot进行web开发时,boot底层实际用的就是springmvc,项目中 ...
- .Net ADO拼接带参数的SQL语句
首先是在DAL数据访问层中的代码://数据更新的方法public static int shuxing_update(s_passnature model) { string sql = " ...
- SpringBoot开发案例之mail中文附件名字乱码
最近在开发一个邮件发送多附件的微服务,使用的是org.springframework.mail.javamail.JavaMailSender;包下面的JavaMailSender 但是发送出来的附件 ...
- .net post请求wcf
class Program { static void Main(string[] args) { }); var r = HttpHelper.PostRequest("http://lo ...
- PDF怎么转换为CAD文件?这两种方法你的会
在日常的办公中,我们最常见的文件格式就是PDF格式的,因为PDF文件的安全性是比较高的,可以防止不小心触碰到键盘修改文件内容,而且PDF文件便于进行文件的传输.但是有时候也需要将PDF转换成CAD,那 ...
- centos 安装gitlab
1.开始安装依赖软件:yum -y install policycoreutils openssh-server openssh-clients postfix 2.设置postfix开机自启动,po ...