guava

很多时候,我们将不得不从数据库或另一个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 }

响应部分对我们有用,如下所示:

有关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图书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

guava

guava_使用Google Guava Cache进行本地缓存相关推荐

  1. 使用Google Guava Cache进行本地缓存

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

  2. Google Guava Cache高效本地缓存

    目录 Guava Cache使用需求和场景 需求 场景 缓存设置 缓存的并发级别 缓存的初始容量设置 设置最大存储 缓存清除策略 基于存活时间的清除策略 基于容量的清除策略 基于权重的清除 策略 显式 ...

  3. java guava cache_java使用guava cache实现本地缓存

    cache = CacheBuilder.newBuilder() .recordStats() .maximumSize(5000000) .expireAfterWrite(1, TimeUnit ...

  4. 正则表达式 guava_带有正则表达式模式的Google Guava Cache

    正则表达式 guava 最近我看到了一个关于Google Guava的不错的介绍 ,我们在我们的项目中得出结论,使用它的缓存功能真的很有趣. 让我们看一下regexp Pattern类及其编译功能 . ...

  5. mysql caching_Spring Caching抽象和Google Guava Cache

    mysql caching Spring为缓存昂贵的方法调用提供了强大的现成支持. 这里详细介绍了缓存抽象. 我的目标是使用Spring Guava Cache涵盖Spring现在提供的4.0+版本的 ...

  6. Spring Caching抽象和Google Guava Cache

    Spring为缓存昂贵的方法调用提供了强大的开箱即用支持. 这里详细介绍了缓存抽象. 我的目的是要介绍Spring现在为框架的4.0+版本提供的较新的缓存实现之一-使用Google Guava Cac ...

  7. 带有正则表达式模式的Google Guava Cache

    最近我看到了一个关于Google Guava的精彩演讲 ,我们在我们的项目中得出结论,使用它的缓存功能真的很有趣. 让我们看一下regexp Pattern类及其编译功能 . 在代码中经常可以看到,每 ...

  8. (翻译)Google Guava Cache

    翻译自Google Guava Cache This Post is a continuation of my series on Google Guava, this time covering G ...

  9. Caffeine cache实现本地缓存(简单又清楚)

    Caffeine cache实现本地缓存题 缓存填充策略  手动加载  介绍:  使用方式:  同步加载  介绍:  使用方式:  异步加载  介绍:   注意: 异步和同步使用方式相似, 这里的话主 ...

最新文章

  1. Matlab数据的可视化 -- 线性图函数plot
  2. 清华学长免费分享Java基础核心知识大总结(1)
  3. 2021,让 AI 不再野蛮生长
  4. NetBpm 组织架构(4)
  5. python3中的编码与解码
  6. 10月第3周网络安全报告:新增信息安全漏洞308个
  7. JZOJ 5938. 【NOIP2018模拟10.30】分离计划
  8. hihocoder第229周:最大连续字母个数
  9. 基本数据类型和包装数据类型的使用标准
  10. Fibonacci斐波拉契数列----------动态规划DP
  11. 中英文对照 —— 标点符号(punctuation)
  12. c语言计算10亿位圆周率,C语言:圆周率的计算
  13. 【java初学】正则表达式和敏感词汇过滤
  14. 地下水环评(一级)实践技术及Modflow地下水数值模拟
  15. 微信开发者工具及其文档
  16. 16个精美的 HTML5 作品集网站设计案例
  17. html静态网页模板如何上传,html – 如何使用模板生成静态网页?
  18. php 设置允许跨域请求
  19. 六十星系之25廉贞天相坐子午
  20. 有得必有失,你该把技术做多细?

热门文章

  1. html5 兼容移动端参数设置的一些小细节
  2. 推荐一款带暂停功能的轮播组件,不要谢我,我叫红领巾!
  3. object-c 常见问题
  4. 37个我爱Ruby的理由
  5. linux centos yum 报错 one of the configured repositories failed 解决方法
  6. linux centos rc.local 自启动无效 解决方法
  7. docker 容器 导入 导出
  8. linux 文本 查看 搜索
  9. linux shell 获取当前正在执行脚本的绝对路径
  10. Duilib技巧:背景图片平铺