GRPCChannelPool.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * Copyright 2021, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import Logging
  17. import NIOCore
  18. import NIOPosix
  19. public enum GRPCChannelPool {
  20. /// Make a new ``GRPCChannel`` on which calls may be made to gRPC services.
  21. ///
  22. /// The channel is backed by one connection pool per event loop, each of which may make multiple
  23. /// connections to the given target. The size of the connection pool, and therefore the maximum
  24. /// number of connections it may create at a given time is determined by the number of event loops
  25. /// in the provided `EventLoopGroup` and the value of
  26. /// ``GRPCChannelPool/Configuration/ConnectionPool-swift.struct/connectionsPerEventLoop``.
  27. ///
  28. /// The event loop and therefore connection chosen for a call is determined by
  29. /// ``CallOptions/eventLoopPreference-swift.property``. If the `indifferent` preference is used
  30. /// then the least-used event loop is chosen and a connection on that event loop will be selected.
  31. /// If an `exact` preference is used then a connection on that event loop will be chosen provided
  32. /// the given event loop belongs to the `EventLoopGroup` used to create this ``GRPCChannel``.
  33. ///
  34. /// Each connection in the pool is initially idle, and no connections will be established until
  35. /// a call is made. The pool also closes connections after they have been inactive (i.e. are not
  36. /// being used for calls) for some period of time. This is determined by
  37. /// ``GRPCChannelPool/Configuration/idleTimeout``.
  38. ///
  39. /// > Important: The values of `transportSecurity` and `eventLoopGroup` **must** be compatible.
  40. /// >
  41. /// > For ``GRPCChannelPool/Configuration/TransportSecurity-swift.struct/tls(_:)`` the allowed
  42. /// > `EventLoopGroup`s depends on the value of ``GRPCTLSConfiguration``. If a TLS configuration
  43. /// > is known ahead of time, ``PlatformSupport/makeEventLoopGroup(compatibleWith:loopCount:)``
  44. /// > may be used to construct a compatible `EventLoopGroup`.
  45. /// >
  46. /// > If the `EventLoopGroup` is known ahead of time then a default TLS configuration may be
  47. /// > constructed with ``GRPCTLSConfiguration/makeClientDefault(compatibleWith:)``.
  48. /// >
  49. /// > For ``GRPCChannelPool/Configuration/TransportSecurity-swift.struct/plaintext`` transport
  50. /// > security both `MultiThreadedEventLoopGroup` and `NIOTSEventLoopGroup` (and `EventLoop`s
  51. /// > from either) may be used.
  52. ///
  53. /// - Parameters:
  54. /// - target: The target to connect to.
  55. /// - transportSecurity: Transport layer security for connections.
  56. /// - eventLoopGroup: The `EventLoopGroup` to run connections on.
  57. /// - configure: A closure which may be used to modify defaulted configuration before
  58. /// constructing the ``GRPCChannel``.
  59. /// - Throws: If it is not possible to construct an SSL context. This will never happen when
  60. /// using the ``GRPCChannelPool/Configuration/TransportSecurity-swift.struct/plaintext``
  61. /// transport security.
  62. /// - Returns: A ``GRPCChannel``.
  63. public static func with(
  64. target: ConnectionTarget,
  65. transportSecurity: GRPCChannelPool.Configuration.TransportSecurity,
  66. eventLoopGroup: EventLoopGroup,
  67. _ configure: (inout GRPCChannelPool.Configuration) -> Void = { _ in }
  68. ) throws -> GRPCChannel {
  69. let configuration = GRPCChannelPool.Configuration.with(
  70. target: target,
  71. transportSecurity: transportSecurity,
  72. eventLoopGroup: eventLoopGroup,
  73. configure
  74. )
  75. return try PooledChannel(configuration: configuration)
  76. }
  77. /// See ``GRPCChannelPool/with(target:transportSecurity:eventLoopGroup:_:)``.
  78. public static func with(
  79. configuration: GRPCChannelPool.Configuration
  80. ) throws -> GRPCChannel {
  81. return try PooledChannel(configuration: configuration)
  82. }
  83. }
  84. extension GRPCChannelPool {
  85. public struct Configuration {
  86. private init(
  87. target: ConnectionTarget,
  88. transportSecurity: TransportSecurity,
  89. eventLoopGroup: EventLoopGroup
  90. ) {
  91. self.target = target
  92. self.transportSecurity = transportSecurity
  93. self.eventLoopGroup = eventLoopGroup
  94. }
  95. // Note: we use `configure` blocks to avoid having to add new initializers when properties are
  96. // added to the configuration while allowing the configuration to be constructed as a constant.
  97. /// Construct and configure a ``GRPCChannelPool/Configuration``.
  98. ///
  99. /// - Parameters:
  100. /// - target: The target to connect to.
  101. /// - transportSecurity: Transport layer security for connections. Note that the value of
  102. /// `eventLoopGroup` must be compatible with the value
  103. /// - eventLoopGroup: The `EventLoopGroup` to run connections on.
  104. /// - configure: A closure which may be used to modify defaulted configuration.
  105. public static func with(
  106. target: ConnectionTarget,
  107. transportSecurity: TransportSecurity,
  108. eventLoopGroup: EventLoopGroup,
  109. _ configure: (inout Configuration) -> Void = { _ in }
  110. ) -> Configuration {
  111. var configuration = Configuration(
  112. target: target,
  113. transportSecurity: transportSecurity,
  114. eventLoopGroup: eventLoopGroup
  115. )
  116. configure(&configuration)
  117. return configuration
  118. }
  119. /// The target to connect to.
  120. public var target: ConnectionTarget
  121. /// Connection security.
  122. public var transportSecurity: TransportSecurity
  123. /// The `EventLoopGroup` used by the connection pool.
  124. public var eventLoopGroup: EventLoopGroup
  125. /// Connection pool configuration.
  126. public var connectionPool: ConnectionPool = .defaults
  127. /// HTTP/2 configuration.
  128. public var http2: HTTP2 = .defaults
  129. /// The connection backoff configuration.
  130. public var connectionBackoff = ConnectionBackoff()
  131. /// The amount of time to wait before closing the connection. The idle timeout will start only
  132. /// if there are no RPCs in progress and will be cancelled as soon as any RPCs start.
  133. ///
  134. /// If a connection becomes idle, starting a new RPC will automatically create a new connection.
  135. public var idleTimeout = TimeAmount.minutes(30)
  136. /// The connection keepalive configuration.
  137. public var keepalive = ClientConnectionKeepalive()
  138. /// The maximum size in bytes of a message which may be received from a server. Defaults to 4MB.
  139. ///
  140. /// Any received messages whose size exceeds this limit will cause RPCs to fail with
  141. /// a `.resourceExhausted` status code.
  142. public var maximumReceiveMessageLength: Int = 4 * 1024 * 1024 {
  143. willSet {
  144. precondition(newValue >= 0, "maximumReceiveMessageLength must be positive")
  145. }
  146. }
  147. /// A channel initializer which will be run after gRPC has initialized each `NIOCore.Channel`.
  148. /// This may be used to add additional handlers to the pipeline and is intended for debugging.
  149. ///
  150. /// - Warning: The initializer closure may be invoked *multiple times*.
  151. public var debugChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?
  152. /// An error delegate which is called when errors are caught.
  153. public var errorDelegate: ClientErrorDelegate?
  154. /// A logger used for background activity, such as connection state changes.
  155. public var backgroundActivityLogger = Logger(
  156. label: "io.grpc",
  157. factory: { _ in
  158. return SwiftLogNoOpLogHandler()
  159. }
  160. )
  161. }
  162. }
  163. extension GRPCChannelPool.Configuration {
  164. public struct TransportSecurity {
  165. private init(_ configuration: GRPCTLSConfiguration?) {
  166. self.tlsConfiguration = configuration
  167. }
  168. /// The TLS configuration used. A `nil` value means that no TLS will be used and
  169. /// communication at the transport layer will be plaintext.
  170. public var tlsConfiguration: Optional<GRPCTLSConfiguration>
  171. /// Secure the transport layer with TLS.
  172. ///
  173. /// The TLS backend used depends on the value of `configuration`. See ``GRPCTLSConfiguration``
  174. /// for more details.
  175. ///
  176. /// > Important: the value of `configuration` **must** be compatible with
  177. /// > ``GRPCChannelPool/Configuration/eventLoopGroup``. See the documentation of
  178. /// > ``GRPCChannelPool/with(target:transportSecurity:eventLoopGroup:_:)`` for more details.
  179. public static func tls(_ configuration: GRPCTLSConfiguration) -> TransportSecurity {
  180. return TransportSecurity(configuration)
  181. }
  182. /// Insecure plaintext communication.
  183. public static let plaintext = TransportSecurity(nil)
  184. }
  185. }
  186. extension GRPCChannelPool.Configuration {
  187. public struct HTTP2: Hashable {
  188. private static let allowedTargetWindowSizes = (1 ... Int(Int32.max))
  189. private static let allowedMaxFrameSizes = (1 << 14) ... ((1 << 24) - 1)
  190. /// Default HTTP/2 configuration.
  191. public static let defaults = HTTP2()
  192. public static func with(_ configure: (inout HTTP2) -> Void) -> HTTP2 {
  193. var configuration = Self.defaults
  194. configure(&configuration)
  195. return configuration
  196. }
  197. /// The HTTP/2 max frame size. Defaults to 8MB. Values are clamped between 2^14 and 2^24-1
  198. /// octets inclusive (RFC 7540 § 4.2).
  199. public var targetWindowSize = 8 * 1024 * 1024 {
  200. didSet {
  201. self.targetWindowSize = self.targetWindowSize.clamped(to: Self.allowedTargetWindowSizes)
  202. }
  203. }
  204. /// The HTTP/2 max frame size. Defaults to 16384. Value is clamped between 2^14 and 2^24-1
  205. /// octets inclusive (the minimum and maximum allowable values - HTTP/2 RFC 7540 4.2).
  206. public var maxFrameSize: Int = 16384 {
  207. didSet {
  208. self.maxFrameSize = self.maxFrameSize.clamped(to: Self.allowedMaxFrameSizes)
  209. }
  210. }
  211. }
  212. }
  213. extension GRPCChannelPool.Configuration {
  214. public struct ConnectionPool: Hashable {
  215. /// Default connection pool configuration.
  216. public static let defaults = ConnectionPool()
  217. public static func with(_ configure: (inout ConnectionPool) -> Void) -> ConnectionPool {
  218. var configuration = Self.defaults
  219. configure(&configuration)
  220. return configuration
  221. }
  222. /// The maximum number of connections per `EventLoop` that may be created at a given time.
  223. ///
  224. /// Defaults to 1.
  225. public var connectionsPerEventLoop: Int = 1
  226. /// The maximum number of callers which may be waiting for a stream at any given time on a
  227. /// given `EventLoop`.
  228. ///
  229. /// Any requests for a stream which would cause this limit to be exceeded will be failed
  230. /// immediately.
  231. ///
  232. /// Defaults to 100.
  233. public var maxWaitersPerEventLoop: Int = 100
  234. /// The maximum amount of time a caller is willing to wait for a stream for before timing out.
  235. ///
  236. /// Defaults to 30 seconds.
  237. public var maxWaitTime: TimeAmount = .seconds(30)
  238. /// The threshold which, if exceeded, when creating a stream determines whether the pool will
  239. /// establish another connection (if doing so will not violate ``connectionsPerEventLoop``).
  240. ///
  241. /// The 'load' is calculated as the ratio of demand for streams (the sum of the number of
  242. /// waiters and the number of reserved streams) and the total number of streams which each
  243. /// thread _could support.
  244. public var reservationLoadThreshold: Double = 0.9
  245. }
  246. }