Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First naive request response with blocking

  • Loading branch information...
commit b8565afcf84910dbbd1fb1d35fb6339e96fb97ae 1 parent 7447eab
Patrik Nordwall authored
View
13 web-to-backend/build.sbt
@@ -0,0 +1,13 @@
+name := "Web to Backend"
+
+version := "1.0"
+
+scalaVersion := "2.9.1"
+
+resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases"
+
+libraryDependencies += "org.scalatest" % "scalatest_2.9.0" % "1.6.1" % "test"
+
+libraryDependencies += "se.scalablesolutions.akka" % "akka-kernel" % "1.2"
+
+
View
88 web-to-backend/jmeter/jmeter-test.jmx
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jmeterTestPlan version="1.2" properties="2.1">
+ <hashTree>
+ <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
+ <stringProp name="TestPlan.comments"></stringProp>
+ <boolProp name="TestPlan.functional_mode">false</boolProp>
+ <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
+ <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+ <collectionProp name="Arguments.arguments"/>
+ </elementProp>
+ <stringProp name="TestPlan.user_define_classpath"></stringProp>
+ </TestPlan>
+ <hashTree>
+ <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Translate Thread Group" enabled="true">
+ <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
+ <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
+ <boolProp name="LoopController.continue_forever">false</boolProp>
+ <stringProp name="LoopController.loops">2000</stringProp>
+ </elementProp>
+ <stringProp name="ThreadGroup.num_threads">1</stringProp>
+ <stringProp name="ThreadGroup.ramp_time">1</stringProp>
+ <longProp name="ThreadGroup.start_time">1316348954000</longProp>
+ <longProp name="ThreadGroup.end_time">1316348954000</longProp>
+ <boolProp name="ThreadGroup.scheduler">false</boolProp>
+ <stringProp name="ThreadGroup.duration"></stringProp>
+ <stringProp name="ThreadGroup.delay"></stringProp>
+ </ThreadGroup>
+ <hashTree>
+ <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Translate" enabled="true">
+ <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
+ <collectionProp name="Arguments.arguments"/>
+ </elementProp>
+ <stringProp name="HTTPSampler.domain">localhost</stringProp>
+ <stringProp name="HTTPSampler.port">9898</stringProp>
+ <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+ <stringProp name="HTTPSampler.response_timeout"></stringProp>
+ <stringProp name="HTTPSampler.protocol"></stringProp>
+ <stringProp name="HTTPSampler.contentEncoding"></stringProp>
+ <stringProp name="HTTPSampler.path">translate?text=aaa%20bbb</stringProp>
+ <stringProp name="HTTPSampler.method">GET</stringProp>
+ <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
+ <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
+ <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
+ <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
+ <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+ <stringProp name="HTTPSampler.concurrentPool">4</stringProp>
+ <boolProp name="HTTPSampler.monitor">false</boolProp>
+ <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+ </HTTPSamplerProxy>
+ <hashTree/>
+ </hashTree>
+ <Summariser guiclass="SummariserGui" testclass="Summariser" testname="Generate Summary Results" enabled="true"/>
+ <hashTree/>
+ <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="Aggregate Report" enabled="true">
+ <boolProp name="ResultCollector.error_logging">false</boolProp>
+ <objProp>
+ <name>saveConfig</name>
+ <value class="SampleSaveConfiguration">
+ <time>true</time>
+ <latency>true</latency>
+ <timestamp>true</timestamp>
+ <success>true</success>
+ <label>true</label>
+ <code>true</code>
+ <message>true</message>
+ <threadName>true</threadName>
+ <dataType>true</dataType>
+ <encoding>false</encoding>
+ <assertions>true</assertions>
+ <subresults>true</subresults>
+ <responseData>false</responseData>
+ <samplerData>false</samplerData>
+ <xml>true</xml>
+ <fieldNames>false</fieldNames>
+ <responseHeaders>false</responseHeaders>
+ <requestHeaders>false</requestHeaders>
+ <responseDataOnError>false</responseDataOnError>
+ <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
+ <assertionsResultsToSave>0</assertionsResultsToSave>
+ <bytes>true</bytes>
+ </value>
+ </objProp>
+ <stringProp name="filename"></stringProp>
+ </ResultCollector>
+ <hashTree/>
+ </hashTree>
+ </hashTree>
+</jmeterTestPlan>
View
16 web-to-backend/src/main/resources/akka.conf
@@ -0,0 +1,16 @@
+akka {
+
+ enabled-modules = ["http"]
+
+ boot = ["sample.Boot"]
+
+ #event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
+ event-handler-level = "INFO"
+
+ http {
+ hostname = "0.0.0.0"
+ port = 9898
+ timeout=5000
+ }
+
+}
View
30 web-to-backend/src/main/resources/logback.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- For assistance related to logback-translator or configuration -->
+<!-- files in general, please contact the logback user mailing list -->
+<!-- at http://www.qos.ch/mailman/listinfo/logback-user -->
+<!-- -->
+<!-- For professional support please see -->
+<!-- http://www.qos.ch/shop/products/professionalSupport -->
+<!-- -->
+<configuration scan="false" debug="false">
+ <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>[%t] [%4p] [%d{ISO8601}] %c{1}: %m%n</pattern>
+ </encoder>
+ </appender>
+ <appender name="R" class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <File>./logs/akka.log</File>
+ <encoder>
+ <pattern>[%t] [%4p] [%d{ISO8601}] %c{1}: %m%n</pattern>
+ </encoder>
+ <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
+ <fileNamePattern>./logs/akka.log.slf4j.%d{yyyy-MM-dd-HH}</fileNamePattern>
+ </rollingPolicy>
+ </appender>
+ <logger name="org.eclipse.jetty.util.log" level="INFO"/>
+ <root level="INFO">
+ <appender-ref ref="stdout"/>
+ <appender-ref ref="R"/>
+ </root>
+</configuration>
View
80 web-to-backend/src/main/resources/microkernel-server.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
+
+<!-- =============================================================== -->
+<!-- Configure the Jetty Server -->
+<!-- -->
+<!-- Documentation of this file format can be found at: -->
+<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax -->
+<!-- -->
+<!-- Additional configuration files are available in $JETTY_HOME/etc -->
+<!-- and can be mixed in. For example: -->
+<!-- java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml -->
+<!-- -->
+<!-- See start.ini file for the default configuraton files -->
+<!-- =============================================================== -->
+
+
+<Configure id="Server" class="org.eclipse.jetty.server.Server">
+
+ <!-- =========================================================== -->
+ <!-- Server Thread Pool -->
+ <!-- =========================================================== -->
+ <Set name="ThreadPool">
+ <New class="org.eclipse.jetty.util.thread.ExecutorThreadPool">
+ </New>
+ </Set>
+
+ <!-- =========================================================== -->
+ <!-- Set connectors -->
+ <!-- =========================================================== -->
+
+ <Call name="addConnector">
+ <Arg>
+ <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
+ <Set name="host"><SystemProperty name="jetty.host" /></Set>
+ <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
+ <Set name="maxIdleTime">300000</Set>
+ <Set name="Acceptors">2</Set>
+ <Set name="statsOn">false</Set>
+ <Set name="confidentialPort">8443</Set>
+ <Set name="lowResourcesConnections">20000</Set>
+ <Set name="lowResourcesMaxIdleTime">5000</Set>
+ </New>
+ </Arg>
+ </Call>
+
+
+ <!-- =========================================================== -->
+ <!-- Set handler Collection Structure -->
+ <!-- =========================================================== -->
+ <Set name="handler">
+ <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
+ <Set name="handlers">
+ <Array type="org.eclipse.jetty.server.Handler">
+ <Item>
+ <New id="AkkaMistHandler" class="org.eclipse.jetty.servlet.ServletContextHandler">
+ <Set name="contextPath">/</Set>
+ <Call name="addServlet">
+ <Arg>akka.http.AkkaMistServlet</Arg>
+ <Arg>/*</Arg>
+ </Call>
+ </New>
+ </Item>
+ <Item>
+ <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
+ </Item>
+ </Array>
+ </Set>
+ </New>
+ </Set>
+
+ <!-- =========================================================== -->
+ <!-- extra options -->
+ <!-- =========================================================== -->
+ <Set name="stopAtShutdown">true</Set>
+ <Set name="sendServerVersion">true</Set>
+ <Set name="sendDateHeader">true</Set>
+ <Set name="gracefulShutdown">1000</Set>
+
+</Configure>
View
65 web-to-backend/src/main/scala/sample/Backend.scala
@@ -0,0 +1,65 @@
+package sample
+
+import akka.actor.Actor.actorOf
+import akka.actor.Actor
+import akka.actor.ActorRef
+import akka.dispatch.Dispatchers
+import akka.routing.CyclicIterator
+import akka.routing.Routing
+
+object Backend {
+
+ case class TranslationRequest(text: String)
+ case class TranslationResponse(text: String, words: Int)
+
+ private val backendDispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("backend-dispatcher")
+ .setCorePoolSize(4)
+ .build
+
+ val translationService = loadBalanced(10, actorOf[TranslationService])
+
+ private def loadBalanced(poolSize: Int, actor: ActorRef): ActorRef = {
+ val workers = Vector.fill(poolSize)(actor.start())
+ Routing.loadBalancerActor(CyclicIterator(workers)).start()
+ }
+
+ class TranslationService extends Actor {
+ self.dispatcher = backendDispatcher
+
+ val translator = loadBalanced(4, actorOf[Translator])
+ val counter = loadBalanced(4, actorOf[Counter])
+
+ def receive = {
+ case TranslationRequest(text)
+ val future1 = (translator ? text)
+ val translatedText = future1.get.asInstanceOf[String]
+ val future2 = (counter ? text)
+ val words = future2.get.asInstanceOf[Int]
+
+ self.channel ! TranslationResponse(translatedText, words)
+ }
+ }
+
+ class Translator extends Actor {
+ self.dispatcher = backendDispatcher
+
+ def receive = {
+ case x: String
+ Thread.sleep(100)
+ val result = x.toUpperCase
+ self.channel ! result
+ }
+ }
+
+ class Counter extends Actor {
+ self.dispatcher = backendDispatcher
+
+ def receive = {
+ case x: String
+ Thread.sleep(100)
+ val result = x.split(" ").length
+ self.channel ! result
+ }
+ }
+
+}
View
73 web-to-backend/src/main/scala/sample/Frontend.scala
@@ -0,0 +1,73 @@
+package sample
+
+import akka.actor.Actor.actorOf
+import akka.actor.Actor
+import akka.actor.ActorRef
+import akka.dispatch.Dispatchers
+import akka.http.Endpoint
+import akka.http.Get
+import akka.http.RequestMethod
+import akka.http.RootEndpoint
+import akka.routing.CyclicIterator
+import akka.routing.Routing
+import sample.Backend.TranslationRequest
+import sample.Backend.TranslationResponse
+import sample.Backend.translationService
+
+object Frontend {
+
+ object EndpointURI {
+ val Translate = "/translate"
+ }
+
+ private val frontendDispatcher = Dispatchers.newExecutorBasedEventDrivenDispatcher("frontend-dispatcher")
+ .setCorePoolSize(4)
+ .build
+
+ private def loadBalanced(poolSize: Int, actor: ActorRef): ActorRef = {
+ val workers = Vector.fill(poolSize)(actor.start())
+ Routing.loadBalancerActor(CyclicIterator(workers)).start()
+ }
+
+ class WebEndpoint(root: ActorRef) extends Actor with Endpoint {
+ import EndpointURI._
+ self.dispatcher = frontendDispatcher
+
+ val translate = loadBalanced(4, actorOf[TranslateHandler])
+
+ override def preStart() = {
+ root ! Endpoint.Attach(hook, provide)
+ }
+
+ def receive = handleHttpRequest
+
+ def hook(uri: String): Boolean = {
+ uri.startsWith(Translate)
+ }
+
+ def provide(uri: String): ActorRef = {
+ if (uri.startsWith(Translate)) translate
+ else throw new IllegalArgumentException("Unkown URI: " + uri)
+ }
+ }
+
+ class TranslateHandler extends Actor {
+ self.dispatcher = frontendDispatcher
+
+ def receive = {
+ case get: Get
+ val text = get.request.getParameter("text")
+ val TranslationResponse(translatedText, words) = (translationService ? TranslationRequest(text)).
+ get.asInstanceOf[TranslationResponse]
+ get.OK("Translated %s words to: %s".format(words, translatedText))
+ case other: RequestMethod
+ other.NotAllowed("Invalid method for this endpoint.")
+ }
+ }
+
+}
+
+class Boot {
+ val root = actorOf[RootEndpoint].start()
+ actorOf(new Frontend.WebEndpoint(root)).start()
+}
Please sign in to comment.
Something went wrong with that request. Please try again.