iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >Golang如何实现 pipeline 模式的 redis 客户端
  • 615
分享到

Golang如何实现 pipeline 模式的 redis 客户端

2023-06-20 15:06:50 615人浏览 独家记忆
摘要

这篇文章主要介绍“golang如何实现 pipeline 模式的 redis 客户端”,在日常操作中,相信很多人在Golang如何实现 pipeline 模式的 Redis 客户端问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法

这篇文章主要介绍“golang如何实现 pipeline 模式的 redis 客户端”,在日常操作中,相信很多人在Golang如何实现 pipeline 模式的 Redis 客户端问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang如何实现 pipeline 模式的 redis 客户端”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

本文的完整代码在GitHub.com/hdt3213/godis/redis/client

通常 tcp 客户端的通信模式都是阻塞式的: 客户端发送请求 -> 等待服务端响应 -> 发送下一个请求。因为需要等待网络传输数据,完成一次请求循环需要等待较多时间。

我们能否不等待服务端响应直接发送下一条请求呢?答案是肯定的。

TCP 作为全双工协议可以同时进行上行和下行通信,不必担心客户端和服务端同时发包会导致冲突。

p.s. 打电话的时候两个人同时讲话就会冲突听不清,只能轮流讲。这种通信方式称为半双工。广播只能由电台发送到收音机不能反向传输,这种方式称为单工。

我们为每一个 tcp 连接分配了一个 goroutine 可以保证先收到的请求先先回复。另一个方面,tcp 协议会保证数据流的有序性,同一个 tcp 连接上先发送的请求服务端先接收,先回复的响应客户端先收到。因此我们不必担心混淆响应所对应的请求。

这种在服务端未响应时客户端继续向服务端发送请求的模式称为 Pipeline 模式。因为减少等待网络传输的时间,Pipeline 模式可以极大的提高吞吐量,减少所需使用的 tcp 链接数。

pipeline 模式的 redis 客户端需要有两个后台协程程负责 tcp 通信,调用方通过 channel 向后台协程发送指令,并阻塞等待直到收到响应,这是一个典型的异步编程模式。

我们先来定义 client 的结构:

type Client struct {    conn        net.Conn // 与服务端的 tcp 连接    pendingReqs chan *Request // 等待发送的请求    waitingReqs chan *Request // 等待服务器响应的请求    ticker      *time.Ticker // 用于触发心跳包的计时器    addr        string    ctx        context.Context    cancelFunc context.CancelFunc    writing    *sync.WaitGroup // 有请求正在处理不能立即停止,用于实现 graceful shutdown}type Request struct {    id        uint64 // 请求id    args      [][]byte // 上行参数    reply     redis.Reply // 收到的返回值    heartbeat bool // 标记是否是心跳请求    waiting   *wait.Wait // 调用协程发送请求后通过 waitgroup 等待请求异步处理完成    err       error}

调用者将请求发送给后台协程,并通过 wait group 等待异步处理完成:

func (client *Client) Send(args [][]byte) redis.Reply {request := &request{args:      args,heartbeat: false,waiting:   &wait.Wait{},}request.waiting.Add(1)client.working.Add(1)defer client.working.Done()client.pendingReqs <- request // 请求入队timeout := request.waiting.WaitWithTimeout(maxWait) // 等待响应或者超时if timeout {return reply.MakeErrReply("server time out")}if request.err != nil {return reply.MakeErrReply("request failed")}return request.reply}

client 的核心部分是后台的读写协程。先从写协程开始:

// 写协程入口func (client *Client) handleWrite() {for req := range client.pendingReqs {client.doRequest(req)}}// 发送请求func (client *Client) doRequest(req *request) {if req == nil || len(req.args) == 0 {return}    // 序列化请求re := reply.MakeMultiBulkReply(req.args)bytes := re.ToBytes()_, err := client.conn.Write(bytes)i := 0    // 失败重试for err != nil && i < 3 {err = client.handleConnectionError(err)if err == nil {_, err = client.conn.Write(bytes)}i++}if err == nil {        // 发送成功等待服务器响应client.waitingReqs <- req} else {req.err = errreq.waiting.Done()}}

读协程是我们熟悉的协议解析器模板, 不熟悉的朋友可以到解析Redis Cluster原理了解更多。

// 收到服务端的响应func (client *Client) finishRequest(reply redis.Reply) {defer func() {if err := recover(); err != nil {debug.PrintStack()logger.Error(err)}}()request := <-client.waitingReqsif request == nil {return}request.reply = replyif request.waiting != nil {request.waiting.Done()}}// 读协程是个 RESP 协议解析器func (client *Client) handleRead() error {ch := parser.ParseStream(client.conn)for payload := range ch {if payload.Err != nil {client.finishRequest(reply.MakeErrReply(payload.Err.Error()))continue}client.finishRequest(payload.Data)}return nil}

最后编写 client 的构造器和启动异步协程的代码:

func MakeClient(addr string) (*Client, error) {    conn, err := net.Dial("tcp", addr)    if err != nil {        return nil, err    }    ctx, cancel := context.WithCancel(context.Background())    return &Client{        addr:        addr,        conn:        conn,        sendingReqs: make(chan *Request, chanSize),        waitingReqs: make(chan *Request, chanSize),        ctx:         ctx,        cancelFunc:  cancel,        writing:     &sync.WaitGroup{},    }, nil}func (client *Client) Start() {    client.ticker = time.NewTicker(10 * time.Second)    go client.handleWrite()    go func() {        err := client.handleRead()        logger.Warn(err)    }()    go client.heartbeat()}

关闭 client 的时候记得等待请求完成:

func (client *Client) Close() {    // 先阻止新请求进入队列    close(client.sendingReqs)    // 等待处理中的请求完成    client.writing.Wait()    // 释放资源    _ = client.conn.Close() // 关闭与服务端的连接,连接关闭后读协程会退出    client.cancelFunc() // 使用 context 关闭读协程    close(client.waitingReqs) // 关闭队列}

测试一下:

func TestClient(t *testing.T) {    client, err := MakeClient("localhost:6379")    if err != nil {        t.Error(err)    }    client.Start()    result = client.Send([][]byte{        []byte("SET"),        []byte("a"),        []byte("a"),    })    if statusRet, ok := result.(*reply.StatusReply); ok {        if statusRet.Status != "OK" {            t.Error("`set` failed, result: " + statusRet.Status)        }    }    result = client.Send([][]byte{        []byte("GET"),        []byte("a"),    })    if bulkRet, ok := result.(*reply.BulkReply); ok {        if string(bulkRet.Arg) != "a" {            t.Error("`get` failed, result: " + string(bulkRet.Arg))        }    }}

Keep working, we will find a way out.This is Finley, welcome to join us.

到此,关于“Golang如何实现 pipeline 模式的 redis 客户端”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

您可能感兴趣的文档:

--结束END--

本文标题: Golang如何实现 pipeline 模式的 redis 客户端

本文链接: https://www.lsjlt.com/news/297791.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Golang如何实现 pipeline 模式的 redis 客户端
    这篇文章主要介绍“Golang如何实现 pipeline 模式的 redis 客户端”,在日常操作中,相信很多人在Golang如何实现 pipeline 模式的 redis 客户端问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法...
    99+
    2023-06-20
  • Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端
    本文的完整代码在github.com/hdt3213/godis/redis/client 通常 TCP 客户端的通信模式都是阻塞式的: 客户端发送请求 -> 等待服务端响应 ...
    99+
    2024-04-02
  • Java中Socket如何实现Redis客户端
    小编给大家分享一下Java中Socket如何实现Redis客户端,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!Redis是最常见的缓存服务中间件,在java开发中,一般使用 jedis 来实现。Redis的命令协议:$参数...
    99+
    2023-06-15
  • Redis远程连接Redis客户端的实现步骤
    目录一 进入redis下的目录二 启动 redis服务三 连接客户端四 在windows本地中安装 redis可视化软件五 在linux关闭客户端六 设置防火墙规则:我的因为已经设置...
    99+
    2024-04-02
  • 怎么用golang实现rtsp客户端
    近年来,视频成为了信息传递的一个重要形式,也因此而催生了一种被广泛应用的视频流协议——RTSP(Real Time Streaming Protocol,实时流协议)。相比于HTTP,RTSP获得了更高的传输效率和更低的延迟,加上其支持多种...
    99+
    2023-05-14
  • Java Socket实现Redis客户端的详细说明
    Redis是最常见的缓存服务中间件,在java开发中,一般使用 jedis 来实现。 如果不想依赖第三方组件,自己实现一个简单的redis客户端工具,该如何实现呢?本文就是介绍这样一...
    99+
    2024-04-02
  • 如何在Golang中使用gRPC实现基础的服务端和客户端
    gRPC是一种高性能、开源和通用的远程过程调用框架,适用于跨语言和平台的RPC调用。它利用Google开发的protobuf协议进行数据传输,可以快速实现服务端和客户端的通信,并且提供了丰富的功能和扩展性。本文将介绍如何在Golang中使用...
    99+
    2023-05-14
  • redis客户端实现高可用读写分离的方式详解
    背景 (1) redis单机的读写性能轻松上大几万,不过线上环境不会只部署光秃秃的一个节点,还是会配合 sentinel 再部署一个 slave作为高可用节点的; 但是standby...
    99+
    2024-04-02
  • nodejs如何模拟客户端请求
    Node.js是一个基于事件驱动的异步I/O的开发框架,具有轻量、高效和可扩展的特点。它可以让JavaScript在服务器端运行,使得开发人员可以用同一种语言来开发前后端应用程序,从而省去了繁琐的语言切换和学习成本。在Node.js中,我们...
    99+
    2023-05-18
  • python中如何实现客户端通信
    这篇文章将为大家详细讲解有关python中如何实现客户端通信,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1、实现流程创建一套接字。用connect()函数连接到服务器。使用sendall()向服务器发送...
    99+
    2023-06-15
  • redis集群客户端java实现的方法是什么
    在Java中实现Redis集群客户端,可以使用Jedis Cluster库。Jedis Cluster是Jedis库的扩展,专门用于...
    99+
    2023-09-16
    redis java
  • Golang 的 LDAP 客户端库如何使用证书?
    “纵有疾风来,人生不言弃”,这句话送给正在学习Golang的朋友们,也希望在阅读本文《Golang 的 LDAP 客户端库如何使用证书?》后,能够真的帮助到大家。我也会在后续的文章中,陆续更新Gol...
    99+
    2024-04-05
  • 如何实现C#服务端与客户端连接
    今天就跟大家聊聊有关如何实现C#服务端与客户端连接,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。C#服务端与客户端连接实现的时间性:当服务器开始对端口侦听之后,便可以创建客户端与它建...
    99+
    2023-06-17
  • 如何在HTML5中实现客户端存储
    如何在HTML5中实现客户端存储?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。离线检测要知道设备是否在线还是离线,HTML5 定义了一个 navigator.o...
    99+
    2023-06-09
  • Python如何实现MySQL客户端操作库
    这篇文章主要介绍了Python如何实现MySQL客户端操作库,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获。下面让小编带着大家一起了解一下。PyMySQL 是一个纯 Python...
    99+
    2024-04-02
  • 如何从 Golang 客户端创建 ElasticSearch 策略
    问题内容 我正在尝试从 elastic golang 客户端 olivere 创建索引生命周期管理 (ilm) 策略,以删除超过 3 个月的索引(使用“每日索引”模式)。像这样的事情:...
    99+
    2024-02-05
  • java客户端中如何使用Jedis实现操作Redis Sentinel 连接池
    这篇文章给大家介绍java客户端中如何使用Jedis实现操作Redis Sentinel 连接池,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。pom.xml配置<dependency> <grou...
    99+
    2023-05-31
    jedis sentinel java
  • Redis客户端启动不成功如何解决
    Redis客户端启动不成功可能有多种原因,下面列举几种常见的解决方法:1. 确保Redis服务已经正确启动:首先要确保Redis服务...
    99+
    2023-08-30
    Redis
  • 使用Nodejs 实现一个简单的 Redis客户端(推荐)
    目录0. 写在前面1. 背景映入2. 数据库选择3. Nodejs TCP连接4. 代码编写5. 实验6. wireshark 抓包分析7. 杂与代码0. 写在前面 大家如果有去看过...
    99+
    2022-11-13
    Nodejs实现Redis客户端 Nodejs Redis
  • Ruby客户端中如何处理Redis序列化
    在Ruby客户端中处理Redis序列化通常涉及将数据从Ruby对象转换为Redis支持的数据类型,以便存储在Redis中,并在需要时...
    99+
    2024-04-29
    Redis Ruby
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作