What kind of issue is this?
[X] Question. This issue tracker is not the place for questions. If you want to ask how to do
something, or to understand why something isn't working the way you expect it to, use Stack
Overflow. https://stackoverflow.com/questions/tagged/retrofit
[ ] Bug report. If you鈥檝e found a bug, spend the time to write a failing test. Bugs with tests
get fixed. Here鈥檚 an example: https://gist.github.com/swankjesse/6608b4713ad80988cdc9
[ ] Feature Request. Start by telling us what problem you鈥檙e trying to solve. Often a solution
already exists! Don鈥檛 send pull requests to implement new features without first getting our
support. Sometimes we leave features out on purpose to keep the project small.
I know this isn't the best place to ask it, but since i couldn't get any support on starckoverflow and i'm struggling with this problem over a week i'm going to ask here, maybe is something very simple that a person with more experience handle fast.
I'm using OkHttp + Retrofit2 to build an api to a third party system.
I've noticed that even using a ConnectionPool and multiple threads, Retrofit is never making simultaneous calls.
This is how i create the service:
OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
httpClientBuilder.connectionPool(new ConnectionPool(7, 1, TimeUnit.MINUTES););
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(10);
dispatcher.setMaxRequestsPerHost(10);
httpClientBuilder.dispatcher(dispatcher);
OkHttpClient okhttp = httpClientBuilder.build();
this.service = new Retrofit.Builder().baseUrl(API_URL).addConverterFactory(GsonConverterFactory.create(getGson())).client(okhttp).build().create(IService.class);
In order to simplify the IService.class lets supose it has only 2 methods:
@GET
Call<JsonElement> get(@Url String url, @HeaderMap Map<String, Object> headers, @QueryMap Map<String, Object> query);
@POST
Call<ResponseBody> complexPost(@Url String url, @HeaderMap Map<String, Object> headers, @QueryMap Map<String, Object> query, @Body RequestBody body);
now this is how i'm trying to make parallel requests:
public static void main(String[] args) {
final Map<String,Object> empty = new HashMap<String, Object>();
new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("sending short request");
this.service.get(bar,empty,empty).execute();
System.out.println("short request sent");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
byte[] buffer = new byte[1024*1024];
while(true) {
RequestBody requestFile = RequestBody.create(MediaType.parse("application/octet-stream"), buffer, 0, buffer.length);
System.out.println("sending long request").execute();
this.service.complexPost(foo, empty, empty, requestFile);
System.out.println("long request sent");
}
}
}).start();
}
Simple explanation: two threads, thread A sends a small GET request - thread B sends 1MB data request [1024*1024 zeros just]
So, nothing is synchronized, I've a connection pool with enough idle connections to make it run in parallel, IN MY MIND sine request B is bigger is natural it takes long to complete, but as there is no synchronization there should be LOTS of "short request sent" for every "long request sent".
but what is printed is something like:
sending short request
short request sent
sending long request
long request sent
sending short request
short request sent
sending long request
long request sent
sending short request
short request sent
no thread is interrupting the other request. WHY???? how to make multiple requests in parallel?
I did a deep debuging and i found the snippet of code where the synchronization locks:
private Http2Stream newStream(
int associatedStreamId, List<Header> requestHeaders, boolean out) throws IOException {
boolean outFinished = !out;
boolean inFinished = false;
boolean flushHeaders;
Http2Stream stream;
int streamId;
synchronized (writer) {
synchronized (this) {
if (nextStreamId > Integer.MAX_VALUE / 2) {
shutdown(REFUSED_STREAM);
}
if (shutdown) {
throw new ConnectionShutdownException();
}
streamId = nextStreamId;
nextStreamId += 2;
stream = new Http2Stream(streamId, this, outFinished, inFinished, null);
flushHeaders = !out || bytesLeftInWriteWindow == 0L || stream.bytesLeftInWriteWindow == 0L;
if (stream.isOpen()) {
streams.put(streamId, stream);
}
}
if (associatedStreamId == 0) {
writer.synStream(outFinished, streamId, associatedStreamId, requestHeaders);
} else if (client) {
throw new IllegalArgumentException("client streams shouldn't have associated stream IDs");
} else { // HTTP/2 has a PUSH_PROMISE frame.
writer.pushPromise(associatedStreamId, streamId, requestHeaders);
}
}
if (flushHeaders) {
writer.flush();
}
return stream;
}
this is the synchronization block that locks boths threads. my question is WHY, and how to fix it?
Why wasn't that answered?
This is also on SO, unanswered: https://stackoverflow.com/questions/58385954/retrofit2-parallel-requests
thanks for all your love and consideration retrofit guys
@swankjesse clean the mess under the carpet
Most helpful comment
Why wasn't that answered?