Calling network services in parallel using RxJava. Is this the right way?

我怕爱的太早我们不能终老 提交于 2020-01-02 04:37:05

问题


Idea is to make 3 network calls in parallel. (I am using Google as the servies for demo purpose. The following works but not sure if this is the right way or it can be simplified. What should I do if I have to combine the responses of all the three searches? Please advise.

public class GoogleSearchRx
{
    public static void main(String args[])
    {
        CountDownLatch latch = new CountDownLatch(3);

        search("RxJava").subscribeOn(Schedulers.io()).subscribe(
                links -> {
                    links.forEach(link -> out.println(currentThreadName() + "\t" + link.text()));
                    latch.countDown();
                },
                e -> {
                    out.println(currentThreadName() + "\t" + "Failed: " + e.getMessage());
                    latch.countDown();
                }
        );

        search("Reactive Extensions").subscribeOn(Schedulers.io()).subscribe(
                links -> {
                    links.forEach(link -> out.println(currentThreadName() + "\t" + link.text()));
                    latch.countDown();
                },
                e -> {
                    out.println(currentThreadName() + "\t" + "Failed: " + e.getMessage());
                    latch.countDown();
                }
        );

        //run the last one on current thread
        search("Erik Meijer").subscribe(
                links -> {
                    links.forEach(link -> out.println(currentThreadName() + "\t" + link.text()));
                    latch.countDown();
                },
                e -> {
                    out.println(currentThreadName() + "\t" + "Failed: " + e.getMessage());
                    latch.countDown();
                }
        );

        try
        {
            latch.await();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

    public static Observable<Elements> search(String q)
    {
        String google = "http://www.google.com/search?q=";

        String charset = "UTF-8";
        String userAgent = "ExampleBot 1.0 (+http://example.com/bot)"; // Change this to your company's name and bot homepage!

        return Observable.create(new Observable.OnSubscribe<Elements>()
        {

            @Override public void call(Subscriber<? super Elements> subscriber)
            {
                out.println(currentThreadName() + "\tOnSubscribe.call");

                try
                {
                    Elements links = Jsoup.connect(google + URLEncoder.encode(q, charset)).timeout(1000).userAgent(userAgent).get().select("li.g>h3>a");
                    subscriber.onNext(links);
                }
                catch (IOException e)
                {
                    subscriber.onError(e);
                }
                subscriber.onCompleted();
            }
        });
    }
}

回答1:


Going by the "combine the responses of all the three searches" part of your question, you might be looking for Zip.

Observable<Elements> search1 = search("RxJava");
Observable<Elements> search2 = search("Reactive Extensions");
Observable<Elements> search3 = search("Eric Meijer");
Observable.zip(searc1, search2, search3,
            new Func3<Elements, Elements, Elements, Elements>() {
                @Override
                public Elements call(Elements result1, Elements result2, Elements result3) {
                    // Add all the results together...
                    return results;
                }
            }
    ).subscribeOn(Schedulers.io()).subscribe(
            links -> {
                links.forEach(link -> out.println(currentThreadName() + "\t" + link.text()));
                latch.countDown();
            },
            e -> {
                out.println(currentThreadName() + "\t" + "Failed: " + e.getMessage());
                latch.countDown();
            }
    );

This assumes you want to deal with all the results at the same time (in the subscriber, here) and not care about which query was used for the given result.

Note there's different versions of the zip function, taking from 1..N observables, and Func1 to Func9 or FuncN, allowing you to zip a specific or arbitrarily large number of observables.




回答2:


Here's another approach which handles the entire process (including the Jsoup call), avoids any countdown latches, and provides a way to avoid having to use Observable.create (since it's a lot easier to let the Rx operators handle all the subscriber management stuff!)

("back-of-napkin" code, may need some nudging to compile.)

final String google = "http://www.google.com/search?q=";

final String charset = "UTF-8";
final String userAgent = "ExampleBot 1.0 (+http://example.com/bot)"; // ...
Observable.just("RxJava", "Reactive Extensions", "Erik Meijer")
    .flatMap((query) -> Observable.defer(() -> {
        try {
            return Observable.from(Jsoup.connect(google + URLEncoder.encode(query, charset))
                .timeout(1000)
                .userAgent(userAgent)
                .get()
                .select("li.g>h3>a")).subscribeOn(Schedulers.io());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }))
    .forEach(
        (link) -> out.println(link.text()),
        (e) -> out.println("Failed: " + e.getMessage()));

Note that, as in your original example, the sorting of this isn't guaranteed. One way to handle that would be toSortedList, which either expects an Observable of items implementing Comparable, or a Func2 to provide comparison between elements.



来源:https://stackoverflow.com/questions/28005876/calling-network-services-in-parallel-using-rxjava-is-this-the-right-way

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!