netty之实现一个redis的客户端

news/2024/11/2 20:06:21/

写在前面

本文看下如何使用redis来实现一个类似于redis官方提供的redis-cli.exe的客户端工具。

1:用到的模块

主要需要用到netty针对redis的编解码模块,可以解析redis的协议,从而可以实现和redis交互的功能。
在这里插入图片描述

2:正文

首先来定义客户端类:

package com.dahuyou.netty.redis.cli;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.GenericFutureListener;import javax.swing.plaf.synth.SynthRadioButtonMenuItemUI;
import java.io.BufferedReader;
import java.io.InputStreamReader;public class RedisClient {String host;    //   目标主机int port;       //   目标主机端口public RedisClient(String host,int port){this.host = host;this.port = port;}public void start() throws Exception{EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(group).channel(NioSocketChannel.class).handler(new RedisClientInitializer());Channel channel = bootstrap.connect(host, port).sync().channel();System.out.println(" connected to host : " + host + ", port : " + port);System.out.println(" type redis's command to communicate with redis-server or type 'quit' to shutdown ");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));ChannelFuture lastWriteFuture = null;for (;;) {Thread.sleep(1000);RedisClientHandler.serialNum = 0;System.out.println(host + ":" + port + ">");String s = in.readLine();if(s.equalsIgnoreCase("quit")) {break;}
//                System.out.print(">");lastWriteFuture = channel.writeAndFlush(s);lastWriteFuture.addListener(new GenericFutureListener<ChannelFuture>() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (!future.isSuccess()) {System.err.print("write failed: ");future.cause().printStackTrace(System.err);}}});}if (lastWriteFuture != null) {lastWriteFuture.sync();}System.out.println(" bye ");}finally {group.shutdownGracefully();}}public static void main(String[] args) throws Exception{
//        RedisClient client = new RedisClient("192.168.56.10",6379);RedisClient client = new RedisClient("127.0.0.1",6379);client.start();}}

这里redis server其实就是一个tcp server的角色了。
在启动类中同一个for (;;) {的死循环来等待用户录入信息,类似于redis-cli.exe的如下功能:
在这里插入图片描述
另外,通过RedisClientInitializer设置协议解析的编解码器,如下:

package com.dahuyou.netty.redis.cli;import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.redis.RedisArrayAggregator;
import io.netty.handler.codec.redis.RedisBulkStringAggregator;
import io.netty.handler.codec.redis.RedisDecoder;
import io.netty.handler.codec.redis.RedisEncoder;public class RedisClientInitializer extends ChannelInitializer<Channel> {@Overrideprotected void initChannel(Channel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new RedisDecoder());pipeline.addLast(new RedisBulkStringAggregator());pipeline.addLast(new RedisArrayAggregator());pipeline.addLast(new RedisEncoder());pipeline.addLast(new RedisClientHandler());}
}

RedisDecoder,RedisBulkStringAggregator,RedisArrayAggregator,RedisEncoder这几个类都是redis codec模块提供的编解码类,如下:
在这里插入图片描述
RedisClientHandler是我们自定义的业务处理类,源码如下:

package com.dahuyou.netty.redis.cli;import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.redis.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;import java.util.ArrayList;
import java.util.List;public class RedisClientHandler extends ChannelDuplexHandler {// 发送 redis 命令@Overridepublic void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {String[] commands = ((String) msg).split("\\s+");List<RedisMessage> children = new ArrayList<>(commands.length);for (String cmdString : commands) {children.add(new FullBulkStringRedisMessage(ByteBufUtil.writeUtf8(ctx.alloc(), cmdString)));}RedisMessage request = new ArrayRedisMessage(children);ctx.write(request, promise);}// 接收 redis 响应数据@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {RedisMessage redisMessage = (RedisMessage) msg;// 打印响应消息printAggregatedRedisResponse(redisMessage);// 是否资源ReferenceCountUtil.release(redisMessage);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {System.err.print("exceptionCaught: ");cause.printStackTrace(System.err);ctx.close();}private static void printAggregatedRedisResponse(RedisMessage msg) {if (msg instanceof SimpleStringRedisMessage) {System.out.println(((SimpleStringRedisMessage) msg).content());} else if (msg instanceof ErrorRedisMessage) {System.out.println(((ErrorRedisMessage) msg).content());} else if (msg instanceof IntegerRedisMessage) {System.out.println(((IntegerRedisMessage) msg).value());} else if (msg instanceof FullBulkStringRedisMessage) {System.out.println(getString((FullBulkStringRedisMessage) msg));} else if (msg instanceof ArrayRedisMessage) {for (RedisMessage child : ((ArrayRedisMessage) msg).children()) {printAggregatedRedisResponse(child);}} else {throw new CodecException("unknown message type: " + msg);}}public static int serialNum = 0;private static String getString(FullBulkStringRedisMessage msg) {if (msg.isNull()) {return "(null)";}return ++serialNum + ") " +msg.content().toString(CharsetUtil.UTF_8);}}

运行测试:
在这里插入图片描述

写在后面

参考文章列表

redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。 。

netty之导入源码到idea 。


http://www.ppmy.cn/news/1543959.html

相关文章

【线下培训】龙信受邀参加开封市公安局举办的电子数据取证培训班

文章关键词&#xff1a;电子数据取证、手机取证、云取证、国产化取证 为了提升开封市公安机关在互联网电子数据取证分析方面的专业能力&#xff0c;龙信为开封市公安机关量身打造了一场高质量的电子数据取证分析技能培训课程。 本次培训课程不仅涵盖了电子数据取证的基础理论、…

操作系统-第五章-(5.1)外部存储器管理

这个是与第四章顺接着学习比较好&#xff0c;故第五章先学这个。 第四章文件管理的题目经常和磁盘一起综合考察。 1.磁盘的结构 简单看看即可&#xff0c;不是重点&#xff0c;计组外部存储器也是学过。 1.1磁盘、磁道、扇区 磁盘的盘面被划分成一个个磁道。 这些磁性物质…

华为机试HJ18 识别有效的IP地址和掩码并进行分类统计

首先看一下题 描述 请解析IP地址和对应的掩码&#xff0c;进行分类识别。要求按照A/B/C/D/E类地址归类&#xff0c;不合法的地址和掩码单独归类。 所有的IP地址划分为 A,B,C,D,E五类 A类地址从1.0.0.0到126.255.255.255; B类地址从128.0.0.0到191.255.255.255; C类地址从192.0.…

windows 驱动实例分析系列: NDIS 6.0的Filter 驱动改造(二)

缓冲区池 Filter驱动要发送数据&#xff0c;除了实现这两个回调之外&#xff0c;还需要分配一个NET_BUFFER_LIST池&#xff0c;用于从池中分配NET_BUFFER_LIST结构&#xff0c;注意内核代码必须仅从Pool中分配NET_BUFFER_LIST以及NET_BUFFER等缓冲区结构。 分配 一般会使用下…

Spring Boot实现的中小企业设备信息管理系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

千帆AppBuilder:让AI智能对话轻松融入您的网站,提升用户体验新高度!

​ 大模型场景实战培训&#xff0c;提示词效果调优&#xff0c;大模型应用定制开发&#xff0c;点击咨询 嘿&#xff0c;小伙伴们&#xff01;你们是不是也在寻找一种能够快速将AI智能对话融入业务网站的方法&#xff0c;让用户体验更上一层楼呢&#xff1f;如果是的话&#xf…

如何使用 NetBak PC Agent 备份和恢复 Windows 计算机或服务器?

创作立场:原创不易&#xff0c;拒绝搬运~ hello大家好&#xff0c;我是你们的老伙伴&#xff0c;稳重的大王~ 本期给大家介绍一款QNAP 发布的好用的备份软件&#xff0c;给大家分享一下使用经验&#xff0c;有需要的给个爱心鼓励一下&#xff0c;谢谢~ 开始正文 ----------…

加强版 第五节图像处理与视频分析

基本概念 图像轮廓 主要针对二值图像&#xff0c;轮廓是一系列点 vector<vector<Point>xxx用于存储多个点 vector<Vec4i>xxx包含四个整数&#xff0c;分别代表下一个轮廓的索引&#xff0c;上一个轮廓的索引&#xff0c;一个子轮廓的索引和父轮廓的索引 相…