I've been stuck with this for a day. Inspired in Dan Lew great post, I tried to make a simple testcase for repeatWhen() and retryWhen():
public class ObsTest {
private static final Logger LOG = LoggerFactory.getLogger(ObsTest.class);
#Test
public void test1() throws InterruptedException {
Observable<Integer> obs = rx.Observable.<Integer> create(observer -> {
LOG.info("onSubscribe");
Integer data = RandomUtils.nextInt(0, 1000);
if (data % 2 != 0) {
observer.onError(new RuntimeException("Odd number " + data));
} else {
observer.onNext(data);
}
observer.onCompleted();
}, BackpressureMode.BUFFER);
obs.repeatWhen(completed -> completed.delay(1, TimeUnit.MILLISECONDS))
.retryWhen(error -> error.delay(1, TimeUnit.MILLISECONDS))
.subscribe(i -> LOG.info("value={}", i), e -> LOG.info("Exception = {}", e.getMessage()));
}
My idea is this should run forever, emitting even numbers as "correct" results, and odd numbers as "errors".
Instead, this runs for one or two loops and then stops. And that is when the delay is 1 millisecond, for longer periods of time (ie. 1 second), it runs a single time, emitting just a single odd or even number.
I'm sure I'm doing something wrong, but I can't find what it is.
When you call delay which uses Schedulers.computation() by default you are introducing asynchrony. Once activity starts occurring in a background thread your test will finish and presumably your process is exited. You need to use a blockingSubscribe or put a longish Thread.sleep at the end.
As Dave Moten mentioned, delay uses Schedulers.computation() by default, but you can pass scheduler of your choice instead - for tests purposes you may use TestScheduler and "take control over time". Code below shows how can it be used - as you can see this subscription won't terminal for another 30 days, what is basically forever ;)
public class ObsTest {
#Test
public void test1() {
Observable<Integer> obs = rx.Observable.create(observer -> {
Integer data = RandomUtils.nextInt(0, 1000);
if (data % 2 != 0) {
observer.onError(new RuntimeException("Odd number " + data));
} else {
observer.onNext(data);
}
observer.onCompleted();
}, Emitter.BackpressureMode.BUFFER);
TestScheduler scheduler = Schedulers.<Integer>test();
AssertableSubscriber subscriber = obs.repeatWhen(completed -> completed.delay(1, TimeUnit.MILLISECONDS, scheduler))
.retryWhen(error -> error.delay(1, TimeUnit.MILLISECONDS, scheduler))
.subscribeOn(scheduler)
.test();
subscriber.assertNoValues();
scheduler.advanceTimeBy(30, TimeUnit.SECONDS);
subscriber.assertNoTerminalEvent();
scheduler.advanceTimeBy(30, TimeUnit.DAYS);
subscriber.assertNoTerminalEvent();
}
}
Related
Although I've been writing Java code for many years, I've barely done any work with RxJava, and I need to understand how to map it to expected results. I have a lot of existing code in services I work with, but I'm not convinced they are using RxJava properly.
Note that we're using an old version of RxJava, 2.1.10. I can't upgrade at this moment.
The following is a common pattern I see in our codebase:
Single<ResultType> result1 = Single.<ResultType>create(source -> {
source.onSuccess(method1(parameters));
}).subscribeOn(Schedulers.io());
Single<ReturnType> result2 = Single.<ResultType>create(source -> {
source.onSuccess(method2(parameters));
}).subscribeOn(Schedulers.io());
if (null != result1 && null != result2) {
The intent of this is that the execution of "method1" and "method2" run in parallel, and that the check for "null != result1 && null != result2" happens after both methods have finished executing. I'm thinking it's possible that neither of these intentions are being fulfilled here, but I need confirmation of that, and also how to achieve those goals properly.
Depending on how your sources are setup, you can use combineLatest() to wait for the result from both sources. A sample proof-of-concept code might look like this:
public static void main(String[] args) throws Exception {
Callable<Integer> c1 = new Callable<Integer>() {
#Override
public Integer call() throws Exception {
System.out.println(System.currentTimeMillis()+"|Starting first");
Thread.sleep(1111);
System.out.println(System.currentTimeMillis()+"|finished first");
return 42;
}};
Single<Integer> singleFirst = Single.fromCallable(c1).subscribeOn(Schedulers.newThread());
Callable<Integer> c2 = new Callable<Integer>() {
#Override
public Integer call() throws Exception {
System.out.println(System.currentTimeMillis()+"|Starting second");
Thread.sleep(5555);
System.out.println(System.currentTimeMillis()+"|finished second");
return 12;
}};
Single<Integer> singleSecond = Single.fromCallable(c2).subscribeOn(Schedulers.newThread());
BiFunction<Integer, Integer, Integer> func = (a,b) -> a+b;
ObservableSource<Integer> source1 = singleFirst.toObservable();
ObservableSource<Integer> source2 = singleSecond.toObservable();
Observable<Integer> resultSource = Observable.combineLatest(source1, source2, func);
System.out.println(System.currentTimeMillis()+"|All setup, wait for completion");
resultSource.blockingSubscribe(r -> {
System.out.println(System.currentTimeMillis()+"|Result is: "+r);
});
}
This might generate the following output:
1589229378890|All setup, wait for completion
1589229378895|Starting second
1589229378895|Starting first
1589229380007|finished first
1589229384451|finished second
1589229384452|Result is: 54
As you see the Single subscriptions run in parallel and their values are "collected" in a combineLatest() call at the end.
I saw this question here.
It's about achieving delay for each emitted item. This is how to achieve it based on the accepted answer:
Observable.zip(Observable.range(1, 5)
.groupBy(n -> n % 5)
.flatMap(g -> g.toList()),
Observable.interval(50, TimeUnit.MILLISECONDS),
(obs, timer) -> obs)
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
In the question, the asker specifically asked for a fixed set of observables (Observable.range(1,5)), unfortunately this is not what I want to achieve.
I also saw this comment.
That comment is what I want to achieve. So my source observable emits items at a slower (and sometimes faster) rate than the interval. Also the observable's emits are never ending.
===
So basically I want hot observables to have minimum delay.
For example, if I want 400ms minimum delay and I have this kind of observable emittance:
X1-100ms delay-X2-200ms delay-X3-600ms delay-X4-20000ms delay-X5-...
I want it to yield:
X1-400ms delay-X2-400ms delay-X3-600ms delay-X4-20000ms delay-X5-...
Anybody have any idea to achieve that?
Your requirement is so strange...
I can resolve it but not elegant. Here is my code:
class Three<A, B, C> {
A a;
B b;
C c;
// Getter, Setter, Constructor
}
public static void main(String[] args) throws Exception {
BehaviorSubject<Integer> s = BehaviorSubject.create();
// Three = (The value, upstream comes mills, downstream emits mills)
s.map(i -> new Three<>(i, System.currentTimeMillis(), System.currentTimeMillis()))
.scan((a, b) -> {
b.setC(a.getC() + Math.max(400L, b.getB() - a.getB()));
return b;
})
.concatMap(i -> Observable.just(i.getA()).delay(Math.max(0, i.getC() - System.currentTimeMillis()),
TimeUnit.MILLISECONDS))
.subscribe(i -> System.out.println(i + "\t" + System.currentTimeMillis()));
s.onNext(0);
Thread.sleep(100);
s.onNext(1);
Thread.sleep(200);
s.onNext(2);
Thread.sleep(600);
s.onNext(3);
Thread.sleep(2000);
s.onNext(4);
Thread.sleep(200);
s.onNext(5);
Thread.sleep(800);
s.onNext(6);
Thread.sleep(1000);
}
and output
0 1510128693984
1 1510128694366 // 400ms
2 1510128694766 // 400ms
3 1510128695366 // 600ms
4 1510128697366 // 2000ms
5 1510128697766 // 400ms
6 1510128698567 // 800ms
for (int i=0; i<100000; i++) {
// REST API request.
restTemplate.exchange(url, HttpMethod.GET, request, String.class);
}
I have a situation where I have to request a resource for 100k users and it takes 70 minutes to finish. I tried to clean up my code as much as possible and I was able to reduce it only by 4 minutes).
Since each request is independent of each other, I would love to send requests in parallel (may be in 10s, 100s, or even 1000s of chunks which every finishes quickly). I'm hoping that I can reduce the time to 10 minutes or something close. How do I calculate which chunk size would get the job done quickly?
I have found the following way but I can't tell if the program processes all the 20 at a time; or 5 at a time; or 10 at a time.
IntStream.range(0,20).parallel().forEach(i->{
... do something here
});
I appericiate your help. I am open to any suggestions or critics!!
UPDATE: I was able to use IntStream and the task finished in 28 minutes. But I am not sure this is the best I could go for.
I used the following code in Java 8 and it did the work. I was able to reduce the batch job to run from 28 minutes to 3:39 minutes.
IntStream.range(0, 100000).parallel().forEach(i->{
restTemplate.exchange(url, HttpMethod.GET, request, String.class);
}
});
The standard call to parallel() will create a thread for each core your machine has available minus one core, using a Common Fork Join Pool.
If you want to specify the parallelism on your own, you will have different possibilities:
Change the parallelism of the common pool: System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20")
Use an own pool:
Example:
int allRequestsCount = 20;
int parallelism = 4; // Vary on your own
ForkJoinPool forkJoinPool = new ForkJoinPool(parallelism);
IntStream.range(0, parallelism).forEach(i -> forkJoinPool.submit(() -> {
int chunkSize = allRequestsCount / parallelism;
IntStream.range(i * chunkSize, i * chunkSize + chunkSize)
.forEach(num -> {
// Simulate long running operation
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": " + num);
});
}));
This implementation is just examplary to give you an idea.
For your situation you can work with fork/join framework or make executor service pool of threads.
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(8);
service.submit(() -> {
//do your task
});
} catch (Exception e) {
} finally {
if (service != null) {
service.shutdown();
}
}
service.awaitTermination(1, TimeUnit.MINUTES);
if(service.isTerminated())
System.out.println("All threads have been finished");
else
System.out.println("At least one thread running");
And using fork/join framework
class RequestHandler extends RecursiveAction {
int start;
int end;
public RequestHandler(int start, int end) {
this.start = start;
this.end = end;
}
#Override
protected void compute() {
if (end - start <= 10) {
//REST Request
} else {
int middle = start + (end - start) / 2;
invokeAll(new RequestHandler(start, middle), new RequestHandler(middle, end));
}
}
}
Public class MainClass{
public void main(String[] args){
ForkJoinTask<?> task = new RequestHandler(0, 100000);
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(task);
}
}
I've written a short article about that. It contains simple tool that allows you to control pool size:
https://gt-dev.blogspot.com/2016/07/java-8-threads-parallel-stream-how-to.html
I'm using Stream.generate to get data from Instagram. As instagram limits calls per hour I want generate to run less frequent then every 2 seconds.
I've chosen such title because I moved from ScheduledExecutorService.scheduleAtFixedRate and that's what I was searching for. I do realise that stream intermediate operations are lazy and cannot be called on schedule. If you have better idea for title let me know.
So again I want to have at least 2 second delay between genations.
My attempt wich doesn't take into consideration time consumed by operations after generate, which might take longer then 2s:
Stream.generate(() -> {
List<MediaFeedData> feedDataList = null;
while (feedDataList == null) {
try {
Thread.sleep(2000);
feedDataList = newData();
} catch (InstagramException e) {
notifyError(e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return feedDataList;
})
A solution would be to decouple the generator from the Stream, for example using a BlockingQueue
final BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(100);
ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);
scheduler.scheduleAtFixedRate(() -> {
// Generate new data every 2s, regardless of their processing rate
ThreadLocalRandom random = ThreadLocalRandom.current();
queue.offer(random.nextInt(10));
}, 0, 2, TimeUnit.SECONDS);
Stream.generate(() -> {
try {
// Accept new data if ready, or wait for some more to be generated
return queue.take();
} catch (InterruptedException e) {}
return -1;
}).forEach(System.out::println);
If the data processing takes more than 2s, new data will be enqueued and wait to be consumed. If it takes less than 2s, the take method in the generator will wait for new data to be produced by the scheduler.
This way, you are guaranteed to make less than N calls per hour to Instagram !
As far as I understand, your question is about solving two problems:
waiting at a fixed rate rather than a fixed delay
creating a stream for an unknown number of items which allows processing until some point of time (i.e. is not infinite)
You can solve the first task by using a deadline-based waiting and the second by implementing a Spliterator:
Stream<List<MediaFeedData>> stream = StreamSupport.stream(
new Spliterators.AbstractSpliterator<List<MediaFeedData>>(Long.MAX_VALUE, 0) {
long lastTime=System.currentTimeMillis();
#Override
public boolean tryAdvance(Consumer<? super List<MediaFeedData>> action) {
if(quitCondition()) return false;
List<MediaFeedData> feedDataList = null;
while (feedDataList == null) {
lastTime+=TimeUnit.SECONDS.toMillis(2);
while(System.currentTimeMillis()<lastTime)
LockSupport.parkUntil(lastTime);
try {
feedDataList=newData();
} catch (InstagramException e) {
notifyError(e.getMessage());
if(QUIT_ON_EXCEPTION) return false;
}
}
action.accept(feedDataList);
return true;
}
}, false);
Make a Timer and a semaphore. The timer raises the semaphore every 2 seconds, and in the stream you wait on every call for the semaphore.
This keeps the waits to the specified minimum (2 s), and - funnily - would even work with .parallel().
private final volatile Semaphore tickingSemaphore= new Semaphore(1, true);
In its own thread:
Stream.generate(() -> {
tickingSemaphore.acquire();
...
};
In the timer:
tickingSemaphore.release();
I have the following code:
public void onEnter() {
Observable<GObject> obs = context.update(activeG);
obs.subscribe((gObj) -> {
//TODO: add delay of 3 sec.
activeG.publishNewG();
activeG.nextState(GType.RUNNING);
});
}
My question is, How can I put a delay of 3 seconds so
activeG.publishNewG()
is called, then delay of 3 seconds, and then a call to
activeGame.nextState(GameStateType.RUNNING);
'publishNewG' returns void.
Thank you!
If I understand correctly, you want to put a 3 second delay between publishNewG and nextState. You can use doOnNext to inject activity at certain points in the sequence, for example, before and after a 3 second delay:
Observable.just(1)
.doOnNext(e -> System.out.println("Action before"))
.delay(3, TimeUnit.SECONDS)
.doOnNext(e -> System.out.println("Action after"))
.toBlocking().first();
You would typically accomplish this using a scheduler of some sort. Java has ScheduledExecutorService that does this for you.
public class MyClass {
private final ScheduledExecutorService scheduler;
public MyClass() {
scheduler = Executors.newSingleThreadScheduledExecutor();
}
public void onEnter() {
Observable<GObject> obs = context.update(activeG);
obs.subscribe((gObj) -> {
activeG.publishNewG();
// Run in 3 seconds
scheduler.schedule(() -> {
activeG.nextState(GType.RUNNING);
}, 3, TimeUnit.SECONDS);
});
}
}
Edit: From what I can understand of the RxJava docs this is how you would do it with delay:
public void onEnter() {
Observable<GObject> obs = context.update(activeG);
// Observable that delays all events by 3 seconds
Observable<GObject> delayed = obs.delay(3, TimeUnit.SECONDS);
// This will fire immediately when an event happens
obs.subscribe((gObj) -> {
activeG.publishNewG();
});
// This will fire 3 seconds after an event happens
delayed.subscribe((gObj) -> {
activeG.nextState(GType.RUNNING);
});
}
You can easily add delay by using the timer operator.
For example:
// simulated stream of events spread apart by 400ms
Observable<Long> yourObservable = Observable.interval(400, TimeUnit.MILLISECONDS).take(3);
yourObservable.flatMap(data -> {
// add delay of 3 seconds
return Observable.timer(3, TimeUnit.SECONDS).map(i -> data);
}).map(dataAfterDelay -> {
// do whatever you want after 3 seconds
System.out.println("got data " + dataAfterDelay + " after 3 seconds");
return dataAfterDelay + " processed after delay";
}).toBlocking().forEach(System.out::println); // toBlocking here just as example to block main thread
AFAIK you can use Thread.sleep(3000)
If not, something like this should do:
long start = new Date().getTime();
while(new Date().getTime() - start < 3000L){}