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

First pass implementation of NIO client #357

Merged
merged 34 commits into from
Mar 1, 2019
Merged

First pass implementation of NIO client #357

merged 34 commits into from
Mar 1, 2019

Conversation

glbrntt
Copy link
Collaborator

@glbrntt glbrntt commented Jan 14, 2019

@MrMage @timburks

Notes:

  • Resolves Add client support for SwiftGRPCNIO #354
  • The setup and logic is similar to that in the server (see: First implementation of gRPC based on SwiftNIO #281).
  • Users create a GRPCClient and then pass it to a generated client for their service (i.e. `EchoClient)
  • Each type of call returns a corresponding call object, e.g. {Unary,ClientStreaming,ServerStreaming,BidirectionalStreaming}ClientCall
  • Each call object has futures for the initial metadata, trailing metadata and gRPC status
    • {Unary,ClientStreaming}ClientCall have response futures
    • {ServerStreaming,BidirectionalStreaming}ClientCall have response callbacks
    • {ClientStreaming,BidirectionalStreaming}ClientCall have send() methods
  • Documentation and error handling is sparse
  • No tests
  • (EchoProviderNIO.swift taken from Add gRPC Web support. #350)

Todo:

  • Call options (additional headers, call deadlines)
  • TLS
  • Ability to cancel() calls
  • Tests
  • Error handling (depends somewhat on server side error handling)
  • Documentation
  • Client generation
  • Access modifiers
  • Server side timeouts
  • Message (de/)compression

Sources/SwiftGRPCNIO/ClientCalls/BaseClientCall.swift Outdated Show resolved Hide resolved

let requestHead = makeRequestHead(path: path, host: client.host)
subchannel.whenSuccess { channel in
channel.write(GRPCClientRequestPart<Request>.head(requestHead), promise: nil)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chain these via promises?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it makes sense to do that here; you'd have to flush and wait for each part to be sent to the network before sending the next.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unresolving this until we have fully discussed this on the other comment.

Sources/SwiftGRPCNIO/GRPCClient.swift Outdated Show resolved Hide resolved
import NIO
import NIOHTTP2

public final class GRPCClient {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make this (and others?) open, so that library users can modify behaviors as needed on a low level?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

@MrMage
Copy link
Collaborator

MrMage commented Jan 22, 2019

Thank you for this!

As discussed, I don’t have time in the next few weeks to review this in depth, sorry :-/ I did leave a few drive-by comments, though; feel free to disregard them if they don’t make sense. The general approach looks sound to me and some parts are already better-engineered than my server implementation; hopefully we can improve the server based on this later on.

Also, we would need tests, of course, but I could imagine you’d want the API to be fleshed our first.

@rebello95, what do you think?

@MrMage
Copy link
Collaborator

MrMage commented Jan 22, 2019

Sorry, just saw the todo-list entwining Tests. Extra client arguments could come in a later PR; currently I’m favoring an array of parameterized enemy with all the possible call options.

@glbrntt
Copy link
Collaborator Author

glbrntt commented Jan 22, 2019

Thanks for the drive-by comments; no problem with not having the time to go into depth.

@MrMage
Copy link
Collaborator

MrMage commented Jan 22, 2019 via email

@rebello95
Copy link
Collaborator

Sorry for the late reply - let's chat more in the phone call tomorrow. Happy to take a closer look after!

Copy link
Member

@timburks timburks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! I'd like to test this with some production services. What's needed to enable TLS?

Sources/SwiftGRPCNIO/GRPCClient.swift Outdated Show resolved Hide resolved
@glbrntt
Copy link
Collaborator Author

glbrntt commented Jan 29, 2019

Looks great! I'd like to test this with some production services. What's needed to enable TLS?

This is meant to be on the TODO list. Adding it to the client should be relatively straightforward with SwiftNIO SSL. "Relatively" because there's also NIO Transport Services for platforms with Network.framework.

public class ServerStreamingClientCall<RequestMessage: Message, ResponseMessage: Message>: BaseClientCall<RequestMessage, ResponseMessage> {

public init(client: GRPCClient, path: String, request: RequestMessage, handler: @escaping (ResponseMessage) -> Void) {
super.init(channel: client.channel, multiplexer: client.multiplexer, responseHandler: .callback(handler: handler))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the responseHandler be held weak?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably up to the caller to use [weak self] when specifying the contents of the handler since we need to keep the closure around, no?

Sources/SwiftGRPCNIO/GRPCStatus.swift Show resolved Hide resolved
Sources/SwiftGRPCNIO/HTTP1ToRawGRPCClientCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/HTTP1ToRawGRPCClientCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/RawGRPCToGRPCCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/RawGRPCToGRPCCodec.swift Outdated Show resolved Hide resolved
- Adds a user-configurable error handler to the server
- Updates NIO server codegen to provide an optional error handler
- Errors are handled by GRPCChannelHandler or BaseCallHandler,
  depending on the pipeline state
- Adds some error handling tests
- Tidies some logic in HTTP1ToRawGRPCServerCodec
- Extends message handling logic in HTTP1ToRawGRPCServerCodec
  to handle messages split across multiple ByteBuffers (i.e. when a
  message exceeds a the size of a frame)
Copy link
Collaborator

@rebello95 rebello95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes seem reasonable overall. I'll let @MrMage approve/merge after reviewing since he has more context on how NIO and the codecs work together.

Sources/Examples/EchoNIO/main.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/ClientCalls/BaseClientCall.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/ClientCalls/BaseClientCall.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/ClientCalls/BaseClientCall.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/HTTP1ToRawGRPCClientCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/HTTP1ToRawGRPCClientCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/RawGRPCToGRPCCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/RawGRPCToGRPCCodec.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/RawGRPCToGRPCCodec.swift Outdated Show resolved Hide resolved
@glbrntt glbrntt changed the title First pass implementation of NIO client (do not merge) First pass implementation of NIO client Feb 5, 2019
@MrMage
Copy link
Collaborator

MrMage commented Feb 14, 2019

Thanks for your patience, George! I hope to finally get this and #364 reviewed next week.

Sources/SwiftGRPCNIO/ClientCalls/ClientCall.swift Outdated Show resolved Hide resolved
/// with an `.end` event.
///
/// - Parameter event: event to send.
func send(_ event: StreamEvent<RequestMessage>)
Copy link
Collaborator

@MrMage MrMage Feb 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, good point. Fulfilling the promise only when we send the bytes to the network helps with regards to backpressure, though. If we e.g. want to stream 10k messages of 10kB each, we don't want to dump them all at once onto the NIO layer (and lower). Not sure what to do here; leaving this open. Maybe @Lukasa or @weissi could comment on this.

Also, could you elaborate the "(It would also require a flush after writing each message if the end was added to the same queue.)" part?

Copy link
Collaborator

@MrMage MrMage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job; very little work left.

Tests/SwiftGRPCNIOTests/NIOClientTimeoutTests.swift Outdated Show resolved Hide resolved
@MrMage
Copy link
Collaborator

MrMage commented Feb 28, 2019 via email

@MrMage
Copy link
Collaborator

MrMage commented Feb 28, 2019

In the PR description you mean, "Message encoding" is left. Does that refer to compression?

Sources/SwiftGRPCNIO/ClientCalls/UnaryClientCall.swift Outdated Show resolved Hide resolved
Sources/SwiftGRPCNIO/ClientCalls/BaseClientCall.swift Outdated Show resolved Hide resolved
Tests/SwiftGRPCNIOTests/NIOServerTests.swift Outdated Show resolved Hide resolved
Sources/Examples/EchoNIO/main.swift Outdated Show resolved Hide resolved
Sources/Examples/EchoNIO/main.swift Outdated Show resolved Hide resolved
@MrMage
Copy link
Collaborator

MrMage commented Feb 28, 2019 via email

@glbrntt
Copy link
Collaborator Author

glbrntt commented Feb 28, 2019

In the PR description you mean, "Message encoding" is left. Does that refer to compression?

Yes, I'll clarify.

Copy link
Collaborator

@MrMage MrMage left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👌

@MrMage
Copy link
Collaborator

MrMage commented Mar 1, 2019

🎉 I will merge this once CI has finished.

@MrMage MrMage merged commit 97ff923 into grpc:master Mar 1, 2019
@MrMage
Copy link
Collaborator

MrMage commented Mar 1, 2019

🚀

@glbrntt glbrntt deleted the client branch June 11, 2019 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants