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. abp(net core)+easyui+efcore实现仓储管理系统——EasyUI之货物管理四 (二十二)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  2. A-07 前向分步算法

    目录 前向分步算法 一.前向分步算法引入 二.前向分步算法详解 2.1 加法模型 2.2 加法模型目标函数优化问题 三.前向分步算法流程 3.1 输入 3.2 输出 3.3 流程 更新.更全的< ...

  3. java生成32位UUID

    java生成32位UUID,具体代码如下: package com.fxsen.uuid; import java.util.UUID; /** * Copyright: Copyright (c) ...

  4. Redis系列(一):Redis简介

    一.Redis概述 Redis是一个开源(遵循BSD协议)Key-Value数据结构的内存存储系统,用作数据库.缓存和消息代理.它支持5种数据结构:字符串string.哈希hash.列表list.集合 ...

  5. (已解决)STM32L151使用串口发送数据第一字节为FE问题!

    最近学习到串口发送数据时遇到一个问题:第一个字节总是FE,后面才是对的数据. 最终解决的方法是: 讲GPIO复用的操作放到GPIO配置之前! 体现在代码中就是: //打开GPIO时钟 RCC_AHBP ...

  6. 渗透测试-基于白名单执行payload--Compiler

    复现亮神课程 0x01 Compiler前言 说明:Microsoft.Workflow.Comiler.exe是.NET Framework默认自带的一个实用工具,用户能够以XOML工作流文件的形式 ...

  7. TomCat中间件漏洞复现总汇

    TomCat中间件漏洞复现 前言 在渗透测试过程中,遇到php的站相对多一点,所以对apache了解的也多一点.TomCat中间件的站了解的比较少一点,这是自己第一次搭建环境测试漏洞,所以在自己摸索的 ...

  8. 【RabbitMQ 实战指南】一 过期时间TTL

    RabbitMQ 可以对消息和队列设置过期时间(TTL) 1.设置消息的TTL 目前有两种方式可以设置消息的TTL 第一种方式是通过队列属性设置,队列中所有消息都有相同的过期时间 第二种方式是对消息本 ...

  9. TCC推导过程

    svn 账号 yuanzn 密码:TCH5mb 项目分层 MapperDao 数据校验 throw new CloudBaseRuntimeException Helper 数据转换 manager ...

  10. 浅谈K-means聚类算法

    K-means算法的起源 1967年,James MacQueen在他的论文<用于多变量观测分类和分析的一些方法>中首次提出 “K-means”这一术语.1957年,贝尔实验室也将标准算法 ...