Skip to content

Instantly share code, notes, and snippets.

@n4to4
Last active May 24, 2018 09:36
Show Gist options
  • Save n4to4/9f2a10dc816cc6eb1c227004237143ac to your computer and use it in GitHub Desktop.
Save n4to4/9f2a10dc816cc6eb1c227004237143ac to your computer and use it in GitHub Desktop.
http4s question
scalaVersion := "2.12.6"
scalacOptions ++= Seq(
"-deprecation", // Emit warning and location for usages of deprecated APIs.
"-encoding", "utf-8", // Specify character encoding used by source files.
"-explaintypes", // Explain type errors in more detail.
"-feature", // Emit warning and location for usages of features that should be imported explicitly.
"-language:existentials", // Existential types (besides wildcard types) can be written and inferred
"-language:experimental.macros", // Allow macro definition (besides implementation and application)
"-language:higherKinds", // Allow higher-kinded types
"-language:implicitConversions", // Allow definition of implicit functions called views
"-unchecked", // Enable additional warnings where generated code depends on assumptions.
"-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access.
//"-Xfatal-warnings", // Fail the compilation if there are any warnings.
"-Xfuture", // Turn on future language features.
"-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver.
"-Xlint:by-name-right-associative", // By-name parameter of right associative operator.
"-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error.
"-Xlint:delayedinit-select", // Selecting member of DelayedInit.
"-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element.
"-Xlint:inaccessible", // Warn about inaccessible types in method signatures.
"-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`.
"-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id.
"-Xlint:nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'.
"-Xlint:nullary-unit", // Warn when nullary methods return Unit.
"-Xlint:option-implicit", // Option.apply used implicit view.
"-Xlint:package-object-classes", // Class or object defined in package object.
"-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds.
"-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field.
"-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component.
"-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope.
"-Xlint:unsound-match", // Pattern match may not be typesafe.
"-Yno-adapted-args", // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver.
"-Ypartial-unification", // Enable partial unification in type constructor inference
"-Ywarn-dead-code", // Warn when dead code is identified.
"-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined.
"-Ywarn-inaccessible", // Warn about inaccessible types in method signatures.
"-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`.
"-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'.
"-Ywarn-nullary-unit", // Warn when nullary methods return Unit.
"-Ywarn-numeric-widen", // Warn when numerics are widened.
"-Ywarn-unused:implicits", // Warn if an implicit parameter is unused.
"-Ywarn-unused:imports", // Warn if an import selector is not referenced.
"-Ywarn-unused:locals", // Warn if a local definition is unused.
"-Ywarn-unused:params", // Warn if a value parameter is unused.
"-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused.
"-Ywarn-unused:privates", // Warn if a private member is unused.
"-Ywarn-value-discard" // Warn when non-Unit expression results are unused.
)
lazy val http4sVersion = "0.18.11"
lazy val sttpVersion = "1.1.12"
resolvers ++= Seq(Resolver.sonatypeRepo("releases"))
resolvers ++= Seq(Resolver.sonatypeRepo("snapshots"))
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "1.1.0",
"org.typelevel" %% "cats-effect" % "0.10",
"org.http4s" %% "http4s-blaze-client" % http4sVersion,
"org.http4s" %% "http4s-blaze-server" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
"org.http4s" %% "http4s-dsl" % http4sVersion,
"io.monix" %% "monix" % "3.0.0-RC1",
"com.softwaremill.sttp" %% "core" % sttpVersion,
"com.softwaremill.sttp" %% "async-http-client-backend-cats" % sttpVersion,
"com.softwaremill.sttp" %% "async-http-client-backend-fs2" % sttpVersion,
"ch.qos.logback" % "logback-classic" % "1.2.3",
"org.wvlet.airframe" %% "airframe-log" % "0.43",
"io.gatling.highcharts" % "gatling-charts-highcharts" % "2.3.0" % Test,
"io.gatling" % "gatling-test-framework" % "2.3.0" % Test
)
package sandbox.load
import io.gatling.core.Predef._
import io.gatling.core.scenario.Simulation
import io.gatling.http.Predef._
import scala.concurrent.duration._
class LoadTest extends Simulation {
val httpConf = http.baseURL("http://127.0.0.1:8000")
val json = scala.io.Source.fromFile("/path/to/request.json").mkString
val getHomeScenario =
scenario("API")
.exec(http("post").post("/single").body(StringBody(json)).asJSON)
setUp(
getHomeScenario.inject(constantUsersPerSec(1000) during (10.seconds))
).protocols(httpConf)
}
/*
Success: getHomeScenario.inject(constantUsersPerSec(500) during (10.seconds))
================================================================================
---- Global Information --------------------------------------------------------
> request count 5000 (OK=5000 KO=0 )
> min response time 10 (OK=10 KO=- )
> max response time 438 (OK=438 KO=- )
> mean response time 61 (OK=61 KO=- )
> std deviation 75 (OK=75 KO=- )
> response time 50th percentile 20 (OK=20 KO=- )
> response time 75th percentile 77 (OK=77 KO=- )
> response time 95th percentile 228 (OK=228 KO=- )
> response time 99th percentile 320 (OK=320 KO=- )
> mean requests/sec 454.545 (OK=454.545 KO=- )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms 5000 (100%)
> 800 ms < t < 1200 ms 0 ( 0%)
> t > 1200 ms 0 ( 0%)
> failed 0 ( 0%)
================================================================================
*/
/*
Failue: getHomeScenario.inject(constantUsersPerSec(1000) during (10.seconds))
17:45:39.758 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.759 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.759 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.760 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.760 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.761 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.761 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.762 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.762 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.764 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.765 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.766 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.767 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.779 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.779 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.725 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.750 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.781 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.781 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.800 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.800 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.801 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:39.808 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:41.184 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:41.185 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
17:45:41.186 [WARN ] i.g.h.a.ResponseProcessor - Request 'post' failed: j.n.ConnectException: connection timed out: /127.0.0.1:8000
================================================================================
---- Global Information --------------------------------------------------------
> request count 10000 (OK=9835 KO=165 )
> min response time 0 (OK=42 KO=0 )
> max response time 15946 (OK=15946 KO=0 )
> mean response time 4854 (OK=4935 KO=0 )
> std deviation 3069 (OK=3029 KO=0 )
> response time 50th percentile 4581 (OK=4639 KO=0 )
> response time 75th percentile 6642 (OK=6679 KO=0 )
> response time 95th percentile 10640 (OK=10660 KO=0 )
> response time 99th percentile 12582 (OK=12585 KO=0 )
> mean requests/sec 588.235 (OK=578.529 KO=9.706 )
---- Response Time Distribution ------------------------------------------------
> t < 800 ms 448 ( 4%)
> 800 ms < t < 1200 ms 503 ( 5%)
> t > 1200 ms 8884 ( 89%)
> failed 165 ( 2%)
---- Errors --------------------------------------------------------------------
> j.n.ConnectException: connection timed out: /127.0.0.1:8000 162 (98.18%)
> o.a.e.RemotelyClosedException: Remotely closed 3 ( 1.82%)
================================================================================
*/
package sandbox
import cats.data.Kleisli
import cats.effect.IO
import cats.implicits._
import fs2.{Stream, StreamApp}
import fs2.StreamApp.ExitCode
import org.http4s._
import org.http4s.client.Client
import org.http4s.client.blaze._
import org.http4s.dsl.Http4sDsl
import org.http4s.headers._
import org.http4s.server.blaze.BlazeBuilder
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
object Server extends ServerApp
abstract class ServerApp extends StreamApp[IO] with Http4sDsl[IO] {
import com.softwaremill.sttp.asynchttpclient.cats._
implicit val sttpBackend = AsyncHttpClientCatsBackend[IO]()
val time = 300.millis
override def stream(args: List[String], requestShutdown: IO[Unit]): Stream[IO, ExitCode] =
for {
// client <- Http1Client.stream[IO](
// BlazeClientConfig.defaultConfig.copy(
// maxTotalConnections = 5000,
// maxConnectionsPerRequestKey = _ => 5000,
// maxWaitQueueLimit = 5000,
// // requestTimeout = time,
// // responseHeaderTimeout = time,
// // idleTimeout = time
// ))
exitCode <- BlazeBuilder[IO]
.bindHttp(8000, "0.0.0.0")
.mountService(myService(null), "/")
.withIdleTimeout(time)
.serve
_ <- Stream.eval(IO(sttpBackend.close()))
} yield exitCode
def myService(client: Client[IO]): HttpService[IO] = HttpService[IO] {
case req @ POST -> Root / "single" =>
val body = internal(client).run(req)
Response[IO]().withBody(body)
case req @ POST -> Root / "parallel" =>
// this is `List` because there are multiple internal servers
val ios = List(internal(client)).map(_.run(req))
// this should be processed in parallel with timeout
val responsesIO = fs2.async.parallelTraverse(ios)(_.attempt)
responsesIO.flatMap { resps =>
val rs = resps.map {
case Right(r) =>
Response[IO]().withBody(r)
case Left(e) =>
IO(println("*** ERROR: " + e.getMessage)) *> NoContent()
}
// this is supposed to be selected by its result
rs.head
}
}
def internal(client: Client[IO]): Kleisli[IO, Request[IO], String] =
Kleisli { req =>
import com.softwaremill.sttp._
for {
bytes <- req.body.compile.toVector
resp <- sttp
.post(uri"http://otherapi/api")
.contentType("application/json")
.body(bytes.toArray)
.send()
.map(_.body.leftMap(new Throwable(_)))
.rethrow
} yield resp
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment