restapi(9)- caching, akka-http 缓存
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 缓存的更多相关文章
- System.Web.Caching.Cache类 缓存 各种缓存依赖
原文:System.Web.Caching.Cache类 缓存 各种缓存依赖 Cache类,是一个用于缓存常用信息的类.HttpRuntime.Cache以及HttpContext.Current.C ...
- Lind.DDD.Caching分布式数据集缓存介绍
回到目录 戏说当年 大叔原创的分布式数据集缓存在之前的企业级框架里介绍过,大家可以关注<我心中的核心组件(可插拔的AOP)~第二回 缓存拦截器>,而今天主要对Lind.DDD.Cachin ...
- System.Web.Caching.Cache类 缓存
1.文件缓存依赖 public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender ...
- System.Web.Caching.Cache类 缓存 各种缓存依赖(转)
转自:http://www.cnblogs.com/kissdodog/archive/2013/05/07/3064895.html Cache类,是一个用于缓存常用信息的类.HttpRuntime ...
- C# System.Web.Caching.Cache类 缓存 各种缓存依赖
原文:https://www.cnblogs.com/kissdodog/archive/2013/05/07/3064895.html Cache类,是一个用于缓存常用信息的类.HttpRuntim ...
- Spingboot整合Redis,用注解(@Cacheable、@CacheEvict、@CachePut、@Caching)管理缓存
背景:项目从头开始,需结合Springboot和Redis 需求:用注解管理缓存 方法: 一.用Redis取代Springboot原有缓存 1.pom引入依赖 2.applicatio ...
- 缓存篇~第六回 Microsoft.Practices.EnterpriseLibrary.Caching实现基于方法签名的数据集缓存
返回目录 这一讲中主要是说EnterpriseLibrary企业级架构里的caching组件,它主要实现了项目缓存功能,它支持四种持久化方式,内存,文件,数据库和自定义,对于持久化不是今天讨论的重要, ...
- EF 二级缓存 EFSecondLevelCache
EFSecondLevelCache ======= Entity Framework .x Second Level Caching Library. 二级缓存是一个查询缓存.EF命令的结果将存储在 ...
- MVC实用架构设计(三)——EF-Code First(5):二级缓存
前言 今天我们来谈谈EF的缓存问题. 缓存对于一个系统来说至关重要,但是是EF到版本6了仍然没有见到有支持查询结果缓存机制的迹象.EF4开始会把查询语句编译成存储过程缓存在Sql Server中,据说 ...
- ASP.NET中的缓存机制
ASP.NET 提供一个功能完整的缓存引擎,页面可使用该引擎通过 HTTP 请求存储和检索任意对象.缓存的生存期与应用程序的生存期相同,也就是说,当应用程序重新启动时,将重新创建缓存. 将数据添加到缓 ...
随机推荐
- 用到的Dos命令总结 持续更新
1.xcopy命令:复制的扩展命令 常用参数:/s:复制空文件夹 不使用可能会造成文件混乱 /y忽略覆盖提示 使用/y会直接覆盖全部 例子:xcopy lark-UI\dist C:\U ...
- ES三节点重启后报错no known master node
问题 一直在研究ES的监控怎么做,想偷点懒,不去通过API获取然后计算,就想找个现成的插件或者监控软件,只要装个agent就可以,然后就找到了x-pack,插件装好了之后,需要重启ES集群,线上的ES ...
- CountDownLatch源码及Javadoc阅览
/** * A synchronization aid that allows one or more threads to wait until * a set of operations bein ...
- Vue使用vue-recoure + http-proxy-middleware + vuex配合promise实现基本的跨域请求封装
使用vue init webpack 你的项目名称初始化一个vue的项目 安装依赖 npm install vue-resource http-proxy-middleware vuex koa 在项 ...
- [NOIp2009] luogu P1072 Hankson 的趣味题
把 c 改成 d 下了两个点. 题目描述 已知正整数 a0,a1,b0,b1a_0,a_1,b_0,b_1a0,a1,b0,b1,设某未知正整数 xxx 满足: xxx 和 a0a_0a0 ...
- [网络流 24 题] luoguP2756 飞行员配对方案问题
[返回网络流 24 题索引] 题目描述 英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相配合的 222 名飞行员,其中 111 名是英国飞行员,另 ...
- Controller层的方法访问标志与Spring装配与AspectJ切面处理
最近在做AspectJ实现的日志模块,在spring配置中加入了<aop:aspectj-autoproxy/>,之后发现,只要有用到自定义注解的类,某些方法经MVC请求时就报空指针错误. ...
- Halcon一日一练:获取图像属性
从图像属性我们可以了解图像的基本信息,比如大小,高度,指针等. Halcon提供了获取图像属性的算子. 我们来看看下面例子: **获取图像属性 read_image(Image,'Clip') dev ...
- 同一台机上配置多个redis服务
"D:\Program Files\Redis\redis-server.exe" --service-run F:Redis2\redis.windows-service6380 ...
- epoll--IO多路复用
理解 epoll 过程 #include <stdio.h> #include <stdlib.h> #include <string.h> #include &l ...