What I expect is reactor will create threads for each emitted element, meaning elements 1, 2, 3, 4, 5 from the source should be handled concurrently. But it's not true from my demo code output, but I don't know why. Could someone take a look and explain to me for two things:
Why does reactor in my demo code handles elements in synchronize fashion?
How to make reactor handles each element concurrent?
Though the reactor chain in my below demo code is async to the main thread, each element from source flux emits in a synchronized way.
Here is my demo code
System.out.println("main thread start ...");
Flux.range(1,5)
.flatMap(num->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Mono.just(num);
}).flatMap(num-> Mono.just(num*10) )
.subscribeOn(Schedulers.boundedElastic())
.subscribe(res-> System.out.println("Thread name:" + Thread.currentThread().getName()+" value:" + res));
System.out.println("main thread sleep a little");
Thread.sleep(4000);
System.out.println("main thread end ...");
Here is the output
Output:
main thread start ...
main thread sleep a little
0. element: 0
1. element: 1
main thread end ...
2. element: 2
Your code is not really implemented in a reactive way especially for wrapping non-reactive code. Consider slightly reworked example
#Test
void concurrent() {
Flux<Integer> stream = Flux.range(1, 50)
.flatMap(this::process)
.flatMap(num -> Mono.just(num * 10));
StepVerifier.create(stream)
.expectNextCount(50)
.verifyComplete();
}
private Mono<Integer> process(int num) {
return Mono.fromCallable(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return num;
})
.log()
.subscribeOn(Schedulers.boundedElastic());
}
If you run it, you would see that execution is happening on multiple threads with concurrency of Queues.SMALL_BUFFER_SIZE
22:54:26.066 [boundedElastic-50] INFO [r.M.L.50] - | request(32)
22:54:27.038 [boundedElastic-6] INFO [r.M.L.6] - | onNext(6)
22:54:27.038 [boundedElastic-11] INFO [r.M.L.11] - | onNext(11)
22:54:27.038 [boundedElastic-2] INFO [r.M.L.2] - | onNext(2)
22:54:27.038 [boundedElastic-9] INFO [r.M.L.9] - | onNext(9)
22:54:27.038 [boundedElastic-3] INFO [r.M.L.3] - | onNext(3)
22:54:27.040 [boundedElastic-13] INFO [r.M.L.13] - | onNext(13)
22:54:27.040 [boundedElastic-7] INFO [r.M.L.7] - | onNext(7)
22:54:27.040 [boundedElastic-20] INFO [r.M.L.20] - | onNext(20)
22:54:27.041 [boundedElastic-6] INFO [r.M.L.6] - | onComplete()
22:54:27.041 [boundedElastic-13] INFO [r.M.L.13] - | onComplete()
22:54:27.043 [boundedElastic-2] INFO [r.M.L.2] - | onComplete()
22:54:27.043 [boundedElastic-3] INFO [r.M.L.3] - | onComplete()
22:54:27.043 [boundedElastic-20] INFO [r.M.L.20] - | onComplete()
22:54:27.043 [boundedElastic-11] INFO [r.M.L.11] - | onComplete()
22:54:27.043 [boundedElastic-7] INFO [r.M.L.7] - | onComplete()
22:54:27.044 [boundedElastic-9] INFO [r.M.L.9] - | onComplete()
22:54:27.045 [boundedElastic-1] INFO [r.M.L.1] - | onNext(1)
22:54:27.045 [boundedElastic-5] INFO [r.M.L.5] - | onNext(5)
22:54:27.045 [boundedElastic-15] INFO [r.M.L.15] - | onNext(15)
22:54:27.045 [boundedElastic-1] INFO [r.M.L.1] - | onComplete()
22:54:27.045 [boundedElastic-15] INFO [r.M.L.15] - | onComplete()
Also, you could check Flight of the Flux 3 - Hopping Threads and Schedulers for more detailed explanation and more examples.
Related
I have an application that creates routes to connect to a REST endpoint and process the responses for several vendors. Each route is triggered with a quartz2 timer. Recently when the timer fires it creates multiple exchanges instead of just one and I cannot determine what is causing it.
The method that creates the routes is here:
public String generateRoute(String vendorId) {
routeBuilders.add(new RouteBuilder() {
#Override
public void configure() throws Exception {
System.out.println("Building REST input route for vendor " + vendorId);
String vendorCron = vendorProps.getProperty(vendorId + ".rest.cron");
String vendorEndpoint = vendorProps.getProperty(vendorId + ".rest.endpoint");
String vendorAuth = vendorProps.getProperty(vendorId + ".rest.auth");
int vendorTimer = Integer.valueOf(vendorId) * 10000;
GsonDataFormat format = new GsonDataFormat(RestResponse.class);
from("quartz2://timer" + vendorId + "?cron=" + vendorCron)
.routeId("Rte-vendor" + vendorId)
.streamCaching()
.log("Starting route " + vendorId)
.setHeader("Authorization",constant(vendorAuth))
.to("rest:get:" + vendorEndpoint)
.to("direct:processRestResponse")
.end();
};
});
return "direct:myRoute." + vendorId;
and a sample 'vendorCron' string is
"*+5+*+*+*+?&trigger.timeZone=America/New_York".
When the quartz route fires I see this type of output in the log
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
15:39| INFO | CamelLogger.java 159 | Starting route 4
When I should ( and used to) only see one of these.
Any ideas what would cause this?
Thanks!
This is because of your vendorCron
If Cron trigger is every 5secs then you see this log in every 5 secs..
If Cron trigger is every 5mins/hours you see these login in 5 mins/hours.
I was staring so hard I missed the obvious. I need a 0 in the seconds place of the cron expression.
Thank you for the time.
I'm using Stripe.lock() from com.google.guava.guava-28.0-jre.jar
public class NamedLock {
private static final Logger LOGGER = Logger.getLogger(NamedLock.class);
private Striped<Lock> locks;
public NamedLock() {
locks = Striped.lock(1023);
}
/**
* Test method
*
* #param args
* #throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
NamedLock namedLock = new NamedLock();
Runnable runnable = () -> {
namedLock.lock("aAp1h0000004oEcCAI");
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
namedLock.lock("aAT1h0000001OzCGAU");
};
new Thread(runnable).start();
TimeUnit.SECONDS.sleep(2);
namedLock.lock("aAp1h0000004oFRCAY");
namedLock.lock("aAp1h0000004oKYCAY");
}
/**
* Acquires the lock for the Names.
*
* #param names
* the Names
*/
public void lock(String... names) {
String key = Arrays.toString(names);
LOGGER.debug("Locking with Key : " + key);
Lock lock = locks.get(key);
LOGGER.debug("Acquiring Lock : " + lock);
lock.lock();
LOGGER.debug("Lock acquired : " + lock);
}
/**
* Releases the lock acquired for the Names.
*
* #param names
* the Names
*/
public void unlock(String... names) {
String key = Arrays.toString(names);
LOGGER.debug("Unlocking with Key : " + key);
Lock lock = locks.get(key);
LOGGER.debug("Releasing Lock : " + lock);
lock.unlock();
LOGGER.debug("Lock released : " + lock);
}
}
Result for the program :
| DEBUG | 2019-08-13 14:58:33.973 | Thread-0 | NamedLock:61 | Locking with Key : [aAp1h0000004oEcCAI]
| DEBUG | 2019-08-13 14:58:33.975 | Thread-0 | NamedLock:63 | Acquiring Lock : com.google.common.util.concurrent.Striped$PaddedLock#ac92ce3[Unlocked]
| DEBUG | 2019-08-13 14:58:33.975 | Thread-0 | NamedLock:65 | Lock acquired : com.google.common.util.concurrent.Striped$PaddedLock#ac92ce3[Locked by thread Thread-0]
| DEBUG | 2019-08-13 14:58:35.973 | main | NamedLock:61 | Locking with Key : [aAp1h0000004oFRCAY]
| DEBUG | 2019-08-13 14:58:35.974 | main | NamedLock:63 | Acquiring Lock : com.google.common.util.concurrent.Striped$PaddedLock#574caa3f[Unlocked]
| DEBUG | 2019-08-13 14:58:35.980 | main | NamedLock:65 | Lock acquired : com.google.common.util.concurrent.Striped$PaddedLock#574caa3f[Locked by thread main]
| DEBUG | 2019-08-13 14:58:35.981 | main | NamedLock:61 | Locking with Key : [aAp1h0000004oKYCAY]
| DEBUG | 2019-08-13 14:58:35.982 | main | NamedLock:63 | Acquiring Lock : com.google.common.util.concurrent.Striped$PaddedLock#ac92ce3[Locked by thread Thread-0]
| DEBUG | 2019-08-13 14:58:38.976 | Thread-0 | NamedLock:61 | Locking with Key : [aAT1h0000001OzCGAU]
| DEBUG | 2019-08-13 14:58:38.982 | Thread-0 | NamedLock:63 | Acquiring Lock : com.google.common.util.concurrent.Striped$PaddedLock#574caa3f[Locked by thread main]
The program never terminates and goes into DeadLock state, waiting for each others lock though all the keys are different.
From the JavaDoc:
Note that if key1 is not equal to key2, it is not guaranteed that
striped.get(key1) != striped.get(key2)
So presumably more than one of your keys exists in the same stripe. The hash codes are relatively close together for some of them so it seems reasonable:
System.out.println("[aAp1h0000004oEcCAI]".hashCode()); // -1286359401
System.out.println("[aAp1h0000004oFRCAY]".hashCode()); // -1273429611
System.out.println("[aAp1h0000004oKYCAY]".hashCode()); // -1123819209
System.out.println("[aAT1h0000001OzCGAU]".hashCode()); // 1694776185
It sounds like Striped is not applicable to your use-case and that you need one lock per object.
i'm developing the Connect4 game in Java and i'm having problem with the Logger. I don't know why prints in different place ad between other kind of prints.
public void setPlacement(Move lastMove){
Logger.getGlobal().info("Player" + lastMove.getPlayerIndex() + " placed a checker in position : " + lastMove.toString());
display();
}
The method display() just prints the grid of the game. Here's the output of the above method :
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| - | - | - | - | - | - | - |
| - | - | - | - | - | - | - |
| - ago 01, 2019 6:10:24 PM it.unicam.cs.pa.connectFour.GameViewsetPlacement
INFO: Player1 placed a checker in position : column 4, row 5
| - | - | - | - | - | - |
| - | - | - | - | - | - | - |
| - | O | O | O | - | - | - |
| O | X | X | X | - | - | - |
*****************************
Can someone explain me why the logger acts like this?
The response to this question concerning System::out and System::err may help you. Specifically, your console is displaying two streams of output at the same time, with no ordering guarantees between messages sent to different streams.
One way to enable Loggers to write to a standard output stream is to create your own StreamHandler and configure your Logger to send input to the handler instead. You may also have to disable parent handlers to avoid duplicate output. You may also want to ensure that output is proactively sent to the desired output stream, as so:
Handler h = new StreamHandler(System.out, formatter)
{
public void publish(LogRecord record)
{
if(record == null)
return;
super.publish(record);
super.flush();
}
};
I used the NSQ as the mq in my project,java producer produce the message to NSQ and go consumer consume it.But the strange things is that the consumer always get the message after few seconds.There is just a few messages,I really don't know how to explain why it's happened.
Here is the test result,please pay attention to the time.They both consume the same topic.You can see in the second time,Go is slower than java for 7s.
Java result:
INFO | jvm 1 | 2018/07/11 17:22:01 | Msg
receive:{"did":"XSQ000200000005","msg":{"id":"5560","type":1,"content":"ZBINh6CBsLw7k2xjr1wslSjY+5QavEgYU6AzzLZn0lOgON9ZYHnNP4UJVUGB+/SpsxZQnrWR9PlULzpSP/p9l9t8wiAwj8qhznRaT8jeyx1/EUrDE0oXJB8GxWaLJUICCbC92j4BMA2HU8vgcfDOp9nSy1KFafi9zgFiCf9Igqo="}}
INFO | jvm 1 | 2018/07/11 17:22:11 | Msg
receive:{"did":"XSQ000200000005","msg":{"id":"5560","type":1,"content":"ZBINh6CBsLw7k2xjr1wslSjY+5QavEgYU6AzzLZn0lOgON9ZYHnNP4UJVUGB+/SpsxZQnrWR9PlULzpSP/p9l9t8wiAwj8qhznRaT8jeyx1/EUrDE0oXJB8GxWaLJUICCbC92j4BMA2HU8vgcfDOp9nSy1KFafi9zgFiCf9Igqo="}}
INFO | jvm 1 | 2018/07/11 17:23:21 | Msg
receive:{"did":"XSQ000200000005","msg":{"id":"5560","type":1,"content":"ZBINh6CBsLw7k2xjr1wslSjY+5QavEgYU6AzzLZn0lOgON9ZYHnNP4UJVUGB+/SpsxZQnrWR9PlULzpSP/p9l9t8wiAwj8qhznRaT8jeyx1/EUrDE0oXJB8GxWaLJUICCbC92j4BMA2HU8vgcfDOp9nSy1KFafi9zgFiCf9Igqo="}}
INFO | jvm 1 | 2018/07/11 17:25:31 | Msg
receive:{"did":"XSQ000200000005","msg":{"id":"5560","type":1,"content":"ZBINh6CBsLw7k2xjr1wslSjY+5QavEgYU6AzzLZn0lOgON9ZYHnNP4UJVUGB+/SpsxZQnrWR9PlULzpSP/p9l9t8wiAwj8qhznRaT8jeyx1/EUrDE0oXJB8GxWaLJUICCbC92j4BMA2HU8vgcfDOp9nSy1KFafi9zgFiCf9Igqo="}}
Go result:
2018-07-11 17:22:03 broker.go DEBUG Ready to send msg 5560 with type 1 to XSQ000200000005
2018-07-11 17:22:28 broker.go DEBUG Ready to send msg 5560 with type 1 to XSQ000200000005
2018-07-11 17:23:21 broker.go DEBUG Ready to send msg 5560 with type 1 to XSQ000200000005
2018-07-11 17:25:38 broker.go DEBUG Ready to send msg 5560 with type 1 to XSQ000200000005
please ignore the other errors,just because of the business.
Here is my go consumer:
func (b *Broker) createConsumer(topic string, vendor int32) error {
config := nsq.NewConfig()
laddr := "127.0.0.1"
// so that the test can simulate binding consumer to specified address
config.LocalAddr, _ = net.ResolveTCPAddr("tcp", laddr+":0")
// so that the test can simulate reaching max requeues and a call to LogFailedMessage
config.DefaultRequeueDelay = 0
// so that the test wont timeout from backing off
config.MaxBackoffDuration = time.Millisecond * 50
c, err := nsq.NewConsumer(topic, "channel_box_" + util.String(vendor), config)
if err != nil {
return log.Error("Failed to new nsq consumers.")
}
c.AddConcurrentHandlers(nsq.HandlerFunc(func(message *nsq.Message) error {
if err := b.handle(message, vendor); err != nil {
log.Errorf("Handle message %v for vendor %d from mq failed.", message.ID, vendor)
}
return nil
}), 5)
if err = c.ConnectToNSQLookupds(b.Opts.Nsq.Lookup); err != nil {
return log.Error("Failed to connect to nsq lookup server.")
}
b.consumers = append(b.consumers, c)
return nil
}
In ArrayBlockingQueue, inside the put method, why does it call notFull.signal() after catching InterruptedException? When the thread is going to terminate, why does it send out a 'not full' signal?
From the source:
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
final E[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
try {
while (count == items.length)
notFull.await();
} catch (InterruptedException ie) {
notFull.signal(); // propagate to non-interrupted thread
throw ie;
}
insert(e);
} finally {
lock.unlock();
}
}
Imagine the following scenario:
Threads 1 and 4 are waiting on notFull, ie, the queue is full, and the lock is released.
Thread 2 holds the lock, and is about to remove an element from the queue.
Thread 3 interrupts Thread 1.
Now imagine the following interleaving:
+-+--+--------+-----------+------- TIME +---+------------+---------------------->
| | | | | | | |
+---------+ +------------------+ +----------+ |
| Thread2 | | Thread2 | | Thread2 | |
| lock() | | notFull.signal() | | unlock() | |
+---------+ +------------------+ +----------+ |
| | | | |
+---------------------+ | | |
| Thread3 | | | |
| Thread1.interrupt() | | | |
+---------------------+ | | |
| | | |
+---------------+ +-------------+ +---------+ +----------------------+
| Thread1 | | Thread1 | | Thread1 | | Thread1 |
| interrupted() | | signalled() | | lock() | | InterruptedException |
+---------------+ +-------------+ +---------+ +----------------------+
What if InterruptedException wasn't caught, and Thread 1 was just to unlock and abandon the wait? What would happen to Thread 4, who was still waiting for a signal on notFull? The signal had already been sent by Thread 2, but it just so happened that the receiving thread, Thread 1, had been interrupted, and the signal was wasted.
In short: If the thread received a signal when it was also interrupted, it passes along the signal to another thread, so that it isn't lost. This prevents threads from waiting indefinitely for something that already happened.