Docker not forwarding http requests - scala

I am running a Scala microservice application in my local docker container and trying to test it manually by using Postman but I am constantly getting a socket error:
Error: socket hang up
My application listens to 8080 port as main function contains:
val bindingFuture = Http()
.newServerAt("localhost", 8080)
.bind(userRoute.route)
I am running my container by forwarding 8080 to 8080:
docker run -i -t -d -p 8080:8080 app-core:0.1.0-SNAPSHOT
In container logs I see my application is up and running. Also, when I check with netstat, I see Docker is indeed listening to the 8080 port:
netstat -na | Select-String "8080"
TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING
TCP 127.0.0.1:8080 127.0.0.1:59087 TIME_WAIT
TCP 127.0.0.1:8080 127.0.0.1:59088 TIME_WAIT
TCP [::]:8080 [::]:0 LISTENING
TCP [::1]:8080 [::]:0 LISTENING
But for some reason, my http request is not being forwarded to the application. Any ideas?
Postman ss:
Main.scala:
object Main extends App {
implicit val system = ActorSystem(Behaviors.empty, "hb-core-service-system")
implicit val executionContext = system.executionContext
val db = Database.forConfig("mydb")
val userCommand: UserCommand = new UserCommand(db)
val userRoute = new UserRoute(userCommand)
val bindingFuture = Http()
.newServerAt("127.0.0.1", 8080)
.bind(userRoute.route)
println(
s"Server now online.\nPress RETURN to stop..."
)
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
UserRoute.scala:
class UserRoute(
user: UserCommand
)(implicit ec: ExecutionContext)
extends JsonFormats {
def route: Route =
pathPrefix("api" / "core-service") {
Directives.concat(
(path("create-user") & post)(createUser)
}
private def createUser: Route =
entity(Directives.as[UserDto]) { dto =>
complete {
user.createUser(dto).map {
case Some(id) =>
id.asJson
case None =>
StatusCodes.BadRequest
}
}
}
}

After hours of research I've realized the problem is basically with the following binding:
val bindingFuture = Http()
.newServerAt("localhost", 8080)
.bind(userRoute.route)
It works perfectly until you put the application inside a docker container. When I changed "localhost" with "0.0.0.0", it started working as expected.

Related

Akka Stream, Tcp().bind, handle when the client close the socket

I'm quite new in Akka Stream and I'd like to learn how handle a TCP socket for a project of mine. I took this piece of code from the Akka Stream official documentation.
import akka.stream.scaladsl.Framing
val connections: Source[IncomingConnection, Future[ServerBinding]] =
Tcp().bind(host, port)
connections.runForeach { connection =>
println(s"New connection from: ${connection.remoteAddress}")
val echo = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(_.utf8String)
.map(_ + "!!!\n")
.map(ByteString(_))
connection.handleWith(echo)
}
If I connect from the terminal using netcat I can see that the Akka Stream TCP socket works as expected. I also found out that If I need to close the connection using an user message, I can use a takeWhile as follow
import akka.stream.scaladsl.Framing
val connections: Source[IncomingConnection, Future[ServerBinding]] =
Tcp().bind(host, port)
connections.runForeach { connection =>
println(s"New connection from: ${connection.remoteAddress}")
val echo = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(_.utf8String)
.takeWhile(_.toLowerCase.trim != "exit") // < - - - - - - HERE
.map(_ + "!!!\n")
.map(ByteString(_))
connection.handleWith(echo)
}
What I can't find is how to manage a socket closed by a CMD + C action. Akka Stream use Akka.io to manage the TCP connection internally, so it must send some of its PeerClose messages when the socket is closed. So, my understanding of Akka.io tells me that I should receive a feedback from the socket closing but I can't find how to do that with Akka Stream. Is there a way to manage that ?
connection.handleWith(echo) is syntactic sugar for connection.flow.joinMat(echo)(Keep.right).run() which will have the materialized value of echo, which is generally not useful. Flow.via.map.takeWhile has NotUsed as a materialized value, so that's also basically useless. However, you can attach stages to echo which will materialize differently.
One of these is .watchTermination:
connections.runForeach { connection =>
println(s"New connection from: ${connection.remoteAddress}")
val echo: Flow[ByteString, ByteString, Future[Done]] = Flow[ByteString]
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256, allowTruncation = true))
.map(_.utf8String)
.takeWhile(_.toLowerCase.trim != "exit") // < - - - - - - HERE
.map(_ + "!!!\n")
.map(ByteString(_))
// change the materialized value to a Future[Done]
.watchTermination()(Keep.right)
// you may need to have an implicit ExecutionContext in scope, e.g. system.dispatcher,
// if you don't already
connection.handleWith(echo).onComplete {
case Success(_) => println("stream completed successfully")
case Failure(e) => println(e.getMessage)
}
}
This will not distinguish between your side or the remote side closing the connection normally; it will distinguish the stream failing.

how to read tcp stream using scala

I have a java jar that generates the tcp stream on particular port.
I cam run the using java command like java -jar runner.jar and this start producing the stream of message on port 8888.
When I do nc -l 8888 I can see the messages.
I want to read this stream using scala and another framework or tool like akka, akka-stream.
Can anyone help me understand the best tool, framework or any other technique to read this tcp stream.
I tried using akka stream with following code :-
implicit val system = ActorSystem()
implicit val mater = ActorMaterializer() val ss = Tcp().outgoingConnection("127.0.0.1", 8888)
.to(Sink.foreach(println(_)))
Source.empty.to(ss).run()
I also tried
Tcp().outgoingConnection(new InetSocketAddress("127.0.0.1", 8888))
.runWith(Source.maybe[ByteString], Sink.foreach(bs => println(bs.utf8String)))
This doesn't work.
I only need to read the messages and process on my own.
Thanks
As I understand you want to setup TCP server, here is example of TCP Echo using akka streams
def server(system: ActorSystem, address: String, port: Int): Unit = {
implicit val sys = system
import system.dispatcher
implicit val materializer = ActorMaterializer()
val handler = Sink.foreach[Tcp.IncomingConnection] { conn =>
println("Client connected from: " + conn.remoteAddress)
conn handleWith Flow[ByteString]
}
val connections = Tcp().bind(address, port)
val binding = connections.to(handler).run()
binding.onComplete {
case Success(b) =>
println("Server started, listening on: " + b.localAddress)
case Failure(e) =>
println(s"Server could not bind to $address:$port: ${e.getMessage}")
system.terminate()
}
}

Shut down Akka HTTP app

I have a running Akka HTTP application and I want to shut it down.
Pressing Ctrl + C in SBT does not work for me (my shell is currently Git Bash for Windows).
What's the recommended way of shutting down an Akka app gracefully?
Taking inspiration from this thread, I added a route to my application that shuts down the application:
def shutdownRoute: Route = path("shutdown") {
Http().shutdownAllConnectionPools() andThen { case _ => system.terminate() }
complete("Shutting down app")
}
where system is the app's ActorSystem.
Given this route, I can now shut down my application with
curl http://localhost:5000/shutdown
Edit:
Being able to shut down a server remotely is not a good idea for production code. In the comments, Henrik pointed to a different way that shuts down the server by hitting Enter in the SBT console:
StdIn.readLine()
// Unbind from the port and shut down when done
bindingFuture
.flatMap(_.unbind())
.onComplete(_ => system.terminate())
For context, I put the above code at the end of server initialization:
// Gets the host and a port from the configuration
val host = system.settings.config.getString("http.host")
val port = system.settings.config.getInt("http.port")
implicit val materializer = ActorMaterializer()
// bindAndHandle requires an implicit ExecutionContext
implicit val ec = system.dispatcher
import akka.http.scaladsl.server.Directives._
val route = path("hi") {
complete("How's it going?")
}
// Starts the HTTP server
val bindingFuture: Future[ServerBinding] =
Http().bindAndHandle(route, host, port)
val log = Logging(system.eventStream, "my-application")
bindingFuture.onComplete {
case Success(serverBinding) =>
log.info(s"Server bound to ${serverBinding.localAddress}")
case Failure(ex) =>
log.error(ex, "Failed to bind to {}:{}!", host, port)
system.terminate()
}
log.info("Press enter key to stop...")
// Let the application run until we press the enter key
StdIn.readLine()
// Unbind from the port and shut down when done
bindingFuture
.flatMap(_.unbind())
.onComplete(_ => system.terminate())

How to run an Akka-HTTP server inside an Akka cluster?

I am building an Akka cluster and want to use Akka-HTTP server as an API server inside. How does one do that?
I would imagine it will be a cluster singleton, as it is an access point to the cluster, but making it an actor seems weird, as it will need to have a receive() method, which will do nothing (i guess).
Simplest example:
implicit val system = ... // your ActorSystem goes here
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val route =
path("hello") {
get {
complete(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "Hello World !"))
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
To stop:
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done

Get Simple Remote Akka Application Running

I am trying to set up a simple server/client akka (using Akka 2.0.3) application, but it failed to connect. Beforehand here is the basic code:
import com.typesafe.config.ConfigFactory
import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.Props
class Server extends Actor {
def receive = {
case s: String => println("Got " + s)
}
}
val serverSystem = ActorSystem("server", ConfigFactory.load(ConfigFactory.parseString("""
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "localhost"
port = 5678
}
}
}
""")))
val server = serverSystem.actorOf(Props[Server], name = "server")
Thread.sleep(500)
println("started")
Thread.sleep(500)
val clientSystem = ActorSystem("client", ConfigFactory.load(ConfigFactory.parseString("""
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
}
""")))
val remoteServer = clientSystem.actorFor("akka://server#XXX:5678/user/server")
remoteServer ! "HEY"
Thread.sleep(3000)
clientSystem.shutdown
serverSystem.shutdown
I know that the configurations should be placed in external files.
If you replace XXX with localhost it works:
started
Got HEY
But if I used my external (resolved) IP (PC behind home router) for XXX the HEY message never arrives. I thought it is due to some firewall problem and forwarded the related TCP and UDP ports at my router and also opened/allowed them at my Windows firewall. So after that the following code worked (also XXX replaced with my external IP). A started ServerTest can be connected by a ClientTest:
import java.net.ServerSocket
object ServerTest extends App {
println("START server")
val ss = new ServerSocket(5678)
val s = ss.accept()
println(s)
println("END")
}
import java.net.Socket
object ClientTest extends App {
println("START client")
val s = new Socket("XXX", 5678)
println(s)
println("END")
}
So it´s not a port/firewall problem, isn´t it?! So where is the problem???
localhost usually means 127.0.0.1, which is only one of the possibly many interfaces (cards) in a computer. The server binding to localhost won't receive connections connecting to the other interfaces (including the one with the external address).
You should either specify the external address in the server, or 0.0.0.0 which means "bind to all interfaces".