使用Google Guava Cache进行本地缓存

很多时候,我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据。 在涉及网络呼叫的情况下,将存在固有的网络等待时间,网络带宽限制。 解决此问题的方法之一是在应用程序本地拥有一个缓存。

如果您的应用程序跨越多个节点,则缓存将位于每个节点本地,从而导致固有的数据不一致。 可以权衡此数据不一致以提高吞吐量和降低延迟。 但是有时,如果数据不一致会产生重大差异,则可以减少缓存对象的ttl(生存时间),从而减少数据不一致可能发生的持续时间。

在实现本地缓存的多种方法中,我在高负载环境中使用的一种方法是Guava缓存。 我们使用了番石榴缓存来每秒处理80,000个以上的请求。 延迟的90%约为5毫秒。 这帮助我们扩展了有限的网络带宽需求。

在本文中,我将展示如何添加一层Guava缓存以避免频繁的网络呼叫。 为此,我选择了一个非常简单的示例,该示例使用Google Books API给出了图书的 ISBN来获取图书的详细信息。

使用ISBN13字符串获取图书详细信息的示例请求为: https : //www.googleapis.com/books/v1/volumes? q = isbn:9781449370770 & key ={ API_KEY }

对我们有用的部分响应如下:

SampleResponse

有关Guava Cache功能的非常详细的说明,请参见此处 。 在此示例中,我将使用LoadingCache。 LoadingCache接收一个代码块,该代码块用于将数据加载到缓存中以查找丢失的密钥。 因此,当您使用不存在的键进行缓存时,LoadingCache将使用CacheLoader提取数据并将其设置在缓存中,然后将其返回给调用方。

现在让我们看一下表示书籍详细信息所需的模型类:

  • 书本类
  • 作者班级

Book类定义为:

//Book.java
package info.sanaulla.model;import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class Book {private String isbn13;private List<Author> authors;private String publisher;private String title;private String summary;private Integer pageCount;private String publishedDate;public String getIsbn13() {return isbn13;}public void setIsbn13(String isbn13) {this.isbn13 = isbn13;}public List<Author> getAuthors() {return authors;}public void setAuthors(List<Author> authors) {this.authors = authors;}public String getPublisher() {return publisher;}public void setPublisher(String publisher) {this.publisher = publisher;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getSummary() {return summary;}public void setSummary(String summary) {this.summary = summary;}public void addAuthor(Author author){if ( authors == null ){authors = new ArrayList<Author>();}authors.add(author);}public Integer getPageCount() {return pageCount;}public void setPageCount(Integer pageCount) {this.pageCount = pageCount;}public String getPublishedDate() {return publishedDate;}public void setPublishedDate(String publishedDate) {this.publishedDate = publishedDate;}
}

而Author类的定义为:

//Author.java
package info.sanaulla.model;public class Author {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}

现在让我们定义一个服务,该服务将从Google Books REST API中获取数据,并将其称为BookService。 该服务执行以下操作:

  1. 从REST API获取HTTP响应。
  2. 使用Jackson的ObjectMapper将JSON解析为Map。
  3. 从步骤2中获得的地图中获取相关信息。

我已经从BookService中提取了一些操作到Util类中,即:

  1. 读取包含Google图书API密钥的application.properties文件(我尚未将该文件提交到git存储库。但是可以将此文件添加到其src / main / resources文件夹中,并将该文件命名为application.properties和Util API将能够为您阅读)
  2. 向REST API发出HTTP请求并返回JSON响应。

以下是Util类的定义方式:

//Util.javapackage info.sanaulla;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;public class Util {private static ObjectMapper objectMapper = new ObjectMapper();private static Properties properties = null;public static ObjectMapper getObjectMapper(){return objectMapper;}public static Properties getProperties() throws IOException {if ( properties != null){return  properties;}properties = new Properties();InputStream inputStream = Util.class.getClassLoader().getResourceAsStream("application.properties");properties.load(inputStream);return properties;}public static String getHttpResponse(String urlStr) throws IOException {URL url = new URL(urlStr);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setRequestProperty("Accept", "application/json");conn.setConnectTimeout(5000);//conn.setReadTimeout(20000);if (conn.getResponseCode() != 200) {throw new RuntimeException("Failed : HTTP error code : "+ conn.getResponseCode());}BufferedReader br = new BufferedReader(new InputStreamReader((conn.getInputStream())));StringBuilder outputBuilder = new StringBuilder();String output;while ((output = br.readLine()) != null) {outputBuilder.append(output);}conn.disconnect();return outputBuilder.toString();}
}

因此,我们的Service类如下所示:

//BookService.java
package info.sanaulla.service;import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Optional;
import com.google.common.base.Strings;import info.sanaulla.Constants;
import info.sanaulla.Util;
import info.sanaulla.model.Author;
import info.sanaulla.model.Book;import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;public class BookService {public static Optional<Book> getBookDetailsFromGoogleBooks(String isbn13) throws IOException{Properties properties = Util.getProperties();String key = properties.getProperty(Constants.GOOGLE_API_KEY);String url = "https://www.googleapis.com/books/v1/volumes?q=isbn:"+isbn13;String response = Util.getHttpResponse(url);Map bookMap = Util.getObjectMapper().readValue(response,Map.class);Object bookDataListObj = bookMap.get("items");Book book = null;if ( bookDataListObj == null || !(bookDataListObj instanceof List)){return Optional.fromNullable(book);}List bookDataList = (List)bookDataListObj;if ( bookDataList.size() < 1){return Optional.fromNullable(null);}Map bookData = (Map) bookDataList.get(0);Map volumeInfo = (Map)bookData.get("volumeInfo");book = new Book();book.setTitle(getFromJsonResponse(volumeInfo,"title",""));book.setPublisher(getFromJsonResponse(volumeInfo,"publisher",""));List authorDataList = (List)volumeInfo.get("authors");for(Object authorDataObj : authorDataList){Author author = new Author();author.setName(authorDataObj.toString());book.addAuthor(author);}book.setIsbn13(isbn13);book.setSummary(getFromJsonResponse(volumeInfo,"description",""));book.setPageCount(Integer.parseInt(getFromJsonResponse(volumeInfo, "pageCount", "0")));book.setPublishedDate(getFromJsonResponse(volumeInfo,"publishedDate",""));return Optional.fromNullable(book);}private static String getFromJsonResponse(Map jsonData, String key, String defaultValue){return Optional.fromNullable(jsonData.get(key)).or(defaultValue).toString();}
}

在Google Books API调用的顶部添加缓存

我们可以使用Guava库提供的CacheBuilder API创建一个缓存对象。 它提供了设置属性的方法,例如

  • 缓存中的最大项目数
  • 基于缓存对象的上次写入时间或上次访问时间的生存时间,
  • ttl用于刷新缓存对象,
  • 在缓存中记录统计信息,例如命中,未命中,加载时间和
  • 提供加载程序代码以在高速缓存未命中或高速缓存刷新的情况下获取数据。

因此,我们理想地希望的是,缓存未命中应调用上面编写的API,即getBookDetailsFromGoogleBooks。 我们希望最多存储1000个项目,并在24小时后使这些项目过期。 因此,构建缓存的代码如下:

private static LoadingCache<String, Optional<Book>> cache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(24, TimeUnit.HOURS).recordStats().build(new CacheLoader<String, Optional<Book>>() {@Overridepublic Optional<Book> load(String s) throws IOException {return getBookDetailsFromGoogleBooks(s);}});

重要的是要注意,要存储在缓存中的最大项目会影响应用程序使用的堆。 因此,您必须根据要缓存的每个对象的大小以及分配给应用程序的最大堆内存来仔细确定该值。

让我们付诸实践,并查看缓存统计信息如何报告统计信息:

package info.sanaulla;import com.google.common.cache.CacheStats;
import info.sanaulla.model.Book;
import info.sanaulla.service.BookService;import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ExecutionException;public class App 
{public static void main( String[] args ) throws IOException, ExecutionException {Book book = BookService.getBookDetails("9780596009205").get();System.out.println(Util.getObjectMapper().writeValueAsString(book));book = BookService.getBookDetails("9780596009205").get();book = BookService.getBookDetails("9780596009205").get();book = BookService.getBookDetails("9780596009205").get();book = BookService.getBookDetails("9780596009205").get();CacheStats cacheStats = BookService.getCacheStats();System.out.println(cacheStats.toString());}
}

我们将得到的输出是:

{"isbn13":"9780596009205","authors":[{"name":"Kathy Sierra"},{"name":"Bert Bates"}],"publisher":"\"O'Reilly Media, Inc.\"","title":"Head First Java","summary":"An interactive guide to the fundamentals of the Java programming language utilizes icons, cartoons, and numerous other visual aids to introduce the features and functions of Java and to teach the principles of designing and writing Java programs.","pageCount":688,"publishedDate":"2005-02-09"}
CacheStats{hitCount=4, missCount=1, loadSuccessCount=1, loadExceptionCount=0, totalLoadTime=3744128770, evictionCount=0}

这是Guava缓存的非常基本的用法,我在学习使用它时就写了它。 在本文中,我利用了诸如Optional之类的其他Guava API,该API有助于将现有或不存在的(null)值包装到对象中。 可以在git hub- https://github.com/sanaulla123/Guava-Cache-Demo上找到此代码。 会有一些担忧,例如它如何处理并发,而我没有详细介绍。 但是在后台,它使用分段的并发哈希图,因此获取始终是非阻塞的,但是并发写入的数量将由分段的数量决定。

一些与此相关的有用链接: http : //guava-libraries.googlecode.com/files/ConcurrentCachingAtGoogle.pdf

翻译自: https://www.javacodegeeks.com/2015/01/using-google-guava-cache-for-local-caching.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAX-RS 2.0:服务器端处理管道

这篇文章的灵感来自JAX-RS 2.0规范文档 &#xff08;附录C&#xff09;中的Processing Pipeline部分。 我喜欢它是因为它提供了JAX-RS中所有模块的漂亮快照-以准备好吞咽的胶囊形式&#xff01; 礼貌– JAX-RS 2.0规范文档 因此&#xff0c;我想到了使用此图简要概述不同的JA…

基于TCP/IP的文件服务器编程一例

来源&#xff0c;华清远见嵌入式学院实验手册&#xff0c;代码来源&#xff1a;华清远见曾宏安 实现的功能&#xff1a; 编写TCP文件服务器和客户端。客户端可以上传和下载文件 客户端支持功能如下&#xff1a; 1.支持一下命令 help 显示客户端所有命令和说明 list 显示服务器…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch、Zookeeper、Kafka、NoSQL等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

JDK 7和JDK 8中大行读取速度较慢的原因

我之前发布了博客文章“使用JDK 7和JDK 8读取慢速行”&#xff0c;并且在该问题上有一些有用的评论来描述该问题。 这篇文章提供了更多解释&#xff0c;说明为何该文章中演示的文件读取&#xff08;并由Ant的LineContainsRegExp使用 &#xff09;在Java 7和Java 8中比在Java 6中…

Spring Stateless State Security第3部分:JWT +社会认证

我的Stateless Spring Security系列文章的第三部分也是最后一部分是关于将基于JWT令牌的身份验证与spring-social-security混合在一起的。 这篇文章直接建立在此基础上&#xff0c;并且主要集中在已更改的部分上。 想法是使用基于OAuth 2的“使用Facebook登录”功能来替换基于用…

nyoj239 月老的难题 二分图 匈牙利算法

月老的难题 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;4描述月老准备给n个女孩与n个男孩牵红线&#xff0c;成就一对对美好的姻缘。 现在&#xff0c;由于一些原因&#xff0c;部分男孩与女孩可能结成幸福的一家&#xff0c;部分可能不会结…

Web应用程序体系结构– Spring MVC – AngularJs堆栈

Spring MVC和AngularJs共同为构建表单密集型Web应用程序提供了一个真正高效且吸引人的前端开发堆栈。在这篇博客文章中&#xff0c;我们将看到如何使用这些技术构建表单密集型Web应用程序&#xff0c;并将这种方法与其他方法进行比较可用选项。 可以在此github 存储库中找到功能…

antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function

问题描述&#xff1a; antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function 原因分析&#xff1a; 在From中渲染默认值&#xff0c;一般数据请求拿到返回值存在异步&#xff0c;会晚于渲染&#xff0c;因此日期转换不能放在DatePi…

集成CDI和WebSockets

考虑尝试一个简单的Java EE 7原型应用程序&#xff0c;该应用程序涉及JAX-RS&#xff08;REST&#xff09;&#xff0c;WebSockets和CDI。 注意 &#xff1a;不想让它成为破坏者-但这篇文章主要讨论了我在尝试使用Web套接字和使用CDI作为“胶水”的REST&#xff08;在Java EE应…

Java中连接字符串的最佳方法

最近有人问我这个问题–在Java中使用运算符连接字符串是否对性能不利&#xff1f; 这让我开始思考Java中连接字符串的不同方法&#xff0c;以及它们如何相互对抗。 这些是我要研究的方法&#xff1a; 使用运算符 使用StringBuilder 使用StringBuffer 使用String.concat() …

十大最常见的Java性能问题

Java性能是所有Java应用程序开发人员都关心的问题&#xff0c;因为快速使应用程序与使其正常运行同等重要。 史蒂文海恩斯&#xff08;Steven Haines&#xff09;使用他在Java性能问题上的个人经验得出的结论是&#xff0c; 大多数问题都有共同的根本原因 。 因此&#xff0c;作…

Unity3D 访问Access数据库

Unity3D 访问Access数据库 在开始这个小教程之前呢&#xff0c;其实在网上你已经可以找到相关的资料了&#xff0c;但是我还是要把我自己做练习的一点东西分享出来。写这个教程的主要原因呢&#xff0c;是一个朋友在u3d的官网论坛里&#xff0c;找到了这个demo&#xff0c;但是…

LaTeX 基础笔记。开篇

LaTeX 的起源非常牛逼&#xff0c;有一套书大家可能听说过《计算机程序设计艺术》&#xff0c;写了好几本。当然能在计算机方面写上艺术俩字的书恐怕不是我们一般人能读懂得东西了。他的作者在1976年准备写第二卷的时候发现计算机的排版非常难看&#xff0c;所以&#xff0c;为…

Java旧版不断发展

我最近偶然发现了JDK API的一个非常有趣的警告&#xff0c;即Class.getConstructors()方法。 它的方法签名是这样的&#xff1a; Constructor<?>[] getConstructors()有趣的是&#xff0c; Class.getConstructor(Class...)返回一个Constructor<T> &#xff0c;并…

带Lambda表达式的Apache Wicket

这是怎么回事&#xff1f; :) 我一直在从事一些项目&#xff0c;这些项目值得庆幸的是将Apache Wicket用于表示层。 我自然想到Java的8个lambda表达式如何与Wicket完美匹配。 而不仅仅是我&#xff0c; Wicket团队似乎已经在努力更改API&#xff0c;以为开箱即用的lambda提供支…

装饰者模式如何拯救了我的一天

在工作中&#xff0c;我正在处理庞大的Java代码库&#xff0c;该代码库是由许多不同的开发人员在15年的时间里开发的。 并不是所有的事情都由书来完成&#xff0c;但是同时我通常没有机会重构遇到的每一个奇怪之处。 尽管如此&#xff0c;仍可以每天采取提高代码质量的措施。 …

快速的骆驼和云消息传递

Apache Camel是一个流行的&#xff0c;成熟的开源集成库。 它实现了企业集成模式 &#xff0c;这是在集成分布式系统时经常出现的一组模式。 过去&#xff0c;我写过很多关于Camel的文章&#xff0c; 包括为什么我比Spring Integration更喜欢它 &#xff0c; 路由引擎 如何 工作…

三角形类1

/* 程序的版权和版本声明部分 Copyright (c)2012, 烟台大学计算机学院学生 All rightsreserved. 文件名称&#xff1a; object.cpp 作者&#xff1a;刘清远 完成日期&#xff1a; 2013年3月29日 版本号&#xff1a; v1.0 输入描述&#xff1a;无 问题描述&#xff1a;设计求三…

android 自定义xml属性

Android 自定义组件 Android 提供了非常精致的和非常强大的组件化模型&#xff0c;能够更加方便的构建UI,这些UI组件都是基于基本的layout类:View 和 ViewGroup。 部分能够用的widgets包括&#xff1a;Button&#xff0c;TextView,EditText,ListView,CheckBox&#xff0c;Radio…

LeetCode: Longest Common Prefix

string.erase没掌握好&#xff0c;悲了个剧&#xff0c;2次过 1 class Solution {2 public:3 string longestCommonPrefix(vector<string> &strs) {4 // Start typing your C/C solution below5 // DO NOT write int main() function6 s…