Netty实战:模拟Redis的客户端

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Netty实战:模拟Redis的客户端

Netty:模拟Redis的客户端

因为redis是部署在服务器上的 我们只需要模拟客户端发送请求即可所以只需要编写客户端的代码就可以了

前置知识

编写前我们需要知道 redis的请求规范

Redis 的通信 是需要遵循 RESP 协议的

例子:

set hello 123
*3\r\n $3 \r\n set \r\n hello \r\n 123

建立 channel 通道后 发送命令给服务端 此时是写数据【输出】 在出战handler里增加逻辑 当接受响应后 此时数据需要【输入】 存入栈 handler 中 在入栈handler 里增加逻辑

Netty中 Redis 命令对应的类型

  • 单行
  • 错误
  • 整形数字
  • 批量回复
  • 多个批量回复

客户端

我们只需要编写客户端。请求搭建redis的服务器接受和传输数据即可就可以了

客户端还是常见的写法

public class RedisClient {
    private static final String HOST = "xxxxxxxxx";
    private static final int PORT = 6379;
    public static void main(String[] args) {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(bossGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        //  调用netty 提供的支持netty 协议的编解码器
                        //    RedisBulkStringAggregator 和 RedisarrayAggregator 是协议中两种特殊格式的聚合器
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        pipeline.addLast(new RedisDecoder());
                        pipeline.addLast(new RedisBulkStringAggregator());
                        pipeline.addLast(new RedisArrayAggregator());
                        pipeline.addLast(new RedisEncoder());
                        // 我们自己的处理器
                        pipeline.addLast(new RedisClientHandler());
                    }
                });
        try {
            ChannelFuture syncFuture = bootstrap.connect(HOST, PORT).sync();
            System.out.println("enter redis commands");
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
            for (; ; ) {
                String input = bufferedReader.readLine();
                //退出任务
                if ("quit".equals(input)) {
                    syncFuture.channel().close().sync();
                    break;
                }
                syncFuture.channel().writeAndFlush(input).sync();
            }
            syncFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
        }
    }
}

处理器

因为要读取和写入 所以我们需要集成混合处理器类 : ChannelDuplexHandler

我们需要判断五种请求类型 所以先封装一个类别输出的方法

RedisMessage Netty提供的redis的信息对象,以下五种类型都来自于它

private void printRedisResponse(RedisMessage msg) {
        if (msg instanceof SimpleStringRedisMessage) {
            SimpleStringRedisMessage tmpMsg = (SimpleStringRedisMessage) msg;
            System.out.println(tmpMsg.content());
        } else if (msg instanceof ErrorRedisMessage) {
            ErrorRedisMessage tmpMsg = (ErrorRedisMessage) msg;
            System.out.println(tmpMsg.content());
        } else if (msg instanceof IntegerRedisMessage) {
            IntegerRedisMessage tmpMsg = (IntegerRedisMessage) msg;
            System.out.println(tmpMsg.value());
        } else if (msg instanceof FullBulkStringRedisMessage) {
            FullBulkStringRedisMessage tmpMsg = (FullBulkStringRedisMessage) msg;
            if (tmpMsg.isNull()) {
                return;
            }
            System.out.println(tmpMsg.content().toString(CharsetUtil.UTF_8));
        } else if (msg instanceof ArrayRedisMessage) {
            ArrayRedisMessage tmpMsg = (ArrayRedisMessage) msg;
            for (RedisMessage child : tmpMsg.children()) {
                printRedisResponse(child);
            }
        }
        // 如果都是不是应该抛出异常
    }

写命令

// 写
    // 出栈handler 的常用方法
    //需要处理redis 命令
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        // 键盘传入的字符串 如 : keys *
        String commandStr = (String) msg;
        // 通过空格来分离出命令
        String[] commandArr = commandStr.split("\\s+");
        // 接受redis 命令的列表  RedisMessag保存命令的内容
        List<RedisMessage> redisMessageList = new ArrayList<>(commandArr.length);
        for (String cmdStr : commandArr) {
            FullBulkStringRedisMessage message = new FullBulkStringRedisMessage(
                    ByteBufUtil.writeUtf8(ctx.alloc(), cmdStr));
            redisMessageList.add(message);
        }
        //将分离的命令 合并成一个完整的命令
        RedisMessage request = new ArrayRedisMessage(redisMessageList);
        //写入通道
        ctx.write(request, promise);
    }

读取命令

// 读取
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // 此时接受的时候这个值 就是我们存储redismessage 的对象
    RedisMessage redisMessage = (RedisMessage) msg;
    // 返回的结果是不同类型 分开进行处理
    printRedisResponse(redisMessage);
    //    内存管理使用了 引用计数的方式 所以我们需要释放资源
    ReferenceCountUtil.release(redisMessage);
}

演示效果

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4天前
|
NoSQL Linux Redis
Redis -- 安装客户端redis-plus-plus
Redis -- 安装客户端redis-plus-plus
23 0
|
10天前
|
NoSQL 测试技术 Go
【Golang】国密SM2公钥私钥序列化到redis中并加密解密实战_sm2反编(1)
【Golang】国密SM2公钥私钥序列化到redis中并加密解密实战_sm2反编(1)
|
9天前
|
存储 缓存 NoSQL
由菜鸟到大神,谈谈redis的概念、实战、原理、高级使用方法
【5月更文挑战第18天】Redis是一个开源的内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。它支持多种类型的数据结构,如字符串、哈希、列表、集合、有序集合等。
23 10
|
11天前
|
存储 缓存 NoSQL
实战:第十一篇:StringRedisTemplate获取redis信息,面试官突击一问
实战:第十一篇:StringRedisTemplate获取redis信息,面试官突击一问
|
12天前
|
存储 NoSQL Redis
Redis数据结构精讲:选择与应用实战指南
Redis数据结构精讲:选择与应用实战指南
975 1
|
12天前
|
JSON NoSQL Java
深入浅出Redis(十三):SpringBoot整合Redis客户端
深入浅出Redis(十三):SpringBoot整合Redis客户端
|
12天前
|
NoSQL 网络协议 Java
Redis客户端Lettuce深度分析介绍(上)
Spring Boot自2.0版本开始默认使用Lettuce作为Redis的客户端(注1)。Lettuce客户端基于Netty的NIO框架实现,对于大多数的Redis操作,只需要维持单一的连接即可高效支持业务端的并发请求 —— 这点与Jedis的连接池模式有很大不同。同时,Lettuce支持的特性更加全面,且其性能表现并不逊于,甚至优于Jedis。本文通过分析Lettuce的特性和内部实现(基于6.0版本),及其与Jedis的对照比较,对这两种客户端,以及Redis服务端进行深度探讨。
101117 7
|
12天前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
157 16
探秘Redis分布式锁:实战与注意事项
|
12天前
|
消息中间件 监控 NoSQL
【亮剑】如何排查和解决Redis高负载问题
【4月更文挑战第30天】本文介绍了如何排查和解决Redis高负载问题。通过监控CPU、内存、网络IO和命令处理速度,可识别性能瓶颈。排查包括:分析慢查询、内存使用、网络连接和命令执行。优化措施涉及优化查询、减少复杂命令、使用连接池、调整数据结构等。建立监控系统、定期性能测试和持续优化是关键。
|
12天前
|
JSON NoSQL Java
【Redis】2、Redis 的 Java 客户端(Jedis 和 SpringDataRedis)
【Redis】2、Redis 的 Java 客户端(Jedis 和 SpringDataRedis)
56 0