在 Scala 中引导 Web 服务器
Posted
技术标签:
【中文标题】在 Scala 中引导 Web 服务器【英文标题】:Bootstrapping a web server in Scala 【发布时间】:2011-05-08 20:50:54 【问题描述】:使用 Python 可以做到以下几点:
$ apt-get install python
$ easy_install Flask
$ cat > hello.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
$ python hello.py
4 条命令和 7 行代码让 Web 服务器运行起来确实令人印象深刻。
Scala 等价物是什么?
【问题讨论】:
您的意思是 apt-get 存在的系统上的 4 个命令,easy_install 与 python 捆绑在一起并使用 sh shell。您是否也假设具有 root 访问权限? 是的,我还假设系统有 CPU、RAM、存储、安装的操作系统、键盘和显示器;) 这是一个严肃的问题,还是只是为了炫耀 Python+Flask 有多酷?在需要输入 15 个命令而不是 4 个命令之前,您需要设置多少次 Web 服务器是您工作中的限速步骤?我承认这很酷,但是我能想到的唯一一个需要它而不是仅仅有趣的应用程序是在一个小缓冲区中嵌入一个漏洞利用,它将目标机器转换为网络服务器。 这是一个严肃的问题。我找不到一个只需不到两个小时就可以设置好的 Scala 或 Java Web 框架。我遇到了每个问题或其他问题。我非常想将我 100% 的编码工作转移到 Java/Scala,但是用 Python 进行开发的轻松和乐趣,如上述命令序列所示,对大多数人来说仍然很有吸引力,在某种程度上包括我自己.我们如何让 Scala 变得如此简单? 好吧,Lift 花了我不到半小时的时间来设置(包括从存储库安装 maven 和诸如此类的东西),但我明白你的意思。在原始问题中包含这种动机会很有帮助。 【参考方案1】:正如David Winslow提到的,Unfiltered使用代码sn-p
信息:使用未过滤的 Scala 编写的 Apache Spark 字数统计示例的简单 API 功能。
object SimplePlan extends Plan
def intent =
case req @ GET(Path("/get")) =>
Ok ~> ResponseString(WordCount.count("Test #1: Test the Default word count program").mkString("\n"));
case req @ POST(Path("/get_custom")) =>
val custom_string = Body.string(req)
Ok ~> ResponseString(WordCount.count(custom_string).mkString("\n"))
object SimpleServer extends App
val bindingIP = SocketPortBinding(host = "localhost", port = 8080)
unfiltered.jetty.Server.portBinding(bindingIP).plan(SimplePlan).run()
完整的例子是here
【讨论】:
【参考方案2】:您可能会发现 Unfiltered 值得一看。
【讨论】:
【参考方案3】:Scala 等效项包含在 6 个命令中:
$ curl https://raw.github.com/n8han/conscript/master/setup.sh | sh
$ ~/bin/cs n8han/giter8
$ ~/bin/g8 scalatra/scalatra-sbt --name=scalatra-example
$ cd scalatra-example
$ wget http://typesafe.artifactoryonline.com/typesafe/ivy-releases/org.scala-tools.sbt/sbt-launch/0.11.0/sbt-launch.jar
$ java -Xmx512M -jar sbt-launch.jar ~jetty-run
使用 Play,
step #1 download Play, then
$ play install scala
$ play new myproject --with scala
$ play run myproject
【讨论】:
【参考方案4】:这使用了 JDK6 中内置的 HttpServer 类。欢迎提出改进建议,我是 Scala 新手。
package org.test.simplehttpserver
import java.net.InetSocketAddress
import com.sun.net.httpserver.HttpExchange, HttpHandler, HttpServer
import collection.mutable.HashMap
abstract class SimpleHttpServerBase(val socketAddress: String = "127.0.0.1",
val port: Int = 8080,
val backlog: Int = 0) extends HttpHandler
private val address = new InetSocketAddress(socketAddress, port)
private val server = HttpServer.create(address, backlog)
server.createContext("/", this)
def redirect(url: String) =
<html>
<head>
<meta http-equiv="Refresh" content="0," + url/>
</head>
<body>
You are being redirected to:
<a href=url>
url
</a>
</body>
</html>
def respond(exchange: HttpExchange, code: Int = 200, body: String = "")
val bytes = body.getBytes
exchange.sendResponseHeaders(code, bytes.size)
exchange.getResponseBody.write(bytes)
exchange.getResponseBody.write("\r\n\r\n".getBytes)
exchange.getResponseBody.close()
exchange.close()
def start() = server.start()
def stop(delay: Int = 1) = server.stop(delay)
abstract class SimpleHttpServer extends SimpleHttpServerBase
private val mappings = new HashMap[String, () => Any]
def get(path: String)(action: => Any) = mappings += path -> (() => action)
def handle(exchange: HttpExchange) = mappings.get(exchange.getRequestURI.getPath) match
case None => respond(exchange, 404)
case Some(action) => try
respond(exchange, 200, action().toString)
catch
case ex: Exception => respond(exchange, 500, ex.toString)
class HelloApp extends SimpleHttpServer
var count = 0
get("/")
"There's nothing here"
get("/hello")
"Hello, world!"
get("/markup")
<html>
<head>
<title>Test Title</title>
</head>
<body>
Test Body
</body>
</html>
def countPage = <html>
<head>
<title>Test Title</title>
</head>
<body>
Count:
count<a href="/increaseCount">++</a>
<a href="/decreaseCount">--</a>
<a href="/resetCount">Reset</a>
</body>
</html>
get("/count")
countPage
get("/resetCount")
count = 0
redirect("/count")
get("/increaseCount")
count = count + 1
redirect("/count")
get("/decreaseCount")
count = count - 1
redirect("/count")
get("/error")
throw new RuntimeException("Bad bad error occurred")
object Main
def main(args: Array[String])
val server = new HelloApp()
server.start()
【讨论】:
有没有办法指定响应的内容类型,例如,使用图像? 通过调用 exchange.sendResponseHeaders() 设置内容类型。【参考方案5】:我知道Max 已经知道mentioned,但我忍不住指出Scalatra's 6 行hello world:
import org.scalatra._
class ScalatraExample extends ScalatraServlet
get("/")
<h1>Hello, world!</h1>
不管怎样,看看可用的Scala web frameworks。
编辑
关于准备好工具有多么容易,特别是关于 Lift。所以,这里有一个关于 Ubuntu 的会话。我的大部分时间都花在试图弄清楚 Sun 的 Java 在包管理器中的位置上。无论如何,一旦安装了 Java,它就是这样进行的,所有消息都被省略了,所以人们可以看到我实际上必须输入的内容:
dcs@dcs-desktop:~$ wget -q -O bin/sbt-launch.jar http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.4.jar
dcs@dcs-desktop:~$ echo 'java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"' > bin/sbt
dcs@dcs-desktop:~$ chmod u+x bin/sbt
dcs@dcs-desktop:~$ mkdir app
dcs@dcs-desktop:~$ cd app
dcs@dcs-desktop:~/app$ sbt
Project does not exist, create new project? (y/N/s) s
> *lifty is org.lifty lifty 1.4
> lifty create project-blank sample 2.1
> reload
> update
> jetty-run
那里,网络服务器正在运行。当然,你必须事先了解 SBT 和 Lifty,才能知道你会使用它们来运行 Scala Lift 程序,但另一方面,我从未听说过 Flask,所以我肯定会花很多时间尝试弄清楚如何在 Python 中运行 Web 服务器应用程序的时间比我获得 Lift 的时间要多。
我在第一次尝试时也没有做对——我尝试使用 Scala 2.8.1(上面使用默认的 2.7.7 版本,虽然 2.8.0 也可以),但发现有到目前为止,该版本的 Scala 还没有可用的 Lift 版本。另一方面,我已经安装了lifty,并卸载它只是为了显示安装它的命令。
我真希望有一个用于 SBT 的 Debian/Ubuntu 软件包——毕竟它只是一个很小的 shell 脚本和一个 jar 文件,它负责下载 Scala、Lift 等,以及你需要的任何版本。
它是一种不同于 Python 和 Ruby 的模型,后者的语言带有一个包管理器来处理大多数事情。
【讨论】:
谢谢,Scala 的 6 行代码太棒了!有没有办法通过几个命令安装 Scala 和 Scalatra 并启动服务器? 请参阅“快速启动 Scalatra”:blog.everythings-beta.com/?p=430。这掩盖了安装 sbt 的步骤,但这并不困难。当然,最好有一个 debian/ubuntu 软件包。 @Matthew 当然。 Scalatra 网页上的快速入门非常简短。你只需要安装 git 和 sbt。我真希望他们能实现像 Lifty (github.com/Lifty/Lifty) 这样的处理器,这样 sbt 就足够了。【参考方案6】:您可以使用嵌入式 Jetty 服务器:
/*
* Required Libs: Jetty, Servlet API
*
* Compile:
* scalac -cp jetty-6.1.14.jar:jetty-util-6.1.14.jar:servlet-api-2.5-6.1.14.jar WebServer.scala
*
* Run:
* scala -cp .:jetty-6.1.14.jar:jetty-util-6.1.14.jar:servlet-api-2.5-6.1.14.jar WebServer
*/
import org.mortbay.jetty.Server
import org.mortbay.jetty.servlet.Context
import javax.servlet.http.HttpServlet,
HttpServletRequest,
HttpServletResponse
class HelloServlet extends HttpServlet
override def doGet(req : HttpServletRequest, resp : HttpServletResponse) =
resp.getWriter().print("Hello There!")
object WebServer
def main(args: Array[String])
val server = new Server(8080)
val root = new Context(server, "/", Context.SESSIONS)
root.addServlet(classOf[HelloServlet], "/*")
server.start()
println("Point your browser to http://localhost:8080/")
println("Type [CTRL]+[C] to quit!")
Thread.sleep(Long.MaxValue)
如果您的目标是进行 LOC 比较,您可以使用嵌入 Sun JDK 的 HTTP 服务器。另一种解决方案是使用 javax.xml.ws.Endpoint 和 Provider API。
【讨论】:
【参考方案7】:此解决方案使用 JAX-WS 端点:
import java.io._
import javax.xml.ws._
import javax.xml.ws.http._
import javax.xml.transform._
import javax.xml.transform.stream._
@WebServiceProvider
@ServiceMode(value=Service.Mode.PAYLOAD)
class P extends Provider[Source]
def invoke(source: Source) = new StreamSource( new StringReader("<p>Hello There!</p>"));
val address = "http://127.0.0.1:8080/"
Endpoint.create(HTTPBinding.HTTP_BINDING, new P()).publish(address)
println("Service running at "+address)
println("Type [CTRL]+[C] to quit!")
Thread.sleep(Long.MaxValue)
您可以将它复制到一个文件 WebServer.scala 并通过键入以下内容运行它:
scala WebServer.scala
【讨论】:
【参考方案8】:嗯,有Scalatra,它旨在在功能和易用性方面类似于 Ruby 的 Sinatra。
【讨论】:
以上是关于在 Scala 中引导 Web 服务器的主要内容,如果未能解决你的问题,请参考以下文章