JedisDataException: ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in thi

阅读更多

最近做redis的发布/订阅,出现如题所示的困扰很久,网上也很少有相关的解答。今天终于解决了,特此记录一下过程。

 

首先是发布类:

 

public class Publisher {
	public void publish(final Jedis jedis) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("开始发布讯息咯:");
//				String channel = jedis.get("channel");
//				String message = jedis.get("300C00");
//				System.out.println("[取到]:" + channel + "," + message);
				try {
				     Thread.currentThread().sleep(2000);
				    } catch (InterruptedException e) {
				     e.printStackTrace();
				}
//jedis.publish(String channel, String message)
				jedis.publish("D00C003", "oklalllla");
                                jedis.publish("hello", "oklalllla");
			}
		}).start();;
	}
}

 

 

订阅类:

 

public class Subscriber1 {
	public void sub(final Jedis jedis, final MyListener1 listener) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("订阅到讯息……");
//subscribe(JedisPubSub, String[] channels)或psubscribe(JedisPubSub, String[] patterns)
				jedis.subscribe(listener, new String[]{"300C00", "news.share"});
			}  
		}).start();
	}
}

 

订阅类依赖的监听类,该监听类表明一旦接收到发布消息后的处理。需继承JeidsPubSub基类:

public class MyListener1 extends JedisPubSub {
	
	//取得订阅后消息的处理
	@Override
	public void onMessage(String channel, String message) {
		System.out.print("onMessage:取得订阅后消息的处理	");
		System.out.println(channel + "=" + message);  
	}
	
	//取得按表达式的方式订阅的消息后的处理
	@Override
	public void onPMessage(String pattern, String channel, String message) {
		System.out.print("onPMessage:取得按表达式的方式订阅的消息后的处理    ");
		System.out.println(pattern + "=" + channel + "=" + message);
	}
	
	//初始化按表达式的方式订阅时候的处理
	@Override
	public void onPSubscribe(String pattern, int subscribedChannels) {
		System.out.print("onPSubscribe:初始化按表达式的方式订阅时候的处理   ");
		System.out.println(pattern + "=" + subscribedChannels);  
	}
	
	//取消化按表达式的方式订阅时候的处理
	@Override
	public void onPUnsubscribe(String pattern, int subscribedChannels) {
		System.out.print("onPUnsubscribe:取消化按表达式的方式订阅时候的处理   ");
		System.out.println(pattern + "=" + subscribedChannels); 
	}
	
	//初始化订阅时候的处理
	@Override
	public void onSubscribe(String channel, int subscribedChannels) {
		System.out.print("onSubscribe:初始化订阅时候的处理   ");
		System.out.println(channel + "=" + subscribedChannels); 
	}
	
	//取消订阅时候的处理
	@Override
	public void onUnsubscribe(String channel, int subscribedChannels) {
		System.out.print("onUnsubscribe:取消订阅时候的处理   ");
		System.out.println(channel + "=" + subscribedChannels);
	}

}

 

接下来就是测试了:

//获得Jedis
		Jedis jedis1 = JedisFactory.getJedis();
		Jedis jedis2 = JedisFactory.getJedis();
		MyListener2 listener = new MyListener2();
		 //发布  channel message
		 Publisher2 publisher = new Publisher2();
		 publisher.publish(jedis2);
		 //订阅
		 Subscriber1 scribe = new Subscriber1();
		 scribe.sub(jedis1, listener);

 

JedisFactory:

public class JedisFactory {
	private static Jedis jedis;
	static {
		 // 池基本配置 
        JedisPoolConfig config = new JedisPoolConfig(); 
        config.setMaxIdle(5); 
        config.setMaxWaitMillis(1000l); 
        config.setTestOnBorrow(false); 
        // slave链接 
//        List shards = new ArrayList(); 
//        shards.add(new JedisShardInfo("127.0.0.1", 6379, "master")); 

        // 构造池 
       JedisPool pool = new JedisPool(config,"127.0.0.1",6379, 100000); //容忍的超时时间
       jedis = pool.getResource();
	}
	
	public static Jedis getJedis() {
		return jedis;
	}
}

 

运行之后,出现如题所示的异常:

redis.clients.jedis.exceptions.JedisDataException: ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context

at redis.clients.jedis.Protocol.processError(Protocol.java:104)

at redis.clients.jedis.Protocol.process(Protocol.java:122)

at redis.clients.jedis.Protocol.read(Protocol.java:191)

at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:175)

at redis.clients.jedis.Jedis.set(Jedis.java:54)

 

该异常的表面意思是说Jedis的发布与订阅数据出现了异常,并且说只有指定的情况在上下文中。不太好理解,查阅底层API发现发布、订阅的语法都没有错,被困扰了N久!!!

后发现问题出在自己写的JeidsFactory。我写JedisFactory的本意是封装获取Jedis对象的代码,但不知不觉地,getJedis()方法是单例的,即尽管发布和订阅分别调用了该方法,但获取到的永远是同一个Jedis对象。所以报错……(为什么发布、订阅不能使用同一个Jedis,我也不知道原因。。)

遂改成:

 

static JedisPool pool;
	static {
		 // 池基本配置 
        JedisPoolConfig config = new JedisPoolConfig(); 
        config.setMaxIdle(5); 
        config.setMaxWaitMillis(1000l);  
        config.setTestOnBorrow(false); 
        // slave链接 
//        List shards = new ArrayList(); 
//        shards.add(new JedisShardInfo("127.0.0.1", 6379, "master")); 

        // 构造池 
       pool = new JedisPool(config,"127.0.0.1",6379, 100000); //容忍的超时时间
      
	}

 

发布和订阅再从pool中取,即可:

 

Jedis jedis1 = pool.getResource();
Jedis jedis2 = pool.getResource();

 

执行顺序:

发布讯息->订阅讯息->on(P)Subscribe->on(P)Message(当通过psubscribe的方式订阅时,就走onPSubscribe、onPMessage)

 

所以,当你发现自己出现这个问题,而发布订阅的语法又没错时,可以看看是不是问题出在这儿!——发布、订阅要使用不同的redis客户端哈!

 

你可能感兴趣的:(jedis,发布,订阅)