scalaakkaakka-httpakka-actor

ExecutionContext issue in Akka Http Server


Why Akka http is not using custom execution context which I defined in this code and always falls back to default execution and default thread pool?

package com.vaz.www

import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route.seal
import scala.concurrent.Future
import scala.util.Failure
import scala.util.Success
import java.util.concurrent.Executors
import scala.io.StdIn
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._

import spray.json._
import akka.actor.typed.DispatcherSelector
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContextExecutor

import org.slf4j.LoggerFactory

object MyServer {
  // implicit val ec: scala.concurrent.ExecutionContext =
  //   scala.concurrent.ExecutionContext.global // fromExecutorService(Executors.newFixedThreadPool(5))
  def main(args: Array[String]): Unit = {

    implicit val system = ActorSystem(Behaviors.empty, "Main")

    implicit val myExCon: ExecutionContextExecutor = system.dispatchers.lookup(
      DispatcherSelector.fromConfig("akka.actor.my-ex-con-dispatcher")
    )

    val logger = LoggerFactory.getLogger(getClass)
    val route = {
      withExecutionContext(myExCon) {
        path("") {
          get {
            extractExecutionContext { implicit exec =>
              println(
                s"Execution and the thread:${Thread.currentThread().getName()}"
              )
              logger.info(
                s"Execution and the thread:${Thread.currentThread().getName()}"
              )
              complete(
                HttpEntity(
                  ContentTypes.`text/html(UTF-8)`,
                  "<h1>Hi</h1>"
                )
              )
            }
          }
        } 
      }
    }

    val bindingFuture = Http().newServerAt("localhost", 9000).bind(route)
    println(
      s"Server now online. Please navigate to http://localhost:9000/hello\nPress RETURN to stop..."
    )

    StdIn.readLine()
    bindingFuture.flatMap(_.unbind()).onComplete(_ => system.terminate())
  }
}


my application conf

akka{
  actor{
    my-ex-con-dispatcher {
      type = Dispatcher
      executor = "fork-join-executor"
      fork-join-executor {
        parallelism-min = 2
        parallelism-factor = 2.0
        parallelism-max = 10
      }
      throughput = 100
    }
  }
}

the build.sbt

import Dependencies._

ThisBuild / scalaVersion     := "3.3.4"
ThisBuild / version          := "0.1.0-SNAPSHOT"
ThisBuild / organization     := "com.example"
ThisBuild / organizationName := "example"

lazy val root = (project in file("."))
  .settings(
    name := "akka-server-example",
    libraryDependencies += munit % Test
  )
val AkkaVersion = "2.9.3"
val AkkaHttpVersion = "10.6.3"
resolvers += "Akka library repository".at("https://repo.akka.io/maven")

libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion,
  "com.typesafe.akka" %% "akka-stream" % AkkaVersion,
  "com.typesafe.akka" %% "akka-http" % AkkaHttpVersion
)


when I curl localhost:9000, got the response as <h1>Hi</h1>, In the console I got this:

Server now online. Please navigate to http://localhost:9000/hello
Press RETURN to stop...
Execution and the thread:Main-akka.actor.default-dispatcher-8

I've tried reading akka [dispatcher] (https://doc.akka.io/libraries/akka/current/typed/dispatchers.html) but it is not working as expected,

  1. Why the threads falls back to defaults
  2. How to use a custom execution context in a route? so that only threads from my configuration only being used.

Solution

  • I think the documentation and directive name is a bit misleading since it sounds a bit like the nested directives will run on the given execution context.

    withExecutionContext only puts the custom execution context in the request context for nested directives that are asynchronous and that use an execution context.

    path, get and extractExecutionContext are not asynchronous. extractExecutionContext only provides a way to access the execution context from the request context to use your own logic.

    There is no directive out of the box that will run all sub-directives on a given execution context. I don't think such a directive is generally useful, but it would be possible to compose your own directive always using a future, like @earthling-paul suggested, doing that.

    Note that for non async directive trees, always having going through a future will come at a cost though.