Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Setup for establishing connection(s) in a multi-service client application using v2 #2211

Open
Labels
status/triageCollecting information required to triage the issue.
@majelbstoat

Description

What are you trying to achieve?

I am building an iOS app for a complex product that speaks to multiple logical GRPC services, all served from the same server. So, for example, I can call UserService.GetUser() and ProgramService.GetProgram() and they will both go to the same (load-balanced, horizontally scaled) host.

Every example I've seen for usage of Swift GRPC 2 is for a one-off single call to a single service, looking like:

try await withGRPCClient(
 transport: .http2NIOPosix(
 target: .dns(host: hostname, port: 443),
 transportSecurity: .tls
 )
 ) { client in
 let greeter = GreetingService.Client(wrapping: client)
 let greeting = try await greeter.sayHello(.with { 0ドル.name = "swift.org" })
 print(greeting.message)
 }

I'd like to understand best practices for usage with multiple services, within an actual app that needs a connection to function. A couple of options spring to mind:

  1. Create a new GRPCClient for each service client.
  2. Create a single GRPCClient and then re-use that for each service client.
  3. Create a single GRPCClient that has some kind of pool behind it (of say, 4 connections), then re-use that single exposed GRPCClient for each service client.

Option 2/3 is most similar to what I had for Swift GRPC 1, and my preferred approach if possible. (There are already a dozen services, and there will be more in the future.)

I would also like to understand if there's a blocking/synchronous approach to ensuring a connection is established.

What have you tried so far?

Previously, I had a single synchronously established GRPCChannel, exposed as a static computed variable:

class GRPCManager {
 private static var host: String {
 Bundle.main.infoDictionary?["APP_CORE_HOST"] as! String
 }
 static var connection: GRPCChannel {
 guard host != "" else {
 fatalError("GRPC Host is not set")
 }
 return connect()
 }
 private static func connect() -> GRPCChannel {
 let eventLoopGroup = NIOTSEventLoopGroup(loopCount: 1, defaultQoS: .default)
 return ClientConnection.usingPlatformAppropriateTLS(for: eventLoopGroup)
 .connect(host: host, port: 443)
 }
}

and then each services had an init like:

 init() {
 client = App_UserServiceAsyncClient(channel: GRPCManager.connection)
 }
 init() {
 client = App_ProgramServiceAsyncClient(channel: GRPCManager.connection)
 }

This lazily established the connection the first time it was needed, requiring no explicit setup in the @main App. It seemed to work well. I appreciate that client.runConnections() is now asynchronous, but I need to know when it's reliably available/block until the connection is established, because the app is unusable without it.

I would like to achieve this while keeping the service clients themselves isolated and independent of each other, in particular not requiring a specific order of service usage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    status/triageCollecting information required to triage the issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

        AltStyle によって変換されたページ (->オリジナル) /