Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get the serviceName in Service discovery #1183

Closed
mengxiangyue opened this issue May 17, 2021 · 8 comments
Closed

Get the serviceName in Service discovery #1183

mengxiangyue opened this issue May 17, 2021 · 8 comments
Labels
kind/support Adopter support requests.

Comments

@mengxiangyue
Copy link

What are you trying to achieve?

When I am creating the client, I want to use service discovery to get the service name. But I need to create Client then I can get the service name, if I want to create Client I need to use host/port to create a channel, at here I need to use service name to get host. it become a cycle. could you give a suggestion?

// Configure the channel, we're not using TLS so the connection is `insecure`.
let channel = ClientConnection.insecure(group: group)
  .connect(host: "localhost", port: self.port) // I need to use service name to get the host/port

// Provide the connection to the generated client.
let greeter = Helloworld_GreeterClient(channel: channel)
@mengxiangyue mengxiangyue added the kind/support Adopter support requests. label May 17, 2021
@glbrntt
Copy link
Collaborator

glbrntt commented May 17, 2021

I suspect the correct way of resolving this is to generate some static metadata for each service and integrate with swift-service-discovery. However, that'll take a little bit of time.


As a workaround for the meantime you can create a no-op client to get the service name by creating something which conforms to GRPCChannel:

struct NoOpChannel: GRPCChannel {
  func makeCall<Request: SwiftProtobuf.Message, Response: SwiftProtobuf.Message>(
    path: String,
    type: GRPCCallType,
    callOptions: CallOptions,
    interceptors: [ClientInterceptor<Request, Response>]
  ) -> Call<Request, Response> { 
    fatalError()
  }

  func makeCall<Request: GRPCPayload, Response: GRPCPayload>(
    path: String,
    type: GRPCCallType,
    callOptions: CallOptions,
    interceptors: [ClientInterceptor<Request, Response>]
  ) -> Call<Request, Response> {
    fatalError()
  }

  func close() -> EventLoopFuture<Void> { 
    fatalError()
  }
}

Then you can extract the service name for service discovery:

let noOpGreeter = Helloworld_GreeterClient(channel: NoOpChannel())
let serviceName = noOpGreeter.serviceName

I appreciate that it's not the nicest solution but it should at least unblock you!

@mengxiangyue
Copy link
Author

@glbrntt thanks your response so fast, what do you think if we change the serviceName into static property?

@glbrntt
Copy link
Collaborator

glbrntt commented May 18, 2021

@glbrntt thanks your response so fast, what do you think if we change the serviceName into static property?

Unfortunately we can't just make it static since we'd effectively be breaking the API.

We'd most likely want to generate some static service metadata alongside the protocol and then add a static property with a default implementation to the generated protocol, something like:

protocol Echo_EchoClientProtocol: GRPCClient {
  // existing
  var serviceName: String { get }
  
  // ... 
  // RPC definitions
  // ...

  // new:
  static var metadata: GRPCServiceMetadata { get }
}

extension Echo_EchoClientProtocol {
  // new, default impl.
  static let metadata = GRPCServiceMetadata(name: "echo.Echo")
}

@weissi
Copy link
Contributor

weissi commented May 18, 2021

@glbrntt couldn't we just add a static one?

@mengxiangyue
Copy link
Author

agree with you we can't break the api. I also have another question, I don't find the GRPCServiceMetadata in current code, will it like this?

struct GRPCServiceMetadata {
  let name: String
  let version: String
  ......
}

@glbrntt
Copy link
Collaborator

glbrntt commented May 18, 2021

@glbrntt couldn't we just add a static one?

We could, but it might just be more useful to have a more complete set of metadata (i.e. include RPCs as well).

@glbrntt
Copy link
Collaborator

glbrntt commented May 18, 2021

agree with you we can't break the api. I also have another question, I don't find the GRPCServiceMetadata in current code, will it like this?

struct GRPCServiceMetadata {
  let name: String
  let version: String
  ......
}

It doesn't exist yet! It could look something like that, it could also include other info like RPC metadata (i.e. name, request type, response type, whether the RPC streams requests or responses etc.). I'm just thinking out loud at this point though.

glbrntt added a commit to glbrntt/grpc-swift that referenced this issue Dec 8, 2021
Motivation:

Sometimes it's useful to know information about a service, including its
name, methods it offers and so on. An example where this is useful is
service discovery (grpc#1183). However, we currently only provide the
service name and this isn't available statically. For service discovery
this is problematic as it requires users to create a client in order to
get the service name so that a server can be dialled.

For the async/await code, since it is not yet final, we can add
requirements to generated protocols to provide the service metadata
statically.

Modifications:

- Add service and method descriptors to `GRPC`
- Generate service descriptors for client and server separately (it's feasible
  that a user may generate client code into one module and server code
  into separate modules)
- Update the generated code for async/await and NIO based APIs to use
  the descriptors directly rather than generating literals in places
  where they are required.
- Add test for the generated echo service metadata
- Regenerate other services

Result:

Adopters can get static information about services.
glbrntt added a commit that referenced this issue Dec 8, 2021
Motivation:

Sometimes it's useful to know information about a service, including its
name, methods it offers and so on. An example where this is useful is
service discovery (#1183). However, we currently only provide the
service name and this isn't available statically. For service discovery
this is problematic as it requires users to create a client in order to
get the service name so that a server can be dialled.

For the async/await code, since it is not yet final, we can add
requirements to generated protocols to provide the service metadata
statically.

Modifications:

- Add service and method descriptors to `GRPC`
- Generate service descriptors for client and server separately (it's feasible
  that a user may generate client code into one module and server code
  into separate modules)
- Update the generated code for async/await and NIO based APIs to use
  the descriptors directly rather than generating literals in places
  where they are required.
- Add test for the generated echo service metadata
- Regenerate other services

Result:

Adopters can get static information about services.
@glbrntt
Copy link
Collaborator

glbrntt commented Jun 14, 2022

The 1.8.0 release adds support for this. See

public enum Echo_EchoClientMetadata {
public static let serviceDescriptor = GRPCServiceDescriptor(
name: "Echo",
fullName: "echo.Echo",
methods: [
Echo_EchoClientMetadata.Methods.get,
Echo_EchoClientMetadata.Methods.expand,
Echo_EchoClientMetadata.Methods.collect,
Echo_EchoClientMetadata.Methods.update,
]
)
public enum Methods {
public static let get = GRPCMethodDescriptor(
name: "Get",
path: "/echo.Echo/Get",
type: GRPCCallType.unary
)
public static let expand = GRPCMethodDescriptor(
name: "Expand",
path: "/echo.Echo/Expand",
type: GRPCCallType.serverStreaming
)
public static let collect = GRPCMethodDescriptor(
name: "Collect",
path: "/echo.Echo/Collect",
type: GRPCCallType.clientStreaming
)
public static let update = GRPCMethodDescriptor(
name: "Update",
path: "/echo.Echo/Update",
type: GRPCCallType.bidirectionalStreaming
)
}
}
as an example.

@glbrntt glbrntt closed this as completed Jun 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/support Adopter support requests.
Projects
None yet
Development

No branches or pull requests

3 participants