• high performance http server writen by akka


    采用akka2.0 IO ByteString相关技术,代码改自http://doc.akka.io/docs/akka/2.0/scala/io.html,目前代码比较粗糙,但性能已经体现出来了。

    话不多说,贴代码

    Scala代码 
    1. /**  
    2.  * Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>  
    3.  */  
    4. package akka.docs.io.v2  
    5.   
    6. //#imports  
    7. import akka.actor._  
    8. import akka.util.{ ByteString, ByteStringBuilder }  
    9. import java.net.InetSocketAddress  
    10. //#imports  
    11.   
    12. //#actor  
    13. class HttpServer2(port: Int) extends Actor {  
    14.   
    15.   val state = new scala.collection.mutable.HashMap[IO.Handle, ActorRef]()  
    16.   
    17.   override def preStart {  
    18.     IOManager(context.system) listen new InetSocketAddress(port)  
    19.   }  
    20.   
    21.   def receive = {  
    22.   
    23.     case IO.NewClient(server) ⇒  
    24.       val socket = server.accept()  
    25.       val worker = context.actorOf(Props(new Worker(socket)))  
    26.       state(socket) = worker  
    27.       state(socket) ! socket  
    28.   
    29.     case IO.Read(socket, bytes) ⇒  
    30.       state(socket) ! IO.Read(socket, bytes)  
    31.   
    32.     case IO.Closed(socket, cause) ⇒  
    33.       state(socket) ! IO.Closed(socket, cause)  
    34.       state -= socket  
    35.   
    36.   }  
    37.   
    38. }  
    39.   
    40. class Worker(socket: IO.SocketHandle) extends Actor {  
    41.   
    42.   val state = IO.IterateeRef.Map.async[IO.Handle]()(context.dispatcher)  
    43.   
    44.   override def preStart {  
    45.     // state(socket) flatMap (_ ⇒ HttpServer2.processRequest(socket))  
    46.   }  
    47.   
    48.   def receive = {  
    49.   
    50.     case socket:IO.SocketHandle ⇒  
    51.       state(socket) flatMap (_ ⇒ HttpServer2.processRequest(socket))  
    52.   
    53.     case IO.Read(socket, bytes) ⇒  
    54.       state(socket)(IO Chunk bytes)  
    55.   
    56.     case IO.Closed(socket, cause) ⇒  
    57.       state(socket)(IO EOF None)  
    58.       state -= socket  
    59.   
    60.   }  
    61.   
    62. }  
    63.   
    64. //#actor  
    65.   
    66. //#actor-companion  
    67. object HttpServer2 {  
    68.   import HttpIteratees._  
    69.   
    70.   def processRequest(socket: IO.SocketHandle): IO.Iteratee[Unit] =  
    71.     IO repeat {  
    72.       for {  
    73.         request ← readRequest  
    74.       } yield {  
    75.         val rsp = request match {  
    76.           case Request("GET""ping" :: Nil, _, _, headers, _) ⇒  
    77.             OKResponse(ByteString("<p>pong</p>"),  
    78.               request.headers.exists { case Header(n, v) ⇒ n.toLowerCase == "connection" && v.toLowerCase == "keep-alive" })  
    79.           case req ⇒  
    80.             OKResponse(ByteString("<p>" + req.toString + "</p>"),  
    81.               request.headers.exists { case Header(n, v) ⇒ n.toLowerCase == "connection" && v.toLowerCase == "keep-alive" })  
    82.         }  
    83.         socket write OKResponse.bytes(rsp).compact  
    84.         if (!rsp.keepAlive) socket.close()  
    85.       }  
    86.     }  
    87.   
    88. }  
    89. //#actor-companion  
    90.   
    91. //#request-class  
    92. case class Request(meth: String, path: List[String], query: Option[String], httpver: String, headers: List[Header], body: Option[ByteString])  
    93. case class Header(name: String, value: String)  
    94. //#request-class  
    95.   
    96. //#constants  
    97. object HttpConstants {  
    98.   val SP = ByteString(" ")  
    99.   val HT = ByteString("\t")  
    100.   val CRLF = ByteString("\r\n")  
    101.   val COLON = ByteString(":")  
    102.   val PERCENT = ByteString("%")  
    103.   val PATH = ByteString("/")  
    104.   val QUERY = ByteString("?")  
    105. }  
    106. //#constants  
    107.   
    108. //#read-request  
    109. object HttpIteratees {  
    110.   import HttpConstants._  
    111.   
    112.   def readRequest =  
    113.     for {  
    114.       requestLine ← readRequestLine  
    115.       (meth, (path, query), httpver) = requestLine  
    116.       headers ← readHeaders  
    117.       body ← readBody(headers)  
    118.     } yield Request(meth, path, query, httpver, headers, body)  
    119.   //#read-request  
    120.   
    121.   //#read-request-line  
    122.   def ascii(bytes: ByteString): String = bytes.decodeString("US-ASCII").trim  
    123.   
    124.   def readRequestLine =  
    125.     for {  
    126.       meth ← IO takeUntil SP  
    127.       uri ← readRequestURI  
    128.       _ ← IO takeUntil SP // ignore the rest  
    129.       httpver ← IO takeUntil CRLF  
    130.     } yield (ascii(meth), uri, ascii(httpver))  
    131.   //#read-request-line  
    132.   
    133.   //#read-request-uri  
    134.   def readRequestURI = IO peek 1 flatMap {  
    135.     case PATH ⇒  
    136.       for {  
    137.         path ← readPath  
    138.         query ← readQuery  
    139.       } yield (path, query)  
    140.     case _ ⇒ sys.error("Not Implemented")  
    141.   }  
    142.   //#read-request-uri  
    143.   
    144.   //#read-path  
    145.   def readPath = {  
    146.     def step(segments: List[String]): IO.Iteratee[List[String]] = IO peek 1 flatMap {  
    147.       case PATH ⇒ IO drop 1 flatMap (_ ⇒ readUriPart(pathchar) flatMap (segment ⇒ step(segment :: segments)))  
    148.       case _ ⇒ segments match {  
    149.         case "" :: rest ⇒ IO Done rest.reverse  
    150.         case _          ⇒ IO Done segments.reverse  
    151.       }  
    152.     }  
    153.     step(Nil)  
    154.   }  
    155.   //#read-path  
    156.   
    157.   //#read-query  
    158.   def readQuery: IO.Iteratee[Option[String]] = IO peek 1 flatMap {  
    159.     case QUERY ⇒ IO drop 1 flatMap (_ ⇒ readUriPart(querychar) map (Some(_)))  
    160.     case _     ⇒ IO Done None  
    161.   }  
    162.   //#read-query  
    163.   
    164.   //#read-uri-part  
    165.   val alpha = Set.empty ++ ('a' to 'z') ++ ('A' to 'Z') map (_.toByte)  
    166.   val digit = Set.empty ++ ('0' to '9') map (_.toByte)  
    167.   val hexdigit = digit ++ (Set.empty ++ ('a' to 'f') ++ ('A' to 'F') map (_.toByte))  
    168.   val subdelim = Set('!''$''&''\'''('')''*''+'','';''=') map (_.toByte)  
    169.   val pathchar = alpha ++ digit ++ subdelim ++ (Set(':''@') map (_.toByte))  
    170.   val querychar = pathchar ++ (Set('/''?') map (_.toByte))  
    171.   
    172.   def readUriPart(allowed: Set[Byte]): IO.Iteratee[String] = for {  
    173.     str ← IO takeWhile allowed map ascii  
    174.     pchar ← IO peek 1 map (_ == PERCENT)  
    175.     all ← if (pchar) readPChar flatMap (ch ⇒ readUriPart(allowed) map (str + ch + _)) else IO Done str  
    176.   } yield all  
    177.   
    178.   def readPChar = IO take 3 map {  
    179.     case Seq('%', rest @ _*) if rest forall hexdigit ⇒  
    180.       java.lang.Integer.parseInt(rest map (_.toChar) mkString, 16).toChar  
    181.   }  
    182.   //#read-uri-part  
    183.   
    184.   //#read-headers  
    185.   def readHeaders = {  
    186.     def step(found: List[Header]): IO.Iteratee[List[Header]] = {  
    187.       IO peek 2 flatMap {  
    188.         case CRLF ⇒ IO takeUntil CRLF flatMap (_ ⇒ IO Done found)  
    189.         case _    ⇒ readHeader flatMap (header ⇒ step(header :: found))  
    190.       }  
    191.     }  
    192.     step(Nil)  
    193.   }  
    194.   
    195.   def readHeader =  
    196.     for {  
    197.       name ← IO takeUntil COLON  
    198.       value ← IO takeUntil CRLF flatMap readMultiLineValue  
    199.     } yield Header(ascii(name), ascii(value))  
    200.   
    201.   def readMultiLineValue(initial: ByteString): IO.Iteratee[ByteString] = IO peek 1 flatMap {  
    202.     case SP ⇒ IO takeUntil CRLF flatMap (bytes ⇒ readMultiLineValue(initial ++ bytes))  
    203.     case _  ⇒ IO Done initial  
    204.   }  
    205.   //#read-headers  
    206.   
    207.   //#read-body  
    208.   def readBody(headers: List[Header]) =  
    209.     if (headers.exists(header ⇒ header.name == "Content-Length" || header.name == "Transfer-Encoding"))  
    210.       IO.takeAll map (Some(_))  
    211.     else  
    212.       IO Done None  
    213.   //#read-body  
    214. }  
    215.   
    216. //#ok-response  
    217. object OKResponse {  
    218.   import HttpConstants.CRLF  
    219.   
    220.   val okStatus = ByteString("HTTP/1.1 200 OK")  
    221.   val contentType = ByteString("Content-Type: text/html; charset=utf-8")  
    222.   val cacheControl = ByteString("Cache-Control: no-cache")  
    223.   val date = ByteString("Date: ")  
    224.   val server = ByteString("Server: Akka")  
    225.   val contentLength = ByteString("Content-Length: ")  
    226.   val connection = ByteString("Connection: ")  
    227.   val keepAlive = ByteString("Keep-Alive")  
    228.   val close = ByteString("Close")  
    229.   
    230.   def bytes(rsp: OKResponse) = {  
    231.     new ByteStringBuilder ++=  
    232.       okStatus ++= CRLF ++=  
    233.       contentType ++= CRLF ++=  
    234.       cacheControl ++= CRLF ++=  
    235.       date ++= ByteString(new java.util.Date().toString) ++= CRLF ++=  
    236.       server ++= CRLF ++=  
    237.       contentLength ++= ByteString(rsp.body.length.toString) ++= CRLF ++=  
    238.       connection ++= (if (rsp.keepAlive) keepAlive else close) ++= CRLF ++= CRLF ++= rsp.body result  
    239.   }  
    240.   
    241. }  
    242. case class OKResponse(body: ByteString, keepAlive: Boolean)  
    243. //#ok-response  
    244.   
    245. //#main  
    246. object Main extends App {  
    247.   val port = Option(System.getenv("PORT")) map (_.toInt) getOrElse 8080  
    248.   val system = ActorSystem()  
    249.   val server = system.actorOf(Props(new HttpServer2(port)))  
    250. }  
    251. //#main  
  • 相关阅读:
    Dubbo介绍以及Dubbo Admin的安装
    CentOS下安Zookeeper服务
    Linux定时备份mysql数据库
    Spring Boot 整合Mybatis 框架以及使用
    Sprint Boot日志功能的使用
    Centos下安装Redis服务
    Java 通用md5工具类
    Screen 用法简述
    解决zabbix的cannot allocate shared memory of size错误
    Python 之禅
  • 原文地址:https://www.cnblogs.com/bozhang/p/3114694.html
Copyright © 2020-2023  润新知