restapi作为前后端交互的枢纽:面对大批量的前端请求,需要确保回复的及时性。使用缓存是一项有效工具。我们可以把多数前端请求的回复response存入缓存,特别是一些需要大量计算才能获取的回复值,更可以大大提高后端的反应速度。值得庆幸的是akka-http已经提供了对缓存的支持,是基于java8 caffein的一套缓存操作工具包的。下面就介绍一下akka-http的caching。

akka-http caching 有个依赖:

 "com.typesafe.akka" %% "akka-http-caching" % akkaHttpVersion,

先从缓存存储结构开始,看看下面的一段缓存结构定义:

import akka.http.scaladsl.util.FastFuture
import akka.http.caching.scaladsl.Cache
import akka.http.caching.scaladsl.CachingSettings
import akka.http.caching.LfuCache val defaultCachingSettings = CachingSettings(sys)
val lfuCacheSettings = //最少使用排除算法缓存
defaultCachingSettings.lfuCacheSettings
.withInitialCapacity() //起始单位
.withMaxCapacity() //最大单位
.withTimeToLive(.hour) //最长存留时间
.withTimeToIdle(.minutes) //最长未使用时间
val cachingSettings =
defaultCachingSettings.withLfuCacheSettings(lfuCacheSettings) //key -> String
val lfuCache: Cache[String, Option[Map[String, Any]]] = LfuCache(cachingSettings)

lfuCache是一种基于使用频率算法的缓存管理系统,这个我们就不必多说了。最好能拿个例子来示范解释:刚好手头有个获取用户信息的http请求样板:

    val route = pathPrefix(pathName) {
pathPrefix("getuserinfo") {
(get & parameter('userid)) { userid => {
val userinfo = posRepo.getUserInfo(userid)
userinfo match {
case Some(ui) => complete(toJson(ui))
case None => complete(toJson(Map[String, Any](("TERMINALID" -> ""))))
}
}
}
} def getUserInfo(userid: String): Option[UserInfo] = {
val sql = "SELECT CUSTOMERS.SHOPID AS SHOPID, TERMINALID, DEVICEID, IMPSVCURL FROM CUSTOMERS INNER JOIN TERMINALS " +
" ON CUSTOMERS.SHOPID=TERMINALS.SHOPID " +
" WHERE (CUSTOMERS.DISABLED=0 AND TERMINALS.DISABLED=0) " +
" AND (CUSTOMERS.EXPDATE > GETDATE() AND TERMINALS.EXPDATE > GETDATE()) AND TERMINALID='" + userid + "'"
val rows = query[Map[String, Any]]("mpos", sql, rsc.resultSet2Map)
val futUI: Future[Option[Map[String, Any]]] = rows.runWith(Sink.lastOption)
Await.result(futUI, seconds)
}

当收到前端 http://mycom.com/pos/getuserinfo?userid=1234 这样的请求时需要从数据库里读取用户信息数据及进行一些转换处理。这个请求调用得频率较高、数据库读取也比较耗时,是个实在的例子。我们来看看如何实现缓存管理:

在akka-http里可以用两种方式来实现缓存管理:1、直接用cache工具,2、用akka-http提供的Directive: cache, alwaysCache

我们先看看如何直接使用cache操作,先看看Cache的构建:

abstract class Cache[K, V] extends akka.http.caching.javadsl.Cache[K, V] {
cache => /**
* Returns either the cached Future for the given key or evaluates the given value generating
* function producing a `Future[V]`.
*/
def apply(key: K, genValue: () => Future[V]): Future[V]

Cache[K,V]是以K为键,一个()=> Future[V]为值的结构,也就是说我们需要把一个获取Future值的函数存在缓存里:

      pathPrefix("getuserinfo") {
(get & parameter('userid)) { userid => {
val userinfo = lfuCache.getOrLoad(userid, _ => posRepo.futureUserInfo(userid))
onComplete(userinfo) {
_ match {
case Success(oui) => oui match {
case Some(ui) => complete(toJson(ui))
case None => complete(toJson(Map[String, Any](("TERMINALID" -> ""))))
}
case Failure(_) => complete(toJson(Map[String, Any](("TERMINALID" -> ""))))
}
}
}
} def futureUserInfo(userid: String): Future[Option[Map[String, Any]]] = {
val sql = "SELECT CUSTOMERS.SHOPID AS SHOPID, TERMINALID, DEVICEID, IMPSVCURL FROM CUSTOMERS INNER JOIN TERMINALS " +
" ON CUSTOMERS.SHOPID=TERMINALS.SHOPID " +
" WHERE (CUSTOMERS.DISABLED=0 AND TERMINALS.DISABLED=0) " +
" AND (CUSTOMERS.EXPDATE > GETDATE() AND TERMINALS.EXPDATE > GETDATE()) AND TERMINALID='" + userid + "'"
val rows = query[Map[String, Any]]("mpos", sql, rsc.resultSet2Map)
rows.runWith(Sink.lastOption)
}

首先我们需要把getUserInfo修改成futureUserInfo,然后传入cache.getOrLoad():

 /**
* Returns either the cached Future for the given key, or applies the given value loading
* function on the key, producing a `Future[V]`.
*/
def getOrLoad(key: K, loadValue: K => Future[V]): Future[V]

跟着我们再试试用akka-http的Directive, cache和alwaysCache。这两个是同一个东西,只是cache多了个是否使用缓存这么个控制,是通过request-header Cache-Control来实现的,如:Cache-Control`(`no-cache`)。cache函数是这样定义的;

def cache[K](cache: Cache[K, RouteResult], keyer: PartialFunction[RequestContext, K]): Directive0

这个函数返回Directive0, 可以直接对应 { ...   complete(...) },所以cache可以把一个route包嵌在里面如:

    cache(myCache, simpleKeyer) {
complete {
i +=
i.toString
}
}

simpleKeyer是个K对应函数:在我们这个例子里K -> Uri, Cache[Uri,RouteResult]。这里有个现成的构建器:routeCache[Uri]

  /**
* Creates an [[LfuCache]] with default settings obtained from the system's configuration.
*/
def routeCache[K](implicit s: ActorSystem): Cache[K, RouteResult] =
LfuCache[K, RouteResult](s)

不过这个LfuCache使用了application.conf里面的cachingSettings. 我们想直接控制lfuCache构建,所以可以用:

val lfuCache = LfuCache[Uri,RouteResult](cachingSettings)

alwaysCache的具体使用和上面的cache.getOrLoad相同:

import akka.http.scaladsl.model.{HttpMethods, StatusCodes, Uri}
import akka.http.scaladsl.util.FastFuture
import akka.http.caching.scaladsl.Cache
import akka.http.caching.scaladsl.CachingSettings
import akka.http.caching.LfuCache
import akka.http.scaladsl.server.RequestContext
import akka.http.scaladsl.server.RouteResult
import akka.http.scaladsl.server.directives.CachingDirectives._
import scala.concurrent.duration._
import scala.util._ val defaultCachingSettings = CachingSettings(sys)
val lfuCacheSettings = //最少使用排除算法缓存
defaultCachingSettings.lfuCacheSettings
.withInitialCapacity() //起始单位
.withMaxCapacity() //最大单位
.withTimeToLive(.hour) //最长存留时间
.withTimeToIdle(.minutes) //最长未使用时间
val cachingSettings =
defaultCachingSettings.withLfuCacheSettings(lfuCacheSettings) //Uri->key, RouteResult -> value
val lfuCache = LfuCache[Uri,RouteResult](cachingSettings) //Example keyer for non-authenticated GET requests
val simpleKeyer: PartialFunction[RequestContext, Uri] = {
val isGet: RequestContext => Boolean = _.request.method == HttpMethods.GET
// val isAuthorized: RequestContext => Boolean =
// _.request.headers.exists(_.is(Authorization.lowercaseName))
val result: PartialFunction[RequestContext, Uri] = {
case r: RequestContext if isGet(r) => r.request.uri
}
result
} val route = pathPrefix(pathName) {
pathPrefix("getuserinfo") {
(get & parameter('userid)) { userid => {
alwaysCache(lfuCache,simpleKeyer) {
onComplete(posRepo.futureUserInfo(userid)) {
_ match {
case Success(oui) => oui match {
case Some(ui) => complete(toJson(ui))
case None => complete(toJson(Map[String, Any](("TERMINALID" -> ""))))
}
case Failure(_) => complete(toJson(Map[String, Any](("TERMINALID" -> ""))))
}
}
}
}
}
} ~

好了,我觉着可能直接调用cache.getOrLoad会更好些,因为akka-http还在不停的变,java8caffein应该不会再调整了吧。

restapi(9)- caching, akka-http 缓存的更多相关文章

  1. System.Web.Caching.Cache类 缓存 各种缓存依赖

    原文:System.Web.Caching.Cache类 缓存 各种缓存依赖 Cache类,是一个用于缓存常用信息的类.HttpRuntime.Cache以及HttpContext.Current.C ...

  2. Lind.DDD.Caching分布式数据集缓存介绍

    回到目录 戏说当年 大叔原创的分布式数据集缓存在之前的企业级框架里介绍过,大家可以关注<我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器>,而今天主要对Lind.DDD.Cachin ...

  3. System.Web.Caching.Cache类 缓存

    1.文件缓存依赖 public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender ...

  4. System.Web.Caching.Cache类 缓存 各种缓存依赖(转)

    转自:http://www.cnblogs.com/kissdodog/archive/2013/05/07/3064895.html Cache类,是一个用于缓存常用信息的类.HttpRuntime ...

  5. C# System.Web.Caching.Cache类 缓存 各种缓存依赖

    原文:https://www.cnblogs.com/kissdodog/archive/2013/05/07/3064895.html Cache类,是一个用于缓存常用信息的类.HttpRuntim ...

  6. Spingboot整合Redis,用注解(@Cacheable、@CacheEvict、@CachePut、@Caching)管理缓存

    背景:项目从头开始,需结合Springboot和Redis 需求:用注解管理缓存 方法:     一.用Redis取代Springboot原有缓存 1.pom引入依赖     2.applicatio ...

  7. 缓存篇~第六回 Microsoft.Practices.EnterpriseLibrary.Caching实现基于方法签名的数据集缓存

    返回目录 这一讲中主要是说EnterpriseLibrary企业级架构里的caching组件,它主要实现了项目缓存功能,它支持四种持久化方式,内存,文件,数据库和自定义,对于持久化不是今天讨论的重要, ...

  8. EF 二级缓存 EFSecondLevelCache

    EFSecondLevelCache ======= Entity Framework .x Second Level Caching Library. 二级缓存是一个查询缓存.EF命令的结果将存储在 ...

  9. MVC实用架构设计(三)——EF-Code First(5):二级缓存

    前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...

  10. ASP.NET中的缓存机制

    ASP.NET 提供一个功能完整的缓存引擎,页面可使用该引擎通过 HTTP 请求存储和检索任意对象.缓存的生存期与应用程序的生存期相同,也就是说,当应用程序重新启动时,将重新创建缓存. 将数据添加到缓 ...

随机推荐

  1. 使用golang插入mysql性能提升经验

    前言 golang可以轻易制造高并发,在某些场景很合适,比如爬虫的时候可以爬的更加高效.但是对应某些场景,如文件读写,数据库访问等IO为瓶颈的场合,就没有什么优势了. 前提基础 1.golang数据库 ...

  2. 网络游戏开发-客户端1(开始Hello world)

    打开Egret Launcher ,新建一个EUI项目,起名为 EQiPai 这里需要勾选的是socket网络库,game游戏库.如果要面向海外用户的话,建议勾上Facebook的小游戏sdk. 然后 ...

  3. python-setup模块

    本地打包,setup安装 一.distutils 使用:distutils 进行打包,步骤如下,以单一文件为例. 1.创建文件 在同一目录下.写一个foo.py文件: #-*- coding:utf- ...

  4. 两种高效的事件处理模型:Reactor模式和Proactor模式

    随着IO多路复用技术的出现,出现了很多事件处理模式.同步I/O模型通常由Reactor模式实现,而异步I/O模型则由Proactor模式实现. Reactor模式: Reator类图如上所示,Reac ...

  5. idea 添加 注释 配置

    为类添加自动注释模版 File-->Settings-->Editor-->File and Code Templates 启用该模板才有效一定要勾上 /** * @author : ...

  6. django2.0+反向查询抛异常处理

    一.错误信息 AttributeError: 'RelatedManager' object has no attribute 'lrc' #其中RelatedManager为关键字 二.反向查询的字 ...

  7. 从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用

    标题:从零开始实现ASP.NET Core MVC的插件式开发(六) - 如何加载插件引用. 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/1171 ...

  8. Java8新特性之Lambda

    为什么要Lambda Java8应该是目前最大的一次更新了,更新后我们迎来了很多新特性,其中便包括Lambda表达式,函数式编程的思想正式进入Java,让我们看一个经典案例. 例1 按照两个人的年龄排 ...

  9. Vue框架构造

    Vue 程序结构框架 Vue.js是典型的MVVM框架,什么是MVVM框架,介绍之前我们先介绍下什么是MVC框架 MVC 即 Model-View-Controller 的缩写,就是 模型-视图-控制 ...

  10. c++11::std::remove_reference

    引用移除 : remove_reference 引用折叠规则 A& & 折叠成 A&  A& && 折叠成 A&  A&& &a ...