From 64f794af1611c8c57aec400737a6a775c9427de5 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Wed, 11 Dec 2019 17:33:59 +0100 Subject: [PATCH 1/3] feat: Add grpc-server module with PureConfig integration --- build.sbt | 23 ++++++++++ .../server/pureconfig/ConfigReaders.scala | 11 +++++ .../grpc/server/pureconfig/implicits.scala | 20 +++++++++ .../sst/grpc/server/GrpcServerConfig.scala | 11 +++++ .../sst/grpc/server/GrpcServerModule.scala | 43 +++++++++++++++++++ project/Dependencies.scala | 4 ++ 6 files changed, 112 insertions(+) create mode 100644 grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/ConfigReaders.scala create mode 100644 grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/implicits.scala create mode 100644 grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerConfig.scala create mode 100644 grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala diff --git a/build.sbt b/build.sbt index adb911759..4684a47a7 100644 --- a/build.sbt +++ b/build.sbt @@ -27,6 +27,8 @@ lazy val root = project example, flyway, flywayPureConfig, + grpcServer, + grpcServerPureConfig, http4sClientBlaze, http4sClientBlazePureConfig, http4sClientMonixCatnap, @@ -168,6 +170,27 @@ lazy val flywayPureConfig = project libraryDependencies += Dependencies.pureConfig ) +lazy val grpcServer = project + .in(file("grpc-server")) + .settings(commonSettings) + .settings( + name := "sst-grpc-server", + libraryDependencies ++= Seq( + Dependencies.grpcNettyShaded, + Dependencies.grpcProtobuf, + Dependencies.grpcStub + ) + ) + +lazy val grpcServerPureConfig = project + .in(file("grpc-server-pureconfig")) + .dependsOn(grpcServer) + .settings(commonSettings) + .settings( + name := "sst-grpc-server-pureconfig", + libraryDependencies += Dependencies.pureConfig + ) + lazy val http4sClientBlaze = project .in(file("http4s-client-blaze")) .settings(commonSettings) diff --git a/grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/ConfigReaders.scala b/grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/ConfigReaders.scala new file mode 100644 index 000000000..3b2e15192 --- /dev/null +++ b/grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/ConfigReaders.scala @@ -0,0 +1,11 @@ +package com.avast.sst.grpc.server.pureconfig + +import com.avast.sst.grpc.server.GrpcServerConfig +import pureconfig.ConfigReader +import pureconfig.generic.semiauto.deriveReader + +trait ConfigReaders { + + implicit val grpcServerGrpcServerConfigReader: ConfigReader[GrpcServerConfig] = deriveReader + +} diff --git a/grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/implicits.scala b/grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/implicits.scala new file mode 100644 index 000000000..5e8bb0f37 --- /dev/null +++ b/grpc-server-pureconfig/src/main/scala/com/avast/sst/grpc/server/pureconfig/implicits.scala @@ -0,0 +1,20 @@ +package com.avast.sst.grpc.server.pureconfig + +import pureconfig.ConfigFieldMapping +import pureconfig.generic.ProductHint + +/** Contains [[pureconfig.ConfigReader]] instances with default "kebab-case" naming convention. */ +object implicits extends ConfigReaders { + + /** Contains [[pureconfig.ConfigReader]] instances with "kebab-case" naming convention. + * + * This is alias for the default `implicits._` import. + */ + object KebabCase extends ConfigReaders + + /** Contains [[pureconfig.ConfigReader]] instances with "camelCase" naming convention. */ + object CamelCase extends ConfigReaders { + implicit def hint[T]: ProductHint[T] = ProductHint(ConfigFieldMapping(pureconfig.CamelCase, pureconfig.CamelCase)) + } + +} diff --git a/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerConfig.scala b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerConfig.scala new file mode 100644 index 000000000..4a656a541 --- /dev/null +++ b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerConfig.scala @@ -0,0 +1,11 @@ +package com.avast.sst.grpc.server + +import java.util.concurrent.TimeUnit + +import scala.concurrent.duration.Duration + +final case class GrpcServerConfig(port: Int, + handshakeTimeout: Duration, + maxInboundMessageSize: Int = 4 * 1024 * 1024, + maxInboundMetadataSize: Int = 8192, + serverShutdownTimeout: Duration = Duration(10, TimeUnit.SECONDS)) diff --git a/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala new file mode 100644 index 000000000..ea3544be5 --- /dev/null +++ b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala @@ -0,0 +1,43 @@ +package com.avast.sst.grpc.server + +import java.util.concurrent.TimeUnit + +import cats.effect.{Resource, Sync} +import io.grpc.{Server, ServerBuilder, ServerInterceptor, ServerServiceDefinition} + +import scala.collection.immutable.Seq +import scala.concurrent.ExecutionContext +object GrpcServerModule { + + /** Makes [[io.grpc.Server]] (Netty) initialized with the given config, services and interceptors. + * + * @param services service implementations to be added to the handler registry + * @param executionContext executor to be used for the server + * @param globalInterceptors that are run for all the services + */ + def make[F[_]: Sync](config: GrpcServerConfig, + services: Seq[ServerServiceDefinition], + executionContext: ExecutionContext, + globalInterceptors: Seq[ServerInterceptor] = List.empty): Resource[F, Server] = + Resource.make { + Sync[F].delay { + val builder = ServerBuilder + .forPort(config.port) + .handshakeTimeout(config.handshakeTimeout.toMillis, TimeUnit.MILLISECONDS) + .maxInboundMessageSize(config.maxInboundMessageSize) + .maxInboundMetadataSize(config.maxInboundMetadataSize) + .executor(executionContext.execute) + + services.foreach(builder.addService) + globalInterceptors.foreach(builder.intercept) + + builder.build.start() + } + } { s => + Sync[F].delay { + s.shutdown().awaitTermination(config.serverShutdownTimeout.toMillis, TimeUnit.MILLISECONDS) + () + } + } + +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 175890386..9f862e908 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -7,6 +7,9 @@ object Dependencies { val doobie = "org.tpolecat" %% "doobie-core" % Versions.doobie val doobieHikari = "org.tpolecat" %% "doobie-hikari" % Versions.doobie val flywayCore = "org.flywaydb" % "flyway-core" % "6.1.2" + val grpcNettyShaded = "io.grpc" % "grpc-netty-shaded" % Versions.grpc + val grpcProtobuf = "io.grpc" % "grpc-protobuf" % Versions.grpc + val grpcStub = "io.grpc" % "grpc-stub" % Versions.grpc val http4sBlazeClient = "org.http4s" %% "http4s-blaze-client" % Versions.http4s val http4sBlazeServer = "org.http4s" %% "http4s-blaze-server" % Versions.http4s val http4sClient = "org.http4s" %% "http4s-client" % Versions.http4s @@ -35,6 +38,7 @@ object Dependencies { val datastaxJavaDriverCore = "4.3.1" val doobie = "0.7.1" + val grpc = "1.25.0" val http4s = "0.20.15" val micrometerCore = "1.3.2" val micrometerJmx = "1.3.2" From dbb64668c712e0d63ad7a967a56abb08230b8601 Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Wed, 11 Dec 2019 17:52:49 +0100 Subject: [PATCH 2/3] fix: Disable WartRemover NonUnitStatements (false positive) --- .../main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala index ea3544be5..6bcf1e626 100644 --- a/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala +++ b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala @@ -15,6 +15,7 @@ object GrpcServerModule { * @param executionContext executor to be used for the server * @param globalInterceptors that are run for all the services */ + @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements")) def make[F[_]: Sync](config: GrpcServerConfig, services: Seq[ServerServiceDefinition], executionContext: ExecutionContext, From 09cbda95e2f098970fae6ba96160a283e991fc4e Mon Sep 17 00:00:00 2001 From: Janecek Jakub Date: Mon, 16 Dec 2019 10:37:13 +0100 Subject: [PATCH 3/3] refactor: Rename method arg --- .../scala/com/avast/sst/grpc/server/GrpcServerModule.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala index 6bcf1e626..1c95afd2c 100644 --- a/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala +++ b/grpc-server/src/main/scala/com/avast/sst/grpc/server/GrpcServerModule.scala @@ -13,13 +13,13 @@ object GrpcServerModule { * * @param services service implementations to be added to the handler registry * @param executionContext executor to be used for the server - * @param globalInterceptors that are run for all the services + * @param interceptors that are run for all the services */ @SuppressWarnings(Array("org.wartremover.warts.NonUnitStatements")) def make[F[_]: Sync](config: GrpcServerConfig, services: Seq[ServerServiceDefinition], executionContext: ExecutionContext, - globalInterceptors: Seq[ServerInterceptor] = List.empty): Resource[F, Server] = + interceptors: Seq[ServerInterceptor] = List.empty): Resource[F, Server] = Resource.make { Sync[F].delay { val builder = ServerBuilder @@ -30,7 +30,7 @@ object GrpcServerModule { .executor(executionContext.execute) services.foreach(builder.addService) - globalInterceptors.foreach(builder.intercept) + interceptors.foreach(builder.intercept) builder.build.start() }