用Akka Http写一个web应用

匿名 (未验证) 提交于 2019-12-03 00:27:02

刚学Scala,其实已经蓄谋已久,看了好些资料,奈何scala太多东西,而且网上资料并不算多,官方英文资料啃得也是很难受,主要是Scala不熟悉,有时候看不懂代码。对于刚学一门语言的新手来说,写一个小Demo可以帮助理解和记忆,这里就以Scala比较流行的框架Akka Http来写一个简单的Web。

Akka Http并不是专门用来搭建Web项目,但是用来搭几个简单的接口和页面,用它也是足够的并且不会太麻烦。Akka Htpp基于Akka Actor,所以需要对Actor模型有一点了解(我也是粗略了解而已),但只要知道Actor模型的工作机制就好了。这个demo用sbt构建,所以对sbt或者maven之类的有一点了解,当然还需要Scala一点点语法基础。

目标:

  • 首先完成一个hello接口,访问就返回一个文本hello
  • 在上面基础上完成一个JSON接口
  • 完成有业务逻辑的JSON接口
  • Web服务支持静态资源的读取,可以访问html文件
  • 把应用部署到服务器

创建一个sbt项目,然后在build.sbt里加上下面的依赖

val akkaHttpV = "10.1.1"  val akkaV = "2.5.12"  libraryDependencies ++= Seq(   "com.typesafe.akka" %% "akka-actor" % akkaV,   "com.typesafe.akka" %% "akka-stream" % akkaV,   "com.typesafe.akka" %% "akka-http" % akkaHttpV,   "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV, ) 

创建一个Server Object,为了方便,直接继承HttpApp,复杂方式可以看官网例子,在重写的routes方法里定义hello接口的Route,最后写上main方法来启动Server

object Server extends HttpApp{   override protected def routes: Route = {     path(""){       get{         complete("hello")       }     }   }    def main(args: Array[String]): Unit = {     Server.startServer("localhost",8080)   } } 

这时候访问下8080端口就可以看到输出hello了。这真的很简单了,只是官网把复杂的例子放在前面,真的要学下别人框架的Get Started,越简单越好才是。

返回JSON其实可以直接用jackson,这里按照官方意见,用spray json来做,话说spray和akka http有很大姻缘呢:)

scala里面的case class真的是好东西,但是在序列化的时候就比较麻烦了。首先创建一个trait和我们的返回实体HelloDTO

case class HelloDTO(str:String,name:String)  trait JsonSupport extends SprayJsonSupport with DefaultJsonProtocol{   implicit val helloDTOFormat = jsonFormat2(HelloDTO) } 

按照官网的说法,继承SprayJsonSupport后这个trait就拥有了(反)序列化一般对象的功能,例如Array,String,Int。而我们需要做的就是为HelloDTO定义Format方法,jsonFormat2就是告诉spray json(据说它不用反射) HelloDTO有两个参数。

接着还需要把JsonSupport应用到需要(反)序列化的地方,我们直接应用给Server,另外提供一个接口返回HelloDTO,用~来lian’qi

object Server extends HttpApp with JsonSupport {    override protected def routes: Route = {     path(""){       get{         complete("hello")       }     }~     path("helloDTO"){       get{         complete(HelloDTO("hello","martin"))       }     }   }    def main(args: Array[String]): Unit = {     Server.startServer("localhost",8080)   } } 

此时,访问/helloDTO就可以看到返回的JSON数据了

上面的代码基本没业务逻辑,业务逻辑我们应该让某个Object来专门负责做。这时候轮到Akka Actor登场了

首先先创建一个HelloActor,接收参数然后返回HelloDTO

class HelloActor extends Actor{   override def receive: Receive = {     case YourName(name) =>       sender ! HelloDTO("hello",name)   } } 

然后把HelloActor注册到我们自己的actorSystem里面。然后再开一个route接收客户端传来的name参数,封装好给HelloActor处理,最后Server代码如下

...其他import import akka.pattern.ask import scala.concurrent.duration._  object Server extends HttpApp with JsonSupport {    implicit val actorSystem = ActorSystem("simple-web")   implicit val helloActor = actorSystem.actorOf(Props[HelloActor])    implicit val timeout = Timeout(5.seconds)    override protected def routes: Route = {     path(""){       get{         complete("hello")       }     }~     path("helloDTO"){       get{         complete(HelloDTO("hello","martin"))       }     }~     (get & path("helloActor")){       parameter("name"){name=>         onSuccess(helloActor ? YourName(name)){           case helloDTO:HelloDTO=>complete{helloDTO}         }       }     }   }    def main(args: Array[String]): Unit = {     //这里注册我们的system给Server     Server.startServer("localhost",8080,actorSystem)   } } 

测试curl localhost:8080/helloActor?name='Tony' 就可以看到返回的JSON里面name是Tony了

上面代码虽然可以跑,但是akka http不会自动帮我们关掉actorSystem,详情看官网

作为一个简单的Server,静态资源的支持也是很有必要的,毕竟写个小项目没有页面没什么卵用。

首先我们让src/main/resources/static作为静态资源的根目录,然后开一个static作为前缀的路由来处理静态资源

...其他路由 ~ (get & pathPrefix("static")){   getFromResourceDirectory("static") } 

这样我们的Server就可以支持静态资源了,包括static下面的js目录里面的js文件等

用Akka Http写个小工具后,应该怎么部署到服务器,由于上面的Demo完全是为了Demo而写,所以有些地方不适合直接部署,首先Server需要一直跑,不能接收回车就退出去,然后就是Server退出时顺便把actorSystem也关掉。这里只要重写Server继承HttpApp的两个方法

override protected def waitForShutdownSignal(system: ActorSystem)(implicit ec: ExecutionContext): Future[Done] = Future.never     override protected def postServerShutdown(attempt: Try[Done], system: ActorSystem): Unit = {     actorSystem.terminate()     super.postServerShutdown(attempt, system)   } 

另外Server绑定的地址不能是localhost,我就因为这个卡了好久,在生产环境中,机器可能会有多个地址,需要绑定到0.0.0.0

Server.startServer("0.0.0.0",8080,actorSystem) 

打包部署用sbt-native-packager比较好,不然每次都要跑sbt run很慢。首先在/project/plugins.sbt文件上引用插件

addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.5") 

在build.sbt里开启插件

enablePlugins(JavaAppPackaging) 

然后执行sbt stage,在/target/universal/stage/bin目录下可以找到打包后的文件,直接运行就可以了

项目地址:Github

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!