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 请求存储和检索任意对象.缓存的生存期与应用程序的生存期相同,也就是说,当应用程序重新启动时,将重新创建缓存. 将数据添加到缓 ...
随机推荐
- TCP/UDP的小事情
UDP: 没有复杂的控制机制,面向无连接的通信服务. 常用于: 包总量少的通信 音视频传输(即时通信) TCP: 对传输.发送.通信.进行控制的协议.面向有连接的协议,只有在确认通信对端存在时才会发送 ...
- Java入门之File类和IO流
1.File类 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建.查找和删除等操作 . 构造方法: public File(String pathname) :通过将 ...
- Azure pipeline 配置根据条件执行脚本
Azure pipeline 配置根据条件执行脚本 Intro 我的应用通过 azure pipeline 来做持续集成,之前已经介绍了根据不同分支去打包不同的package,具体的就不再这里详细介绍 ...
- MySQL 和 Navicat Premium 下载及安装全过程
前言: 我对 “MySQL社区版” 的理解是:它只是一个后台服务,它的管理需要用到其他的数据库管理软件,这里我用的是 Navicat Premium,这个软件可以同时为多个数据库提供管理,比如MySQ ...
- redis相关缓存知识
Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...
- 我们一起学Python之——认识Python"规则"
前言: 开学后,跟预想的一样,开学第一天我们就开了Python,虽然之前早就预料到了,但对于一直学Java的我来说,内心还是有一些涟漪的.总归还是要接受的,还不如振作起来,认真对待.我决定从最简单并且 ...
- wamp server mysql数据库中事件不执行的解决办法
先看看看event 事件是否开启 直接执行下列语句即可, show variables like '%sche%'; 如没开启,则开启. (需要数据库超级权限) set global event_sc ...
- C语言-正序输出一个一个多位数
//正序输出一个多位数,所有的数字中间用空格分隔 int main() { ;//是可变化的 ; int d; int t =x; //先计算x的位数 ){ t /= ; mask *=; } pri ...
- sql数据文件导入数据库
1.首先通过xshell连接数据库服务器,执行命令mysql -u root -p 命令,按照提示输入密码.连接上数据库. 2.在连接终端上执行命令create database JD_Model; ...
- SDL建设-三方依赖库扫描
说明: 本文首发自 https://www.secpulse.com/archives/73373.html,转载请注明出处. 文章综述 本文主要介绍Dependency-Check工具的工作原理和使 ...