I believe I may not understand something about how gRPC Channels, Stubs, And Transports work. I have an Android app that creates a channel and a single blocking stub and injects it with dagger when the application is initialized. When I need to make a grpc call, I have a method in my client, that calls a method with that stub. After the app is idle a while, all of my calls return DEADLINE_EXCEEDED errors, though there are no calls showing up in the server logs.
@Singleton
@Provides
fun providesMyClient(app: Application): MyClient {
val channel = AndroidChannelBuilder
.forAddress("example.com", 443)
.overrideAuthority("example.com")
.context(app.applicationContext)
.build()
return MyClient(channel)
}Where my client class has a function to return a request with a deadline:
class MyClient(channel: ManagedChannel) {
private val blockingStub: MyServiceGrpc.MyServiceBlockingStub = MyServiceGrpc.newBlockingStub(channel)
fun getStuff(): StuffResponse =
blockingStub
.withDeadlineAfter(7, TimeUnit.SECONDS)
.getStuff(stuffRequest())
}
fun getOtherStuff(): StuffResponse =
blockingStub
.withDeadlineAfter(7, TimeUnit.SECONDS)
.getOtherStuff(stuffRequest())
}I make the calls to the server inside a LiveData class in My Repository, where the call looks like this: myClient.getStuff()
I am guessing that the channel looses its connection at some point, and then all of the subsequent stubs simply can't connect, but I don't see anywhere in the AndroidChannelBuilder documentation that talks about how to handle this (I believed it reconnected automatically). Is it possible that the channel I use to create my blocking stub gets stale, and I should be creating a new blocking stub each time I call getStuff()? Any help in understanding this would be greatly appreciated.
After researching a bit, I believe the issue was that the proxy on the server was closing the connection after a few minutes of idle time, and the client ManagedChannel didn't automatically detect that and connect again when that happened. When constructing the ManagedChannel, I added an idleTimeout to it, which will proactively kill the connection when it's idle, and reestablish it when it's needed again, and this seems to solve the problem. So the new channel construction looks like this:
@Singleton
@Provides
fun providesMyClient(app: Application): MyClient {
val channel = AndroidChannelBuilder
.forAddress("example.com", 443)
.overrideAuthority("example.com")
.context(app.applicationContext)
.idleTimeout(60, TimeUnit.SECONDS)
.build()
return MyClient(channel)
}--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/1202aad5-4897-4bbb-a238-34edae74e368%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
On Jan 17, 2019, at 2:41 PM, Bryant Davis <davis....@gmail.com> wrote:Thanks for your response, Robert! I wasn't clear on how the keepalives work, and saw some warning in the docs about increasing load on servers, but perhaps thats better than redoing the tls handshake each time? There seem to be 3 options: keepAliveTime, keepAliveTimeout, and keepAliveWithoutCalls. I suppose I would use keepAliveTime, and that would prevent the connection from closing?
After researching a bit, I believe the issue was that the proxy on the server was closing the connection after a few minutes of idle time, and the client ManagedChannel didn't automatically detect that and connect again when that happened. When constructing the ManagedChannel, I added an idleTimeout to it, which will proactively kill the connection when it's idle, and reestablish it when it's needed again, and this seems to solve the problem. So the new channel construction looks like this:
@Singleton @Provides fun providesMyClient(app: Application): MyClient { val channel = AndroidChannelBuilder .forAddress("example.com", 443) .overrideAuthority("example.com") .context(app.applicationContext) .idleTimeout(60, TimeUnit.SECONDS) .build() return MyClient(channel) }To anyone who might see this, does that seem like a plausible explanation?
On Wednesday, January 16, 2019 at 7:30:42 PM UTC-6, davis....@gmail.com wrote:I believe I may not understand something about how gRPC Channels, Stubs, And Transports work. I have an Android app that creates a channel and a single blocking stub and injects it with dagger when the application is initialized. When I need to make a grpc call, I have a method in my client, that calls a method with that stub. After the app is idle a while, all of my calls return DEADLINE_EXCEEDED errors, though there are no calls showing up in the server logs.
@Singleton @Provides fun providesMyClient(app: Application): MyClient { val channel = AndroidChannelBuilder .forAddress("example.com", 443) .overrideAuthority("example.com") .context(app.applicationContext) .build() return MyClient(channel) }Where my client class has a function to return a request with a deadline:
class MyClient(channel: ManagedChannel) { private val blockingStub: MyServiceGrpc.MyServiceBlockingStub = MyServiceGrpc.newBlockingStub(channel) fun getStuff(): StuffResponse = blockingStub .withDeadlineAfter(7, TimeUnit.SECONDS) .getStuff(stuffRequest()) } fun getOtherStuff(): StuffResponse = blockingStub .withDeadlineAfter(7, TimeUnit.SECONDS) .getOtherStuff(stuffRequest()) }I make the calls to the server inside a LiveData class in My Repository, where the call looks like this: myClient.getStuff()
I am guessing that the channel looses its connection at some point, and then all of the subsequent stubs simply can't connect, but I don't see anywhere in the AndroidChannelBuilder documentation that talks about how to handle this (I believed it reconnected automatically). Is it possible that the channel I use to create my blocking stub gets stale, and I should be creating a new blocking stub each time I call getStuff()? Any help in understanding this would be greatly appreciated.
--
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/CALUXJ7hu1sw9UEg8XS-fw3RNhfBQYs41ozeAAZMSr0yZKjRT6A%40mail.gmail.com.
A lot of proxies - at least the firewall kind - don’t implement the TCP protocol to close the connection for an idle timeout - they just drop their own side / mapping.When you attempt to send via that connection later on, then you will get the error atthe TCP layer.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/3B806C4F-240E-41DC-9E24-93CEB43723CD%40earthlink.net.