针对 .NET Web API 实现的 Akka-Http Rest 服务性能
Akka-Http Rest service performance against .NET Web API implementation
因此,我们在 .NET Web API
中实现的环境中托管了 REST API。
我目前正在研究 PoC,以在 Akka Http
中实现它的某些部分,以展示我们从反应范式中获得的一些性能优势,而不是当前的 WEB API 安装。
我已经实现了一个带有几个接口和一些数据库集成 (CRUD) 的简短 POC。
我想弄清楚我应该展示哪些东西才能清楚地展示性能差异。
与 ASP.NET Web API 或 Spring 等传统框架的每个请求线程方法相反,Akka HTTP 对每个请求处理一个 actor 是否正确?
更新:
这是我的服务器代码的样子,
implicit val blockingDispatcher = system.dispatchers.lookup("my-blocking-
dispatcher")
val agentRoute1 = get {
pathPrefix("agentStream" / Segment) {
applicationName =>
val futuredataListbyStream: Future[Try[Source[Any,NotUsed]]] = Future {
DatabaseProcedure.getDataAsStream(paramName)
}
onComplete(futuredataListbyStream) {
case Success(triedResultsSet) =>
triedResultsSet match{
case Success(srcStream) =>
srcStream.map(x => System.out.println(x.asInstanceOf[String]))
complete(StatusCodes.OK, "Gotten the data . Return is " + srcStream)
case Failure(ex) => complete(StatusCodes.InternalServerError, "Something went wrong here in the rseultset" + ex.getMessage)
}
case Failure(ex) => complete(StatusCodes.InternalServerError, "Something went wrong in procedure execution" + ex.getMessage)
}
}
}
数据库调用代码如下所示,
def getdataAsStream(paramName: String):Try[Source[Any,NotUsed]] = {
entityManager.getTransaction().begin()
System.out.println("In blocking db call"+paramName)
val query: StoredProcedureQuery = entityManager.createStoredProcedureQuery("procedure_name")
.registerStoredProcedureParameter(1, classOf[String],
ParameterMode.IN)
.registerStoredProcedureParameter(2, classOf[String],
ParameterMode.IN)
.registerStoredProcedureParameter(3, classOf[Class[_]],
ParameterMode.REF_CURSOR)
.setParameter(1,"test")
.setParameter(2,paramName)
entityManager.close()
return Try {
query.execute
val list = query.getResultList.asScala.toList
Source.apply(list)
}
}
为了获得最佳性能,我发现一些 "tricks" 是必要的:
- 块大小:如果您使用
HttpEntity.Chunked
作为 HttpResponse
中的 ResponseEntity
,则调整块的大小。太多的小块会降低性能。如果您知道响应实体总是相对较小,那么您可能希望默认使用 Strict
响应实体。
- 异步边界:如果您为传入请求提供服务的操作不会从多个参与者那里获益太多,例如大量 IO 等待,那么您可能希望坚持使用 1 个异步边界而不是多个。所以使用
Flow[HttpRequest].map(...).map(...)
而不是 Flow[HttpRequest].via(Flow...).via(Flow...)
.
根据请求回答您关于 Actors 的问题:
如果您按照我的建议使用 1 个异步边界,那么是的,您将在每个请求中使用 ~1 个 actor,而不是 1 个线程。
回答您关于展示的问题:
我认为将 akka 用作 Web 服务的最佳部分之一是,如果您一直使用反应流,那么每个请求和每个连接的内存使用量可以保持不变。因此,如果您使用流从数据库集成中获取数据,那么 returns 无限行的客户端查询只会随着时间的推移在您的服务中消耗固定数量的内存。这对于每个请求有效地利用您的内存很有用。
始终可以调整 Web 框架,使一个框架比另一个框架更高效,但在反应流之外很难让您的客户端发送 任何 请求想要而不用担心内存不足异常。
因此,我们在 .NET Web API
中实现的环境中托管了 REST API。
我目前正在研究 PoC,以在 Akka Http
中实现它的某些部分,以展示我们从反应范式中获得的一些性能优势,而不是当前的 WEB API 安装。
我已经实现了一个带有几个接口和一些数据库集成 (CRUD) 的简短 POC。
我想弄清楚我应该展示哪些东西才能清楚地展示性能差异。
与 ASP.NET Web API 或 Spring 等传统框架的每个请求线程方法相反,Akka HTTP 对每个请求处理一个 actor 是否正确?
更新:
这是我的服务器代码的样子,
implicit val blockingDispatcher = system.dispatchers.lookup("my-blocking-
dispatcher")
val agentRoute1 = get {
pathPrefix("agentStream" / Segment) {
applicationName =>
val futuredataListbyStream: Future[Try[Source[Any,NotUsed]]] = Future {
DatabaseProcedure.getDataAsStream(paramName)
}
onComplete(futuredataListbyStream) {
case Success(triedResultsSet) =>
triedResultsSet match{
case Success(srcStream) =>
srcStream.map(x => System.out.println(x.asInstanceOf[String]))
complete(StatusCodes.OK, "Gotten the data . Return is " + srcStream)
case Failure(ex) => complete(StatusCodes.InternalServerError, "Something went wrong here in the rseultset" + ex.getMessage)
}
case Failure(ex) => complete(StatusCodes.InternalServerError, "Something went wrong in procedure execution" + ex.getMessage)
}
}
}
数据库调用代码如下所示,
def getdataAsStream(paramName: String):Try[Source[Any,NotUsed]] = {
entityManager.getTransaction().begin()
System.out.println("In blocking db call"+paramName)
val query: StoredProcedureQuery = entityManager.createStoredProcedureQuery("procedure_name")
.registerStoredProcedureParameter(1, classOf[String],
ParameterMode.IN)
.registerStoredProcedureParameter(2, classOf[String],
ParameterMode.IN)
.registerStoredProcedureParameter(3, classOf[Class[_]],
ParameterMode.REF_CURSOR)
.setParameter(1,"test")
.setParameter(2,paramName)
entityManager.close()
return Try {
query.execute
val list = query.getResultList.asScala.toList
Source.apply(list)
}
}
为了获得最佳性能,我发现一些 "tricks" 是必要的:
- 块大小:如果您使用
HttpEntity.Chunked
作为HttpResponse
中的ResponseEntity
,则调整块的大小。太多的小块会降低性能。如果您知道响应实体总是相对较小,那么您可能希望默认使用Strict
响应实体。 - 异步边界:如果您为传入请求提供服务的操作不会从多个参与者那里获益太多,例如大量 IO 等待,那么您可能希望坚持使用 1 个异步边界而不是多个。所以使用
Flow[HttpRequest].map(...).map(...)
而不是Flow[HttpRequest].via(Flow...).via(Flow...)
.
根据请求回答您关于 Actors 的问题:
如果您按照我的建议使用 1 个异步边界,那么是的,您将在每个请求中使用 ~1 个 actor,而不是 1 个线程。
回答您关于展示的问题:
我认为将 akka 用作 Web 服务的最佳部分之一是,如果您一直使用反应流,那么每个请求和每个连接的内存使用量可以保持不变。因此,如果您使用流从数据库集成中获取数据,那么 returns 无限行的客户端查询只会随着时间的推移在您的服务中消耗固定数量的内存。这对于每个请求有效地利用您的内存很有用。
始终可以调整 Web 框架,使一个框架比另一个框架更高效,但在反应流之外很难让您的客户端发送 任何 请求想要而不用担心内存不足异常。