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 请求存储和检索任意对象.缓存的生存期与应用程序的生存期相同,也就是说,当应用程序重新启动时,将重新创建缓存. 将数据添加到缓 ...
随机推荐
- WebGL简易教程(十):光照
目录 1. 概述 2. 原理 2.1. 光源类型 2.2. 反射类型 2.2.1. 环境反射(enviroment/ambient reflection) 2.2.2. 漫反射(diffuse ref ...
- 数据的异构实战(一) 基于canal进行日志的订阅和转换
什么是数据的异构处理.简单说就是为了满足我们业务的扩展性,将数据从某种特定的格式转换到新的数据格式中来. 为什么会有这种需求出现呢? 传统的企业中,主要都是将数据存储在了关系型数据库中,例如说MySQ ...
- AVL树、红黑树以及B树介绍
简介 首先,说一下在数据结构中为什么要引入树这种结构,在我们上篇文章中介绍的数组与链表中,可以发现,数组适合查询这种静态操作(O(1)),不合适删除与插入这种动态操作(O(n)),而链表则是适合删除与 ...
- 点云之OpenMesh:error C1083:无法打开包括文件:“OpenMesh/Core/IO/MeshIO.hh”:No such file or directory
1.这是一个错误引发的惨案:错误提示如下: 在进行点云程序调试时,出现错误:[error C1083:无法打开包括文件:“OpenMesh/Core/IO/MeshIO.hh”:No such fil ...
- Linux 命令之 mkdir
mkdir的作用是创建一个目录,可以理解为 make directory 的缩写. 创建目录 mkdir dir_name 在当前目录创建一个名为 dir_name 的目录. 同时创建多级目录 假设现 ...
- UDP方式的传输
UDP 部分内容需要查文档学习,我们需要了解下面的两个类:java.net.DatagramSocket和java.net.DatagramPacket java.net.DatagramSocket ...
- JavaScript七宗罪和一些槽点
当下JavaScript越来越流行,成为长期霸语言榜前三的语言.但是实际上JavaScript是一个很丑陋有很多槽点的语言,这就是为什么新出了那么多框架(从jQuery到Vue)以及海尔斯伯格大大推出 ...
- Figures Inscribed in Curves (曲线上的图形)
Figures Inscribed in Curves\text{Figures Inscribed in Curves}Figures Inscribed in Curves A short tou ...
- 日志::spdlog
https://github.com/gabime/spdlog git clone https://github.com/gabime/spdlog.git cd spdlog && ...
- vue——父子组件间传值
(1)父组件给子组件传值(商品详情页): 根据订单类型,判断显示立即购买/立即拼单: 通过props来传递参数 父组件(商品详情页) 父组件调用子组件,在子组件的标签中,通过:数据名称=”数据”的形式 ...