Redis实战总结(二)
来源:互联网 发布:网络视频教学平台 编辑:程序博客网 时间:2024/06/05 15:59
本篇博客重点介绍Redis的管道,发布/订阅机制。
Redis是一种基于Client-Server模型以及请求/响应协议的TCP服务。Client端发出请求,server端处理并返回结果到客户端。在这个过程中Client端是以阻塞形式等待服务端的响应。假设从Client发送命令到收到Server的处理结果需要1/16秒,这样带来的结果是Client每秒只能发送16条命令,及时Redis每秒可以处理几百个命令,严重影响了Redis的使用效率。因而针对上述问题Redis实现以下三种机制可以批量式的处理命令(通过减少Client和Server之间的网络通信次数来提升Redis在执行多个命令的性能)。
处理多参数的命令:比如MGET,MSET,HMGET,HMSET,RPUSH等命令可以接受多个参数,这样能够极大的提升性能,但是这些命令只能处理需要重复执行相同命令的操作。
Multi和Exec命令:将多个命令封装成一个事物,以事物的方式提交,然后等待所有回复出现。但是这种方式仍然会消耗资源,并且可能会导致其他重要的命令被延迟执行。正常会和Watch、Unwatch、Discard命令结合使用,确保自己正在使用的数据没有发生变化来避免数据出错。
管道技术:本篇博客重点介绍的。
Redis的管道机制
在Server端未响应时,Client端可以连续式的向Server端发送命令,并最终一次性读取Server端的所有响应,各个命令之间互不干扰。显然这种方式可以显著性地提高了 redis 服务的性能。我们把这种方式称为:非事务性流水线方式。
下面给出一个具体的测试实例:
/** * 设置给定键的值 * @param key * @param value */ public static void set(String key, String value) { Jedis jedis = jedisPool.getResource(); try { jedis.set(key, value); } finally { safeReturn(jedis); } } /** * 测试使用管道的效率 * @param key */ public static void batchTestPipeLine(String key) { Jedis jedis = jedisPool.getResource(); try { Pipeline pipeline = jedis.pipelined(); for (int i = 0; i < 10000; i++) { Response<Long> returns = pipeline.incr(key); } pipeline.syncAndReturnAll(); } finally { safeReturn(jedis); } } /** * 测试不使用管道的效率 * @param key */ public static void batchTestWithNonePipeLine(String key) { Jedis jedis = jedisPool.getResource(); try { for (int i = 0; i < 10000; i++) { jedis.incr(key); } } finally { safeReturn(jedis); } }
主程序测试:
import org.apache.log4j.Logger;import util.JedisUtil;public class Application2 { private static Logger logger = Logger.getLogger(Application.class); private static String PIPELINE_HAS = "PIPELINE_HAS"; private static String PIPELINE_NONE = "PIPELINE_NONE"; public static void main(String[] args) { logger.info("主程序开始运行:"); Long beginTime = System.currentTimeMillis(); JedisUtil.init(); JedisUtil.set(PIPELINE_HAS, "0"); JedisUtil.set(PIPELINE_NONE, "0"); JedisUtil.batchTestWithNonePipeLine(PIPELINE_NONE); Long endTime1 = System.currentTimeMillis(); JedisUtil.batchTestWithNonePipeLine(PIPELINE_HAS); Long endTime2 = System.currentTimeMillis(); logger.info("未使用管道技术,消耗时间:"+(endTime1-beginTime)); logger.info("使用管道技术,消耗时间:"+(endTime2-endTime1)); }}
程序运行结果:
通过测试发现,使用通道技术和不使用差距还是很明显的。
Redis发布和订阅机制
Redis的发布与订阅机制由两部分组成:发布者,订阅者。发布者(publisher)负责向频道发送二进制字符串消息;订阅者(subscriber)负责订阅频道。每当发布者将消息发送至给定频道时,频道的所有订阅者都会收到消息。
Redis的发布和订阅是一种消息通信模式,可以解除消息发布者和消息订阅者之间的耦,类似于设计模式中观察者模式。
Redis中发布和订阅命令
JAVA中发布和订阅的实现
重写类JedisPubSub中onMessage方法
package listener;import redis.clients.jedis.JedisPubSub;import java.util.logging.Logger;/** * 重写Redis的PUB和SUB方法 * @author guweiyu */public class RedisPubSubListener extends JedisPubSub { Logger logger = Logger.getLogger(RedisPubSubListener.class.getName()); @Override public void onMessage(String channel, String message) { logger.info("channel:" + channel + "receives message :" + message); this.unsubscribe(channel); } public void unsubscribe(String channel) { logger.info("unsubscribe channel:"+channel); super.unsubscribe(channel); }}
/** * 订阅频道 * @param jedisPubSub * @param channel */ public static void subscribe(JedisPubSub jedisPubSub, String channel) { Jedis jedis = jedisPool.getResource(); try { jedis.subscribe(jedisPubSub, channel); } finally { safeReturn(jedis); } } /** * 向频道发布信息 * @param channel * @param msg */ public static void publish(String channel, String msg) { Jedis jedis = jedisPool.getResource(); try { jedis.publish(channel, msg); } finally { safeReturn(jedis); } }
编写PUB端测试案例
import org.apache.log4j.Logger;import util.JedisUtil;public class ApplicationPub { private static Logger logger = Logger.getLogger(ApplicationPub.class); public static void main(String[] args) throws Exception{ logger.info("Main Application is starting"); JedisUtil.init(); JedisUtil.publish("channel_test", "hello, world"); Thread.sleep(5000); JedisUtil.publish("channel_test", "hello, china"); }}
编写SUB端测试案例
import listener.RedisPubSubListener;import org.apache.log4j.Logger;import util.JedisUtil;public class ApplicationSub { private static Logger logger = Logger.getLogger(ApplicationSub.class); public static void main(String[] args) { logger.info("Main Application is starting"); JedisUtil.init(); // 订阅频道 RedisPubSubListener listener = new RedisPubSubListener(); // Redis的subscribe是阻塞式方法,在取消订阅该频道前,会一直阻塞在这 JedisUtil.subscribe(listener, "channel_test"); logger.info("订阅结束"); }}
先启动SUB端,再启动PUB端,运行结果如下:
在实际中直接使用Redis的PUB和SUB机制处理消息相对较少,由于和数据传输的可靠性有关。如果客户端在执行订阅操作的过程中断线,那么客户端将会丢失在断线期间发送的所有消息。
- Redis实战总结(二)
- Redis入门实战(二)
- Redis实战总结(一)
- Redis实战总结(三)
- C# Redis实战(二)
- C# Redis实战(二)
- Redis实战之征服 Redis + Jedis + Spring (二)
- Redis实战之征服 Redis + Jedis + Spring (二)
- Redis实战之征服 Redis + Jedis + Spring (二)
- Redis实战之Windows Redis 集群搭建(二)
- redis学习总结(二)
- Redis总结二(事务)
- Redis集群使用总结(二)
- Redis集群使用总结(二)
- Redis总结笔记(二):C#连接Redis简单例子
- redis 学习记录总结(Node.js实战-读书笔记)
- 基于redis排行榜的实战总结
- redis 实战系列二:用python操作redis集群
- 如何实施代码重构
- linux内核编译错误 No rule to make target menuconfig解决方法
- 分布式系统研发心得
- bzoj 1923: [Sdoi2010]外星千足虫
- 登录错误次数限制springmvc+spring+mybatis五次锁定24小时解锁
- Redis实战总结(二)
- 大并发压测下,redis连接异常Read timed out排查优化
- 使用JAVA爬取网页图片
- [设计模式] 原型模式
- JUnit initializationError(org.junit.runner.manipulation.Filter)错误
- 编译原理--笔记
- 合并果子
- 单例模式
- MongoDB大中华区首席架构师唐建法:关系型数据库到MongoDB的战略迁移