RBlockingQueueReactive added. #210
parent
8eed10c39d
commit
f376c74b2b
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Copyright 2014 Nikita Koksharov, Nickolay Borbit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.client.protocol.RedisCommand;
|
||||
import org.redisson.client.protocol.RedisCommands;
|
||||
import org.redisson.command.CommandReactiveExecutor;
|
||||
import org.redisson.connection.decoder.ListDrainToDecoder;
|
||||
import org.redisson.core.RBlockingQueueReactive;
|
||||
|
||||
/**
|
||||
* Offers blocking queue facilities through an intermediary
|
||||
* {@link LinkedBlockingQueue} where items are added as soon as
|
||||
* <code>blpop</code> returns. All {@link BlockingQueue} methods are actually
|
||||
* delegated to this intermediary queue.
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
*/
|
||||
public class RedissonBlockingQueueReactive<V> extends RedissonQueueReactive<V> implements RBlockingQueueReactive<V> {
|
||||
|
||||
protected RedissonBlockingQueueReactive(CommandReactiveExecutor commandExecutor, String name) {
|
||||
super(commandExecutor, name);
|
||||
}
|
||||
|
||||
protected RedissonBlockingQueueReactive(Codec codec, CommandReactiveExecutor commandExecutor, String name) {
|
||||
super(codec, commandExecutor, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<Long> put(V e) {
|
||||
return offer(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<V> take() {
|
||||
return commandExecutor.writeObservable(getName(), codec, RedisCommands.BLPOP_VALUE, getName(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<V> poll(long timeout, TimeUnit unit) {
|
||||
return commandExecutor.writeObservable(getName(), codec, RedisCommands.BLPOP_VALUE, getName(), unit.toSeconds(timeout));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.redisson.core.RBlockingQueueAsync#pollFromAnyAsync(long, java.util.concurrent.TimeUnit, java.lang.String[])
|
||||
*/
|
||||
@Override
|
||||
public Publisher<V> pollFromAny(long timeout, TimeUnit unit, String ... queueNames) {
|
||||
List<Object> params = new ArrayList<Object>(queueNames.length + 1);
|
||||
params.add(getName());
|
||||
for (Object name : queueNames) {
|
||||
params.add(name);
|
||||
}
|
||||
params.add(unit.toSeconds(timeout));
|
||||
return commandExecutor.writeObservable(getName(), codec, RedisCommands.BLPOP_VALUE, params.toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<V> pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) {
|
||||
return commandExecutor.writeObservable(getName(), codec, RedisCommands.BRPOPLPUSH, getName(), queueName, unit.toSeconds(timeout));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<Integer> drainTo(Collection<? super V> c) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
return commandExecutor.evalWriteObservable(getName(), codec, new RedisCommand<Object>("EVAL", new ListDrainToDecoder(c)),
|
||||
"local vals = redis.call('lrange', KEYS[1], 0, -1); " +
|
||||
"redis.call('ltrim', KEYS[1], -1, 0); " +
|
||||
"return vals", Collections.<Object>singletonList(getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Publisher<Integer> drainTo(Collection<? super V> c, int maxElements) {
|
||||
if (c == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
return commandExecutor.evalWriteObservable(getName(), codec, new RedisCommand<Object>("EVAL", new ListDrainToDecoder(c)),
|
||||
"local elemNum = math.min(ARGV[1], redis.call('llen', KEYS[1])) - 1;" +
|
||||
"local vals = redis.call('lrange', KEYS[1], 0, elemNum); " +
|
||||
"redis.call('ltrim', KEYS[1], elemNum + 1, -1); " +
|
||||
"return vals",
|
||||
Collections.<Object>singletonList(getName()), maxElements);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2014 Nikita Koksharov, Nickolay Borbit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.redisson.core;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
/**
|
||||
* {@link BlockingQueue} backed by Redis
|
||||
*
|
||||
* @author Nikita Koksharov
|
||||
* @param <V> the type of elements held in this collection
|
||||
*/
|
||||
public interface RBlockingQueueReactive<V> extends RQueueReactive<V> {
|
||||
|
||||
/**
|
||||
* Retrieves and removes first available head of <b>any</b> queue in mode, waiting up to the
|
||||
* specified wait time if necessary for an element to become available
|
||||
* in any of defined queues <b>including</b> queue own.
|
||||
*
|
||||
* @param timeout how long to wait before giving up, in units of
|
||||
* {@code unit}
|
||||
* @param unit a {@code TimeUnit} determining how to interpret the
|
||||
* {@code timeout} parameter
|
||||
* @return Publisher object with the head of this queue, or {@code null} if the
|
||||
* specified waiting time elapses before an element is available
|
||||
* @throws InterruptedException if interrupted while waiting
|
||||
*/
|
||||
Publisher<V> pollFromAny(long timeout, TimeUnit unit, String ... queueNames);
|
||||
|
||||
Publisher<Integer> drainTo(Collection<? super V> c, int maxElements);
|
||||
|
||||
Publisher<Integer> drainTo(Collection<? super V> c);
|
||||
|
||||
Publisher<V> pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit);
|
||||
|
||||
Publisher<V> poll(long timeout, TimeUnit unit);
|
||||
|
||||
Publisher<V> take();
|
||||
|
||||
Publisher<Long> put(V e);
|
||||
|
||||
}
|
@ -0,0 +1,211 @@
|
||||
package org.redisson;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.redisson.core.RBlockingQueueReactive;
|
||||
|
||||
public class RedissonBlockingQueueReactiveTest extends BaseReactiveTest {
|
||||
|
||||
@Test
|
||||
public void testPollFromAny() throws InterruptedException {
|
||||
final RBlockingQueueReactive<Integer> queue1 = redisson.getBlockingQueue("queue:pollany");
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
RBlockingQueueReactive<Integer> queue2 = redisson.getBlockingQueue("queue:pollany1");
|
||||
RBlockingQueueReactive<Integer> queue3 = redisson.getBlockingQueue("queue:pollany2");
|
||||
sync(queue3.put(2));
|
||||
sync(queue1.put(1));
|
||||
sync(queue2.put(3));
|
||||
}
|
||||
}, 3, TimeUnit.SECONDS);
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
int l = sync(queue1.pollFromAny(4, TimeUnit.SECONDS, "queue:pollany1", "queue:pollany2"));
|
||||
|
||||
Assert.assertEquals(2, l);
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 2000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTake() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getBlockingQueue("queue:take");
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getBlockingQueue("queue:take");
|
||||
sync(queue.put(3));
|
||||
}
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
int l = sync(queue1.take());
|
||||
|
||||
Assert.assertEquals(3, l);
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 9000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPoll() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getBlockingQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
Assert.assertEquals((Integer)1, sync(queue1.poll(2, TimeUnit.SECONDS)));
|
||||
|
||||
long s = System.currentTimeMillis();
|
||||
Assert.assertNull(sync(queue1.poll(5, TimeUnit.SECONDS)));
|
||||
Assert.assertTrue(System.currentTimeMillis() - s > 5000);
|
||||
}
|
||||
@Test
|
||||
public void testAwait() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getBlockingQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
|
||||
Assert.assertEquals((Integer)1, sync(queue1.poll(10, TimeUnit.SECONDS)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPollLastAndOfferFirstTo() throws InterruptedException {
|
||||
RBlockingQueueReactive<Integer> queue1 = redisson.getBlockingQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
sync(queue1.put(2));
|
||||
sync(queue1.put(3));
|
||||
|
||||
RBlockingQueueReactive<Integer> queue2 = redisson.getBlockingQueue("queue2");
|
||||
sync(queue2.put(4));
|
||||
sync(queue2.put(5));
|
||||
sync(queue2.put(6));
|
||||
|
||||
sync(queue1.pollLastAndOfferFirstTo(queue2.getName(), 10, TimeUnit.SECONDS));
|
||||
MatcherAssert.assertThat(sync(queue2), Matchers.contains(3, 4, 5, 6));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOffer() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getBlockingQueue("blocking:queue");
|
||||
sync(queue.add(1));
|
||||
sync(queue.offer(2));
|
||||
sync(queue.add(3));
|
||||
sync(queue.offer(4));
|
||||
|
||||
//MatcherAssert.assertThat(queue, Matchers.contains(1, 2, 3, 4));
|
||||
Assert.assertEquals((Integer) 1, sync(queue.poll()));
|
||||
MatcherAssert.assertThat(sync(queue), Matchers.contains(2, 3, 4));
|
||||
Assert.assertEquals((Integer) 2, sync(queue.peek()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getBlockingQueue("blocking:queue");
|
||||
sync(queue.add(1));
|
||||
sync(queue.add(2));
|
||||
sync(queue.add(3));
|
||||
sync(queue.add(4));
|
||||
|
||||
sync(queue.poll());
|
||||
sync(queue.poll());
|
||||
|
||||
MatcherAssert.assertThat(sync(queue), Matchers.contains(3, 4));
|
||||
sync(queue.poll());
|
||||
sync(queue.poll());
|
||||
|
||||
Assert.assertEquals(0, sync(queue.size()).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveEmpty() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getBlockingQueue("blocking:queue");
|
||||
Assert.assertNull(sync(queue.poll()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainTo() {
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getBlockingQueue("queue");
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
sync(queue.offer(i));
|
||||
}
|
||||
Assert.assertEquals(100, sync(queue.size()).intValue());
|
||||
Set<Integer> batch = new HashSet<Integer>();
|
||||
int count = sync(queue.drainTo(batch, 10));
|
||||
Assert.assertEquals(10, count);
|
||||
Assert.assertEquals(10, batch.size());
|
||||
Assert.assertEquals(90, sync(queue.size()).intValue());
|
||||
sync(queue.drainTo(batch, 10));
|
||||
sync(queue.drainTo(batch, 20));
|
||||
sync(queue.drainTo(batch, 60));
|
||||
Assert.assertEquals(0, sync(queue.size()).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlockingQueue() {
|
||||
|
||||
RBlockingQueueReactive<Integer> queue = redisson.getBlockingQueue("test_:blocking:queue:");
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||
|
||||
final AtomicInteger counter = new AtomicInteger();
|
||||
int total = 100;
|
||||
for (int i = 0; i < total; i++) {
|
||||
// runnable won't be executed in any particular order, and hence, int value as well.
|
||||
executor.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
redisson.getQueue("test_:blocking:queue:").add(counter.incrementAndGet());
|
||||
}
|
||||
});
|
||||
}
|
||||
int count = 0;
|
||||
while (count < total) {
|
||||
int item = sync(queue.take());
|
||||
assertTrue(item > 0 && item <= total);
|
||||
count++;
|
||||
}
|
||||
|
||||
assertThat(counter.get(), equalTo(total));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainToCollection() throws Exception {
|
||||
RBlockingQueueReactive<Object> queue1 = redisson.getBlockingQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
sync(queue1.put(2L));
|
||||
sync(queue1.put("e"));
|
||||
|
||||
ArrayList<Object> dst = new ArrayList<Object>();
|
||||
sync(queue1.drainTo(dst));
|
||||
MatcherAssert.assertThat(dst, Matchers.<Object>contains(1, 2L, "e"));
|
||||
Assert.assertEquals(0, sync(queue1.size()).intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDrainToCollectionLimited() throws Exception {
|
||||
RBlockingQueueReactive<Object> queue1 = redisson.getBlockingQueue("queue1");
|
||||
sync(queue1.put(1));
|
||||
sync(queue1.put(2L));
|
||||
sync(queue1.put("e"));
|
||||
|
||||
ArrayList<Object> dst = new ArrayList<Object>();
|
||||
sync(queue1.drainTo(dst, 2));
|
||||
MatcherAssert.assertThat(dst, Matchers.<Object>contains(1, 2L));
|
||||
Assert.assertEquals(1, sync(queue1.size()).intValue());
|
||||
|
||||
dst.clear();
|
||||
sync(queue1.drainTo(dst, 2));
|
||||
MatcherAssert.assertThat(dst, Matchers.<Object>contains("e"));
|
||||
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue