文档章节

Spring Boot 使用 Redis 提升天气预报应用的并发访问能力

waylau
 waylau
发布于 02/12 23:35
字数 2224
阅读 862
收藏 55

有时,为了提升整个网站的性能,我们会将经常需要访问数据缓存起来,这样,在下次查询的时候,能快速的找到这些数据。 缓存的使用与系统的时效性有着非常大的关系。当我们的系统时效性要求不高时,则选择使用缓存是极好的。当系统要求的时效性比较高时,则并不适合用缓存。 本文,我们将演示如何通过集成 Redis 服务器来进行数据的缓存,以提高微服务的并发访问能力。

为啥我们需要缓存

在之前的文章中,我们已经介绍了如何使用 Spring Boot 来快速实现一个天气预报服务应用micro-weather-basic(见 https://waylau.com/spring-boot-weather-report/)。通过该应用,能实现简单的天气查询。

天气数据接口,本身时效性不是很高,而且又因为是 Web 服务,在调用过程中,本身是存在延时的。所以,采用缓存,一方面可以有效减轻访问天气接口服务带来的延时问题,另一方面,也可以减轻天气接口的负担,提高并发访问量。

特别地,我们是使用的第三方免费的天气 API,这些 API 往往对用户的调用次数及频率有一定的限制。所以为了减轻天气 API 提供方的负荷,我们并不需要实时去调用其第三方接口。

micro-weather-basic的基础上,我们构建了一个micro-weather-redis项目,作为示例。

开发环境

为了演示本例子,需要采用如下开发环境:

  • JDK 8
  • Gradle 4.0
  • Spring Boot Web Starter 2.0.0.M4
  • Apache HttpClient 4.5.3
  • Spring Boot Data Redis Starter 2.0.0.M4
  • Redis 3.2.100

项目配置

Spring Boot Data Redis 提供了 Spring Boot 对 Redis 的开箱即用的功能。在原有的依赖的基础上,添加 Spring Boot Data Redis Starter 的依赖。

// 依赖关系
dependencies {
	//...
 
	// 添加 Spring Boot Data Redis Starter 依赖
	compile('org.springframework.boot:spring-boot-starter-data-redis')

 	//...
}

下载安装、运行 Redis

在 Linux 平台上安装 Redis 比较简单,可以参考官方文档来即可,详见https://github.com/antirez/redis

而在 Windows 平台,微软特别为 Redis 制作了安装包,下载地址见 https://github.com/MicrosoftArchive/redis/releases。本书所使用的案例,也是基于该安装包来进行的。双击 redis-server.exe 文件,就能快速启动 Redis 服务器了。

安装后,Redis 默认运行在 localhost:6379 地址端口

修改 WeatherDataServiceImpl

修改 WeatherDataServiceImpl,增加了 StringRedisTemplate 用于操作 Redis。

@Service
public class WeatherDataServiceImpl implements WeatherDataService {

	private final static Logger logger = LoggerFactory.getLogger(WeatherDataServiceImpl.class);  
	
	@Autowired
	private RestTemplate restTemplate;
	
	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	
	private final String WEATHER_API = "http://wthrcdn.etouch.cn/weather_mini";

	private final Long TIME_OUT = 1800L; // 缓存超时时间
	
	@Override
	public WeatherResponse getDataByCityId(String cityId) {
		String uri = WEATHER_API + "?citykey=" + cityId;
		return this.doGetWeatherData(uri);
	}

	@Override
	public WeatherResponse getDataByCityName(String cityName) {
		String uri = WEATHER_API + "?city=" + cityName;
		return this.doGetWeatherData(uri);
	}

	private WeatherResponse doGetWeatherData(String uri) {
		ValueOperations<String, String> ops = this.stringRedisTemplate.opsForValue();
		String key = uri; // 将调用的 URI 作为缓存的key
		String strBody = null;

		// 先查缓存,没有再查服务
		if (!this.stringRedisTemplate.hasKey(key)) {
			logger.info("未找到 key " + key);
			
			ResponseEntity<String> response = restTemplate.getForEntity(uri, String.class);
			

			if (response.getStatusCodeValue() == 200) {
				strBody = response.getBody();
			}

			ops.set(key, strBody, TIME_OUT, TimeUnit.SECONDS);
		} else {
			logger.info("找到 key " + key + ", value=" + ops.get(key));
			
			strBody = ops.get(key);
		}

		ObjectMapper mapper = new ObjectMapper();
		WeatherResponse weather = null;

		try {
			weather = mapper.readValue(strBody, WeatherResponse.class);
		} catch (IOException e) {
			logger.error("JSON反序列化异常!",e);
		}

		return weather;
	}

}

修改了 doGetWeatherData 方法,增加了 Redis 数据的判断:

  • 当存在某个key(天气接口的URI,是唯一代表某个地区的天气数据)时,我们就从 Redis 里面拿缓存数据;
  • 当不存在某个key(没有初始化数据或者数据过期了),从去天气接口里面去取最新的数据,并初始化到 Redis 中;
  • 由于天气数据更新频率的特点(基本上一个小时或者半个小时更新一次),所以,我们在 Redis 里面设置了30分钟的超时时间。

其中,当然 StringRedisTemplate 跟 RedisTemplate 功能类似,都是封装了对 Redis 的一些常用的操作。区别在于,StringRedisTemplate 更加专注于基于字符串的操作,毕竟,在目前咱们的天气预报应用中,数据的格式主要是 JSON 字符串。

ValueOperations 接口,封装了大部分的简单的 K-V 操作。

同时,我们也使用了日志框架,用来记录运行过程,及日常的信息。

测试、运行

首先,在进行测试前,需要将 Redis 服务器启动起来。

我们可以通过在一个时间段内,多次访问同一个个天气接口来测试效果,比如,接口 http://localhost:8080/weather/cityId/101280601。为了缩短测试的时间,我们可以将 Redis 的超时时间缩短一点,比如10秒。这样,你就不用苦等30分钟才能验证数据是否过期了。

2017-10-18 23:51:41.762  INFO 15220 --- [nio-8080-exec-6] c.w.s.c.w.s.WeatherDataServiceImpl       : 未找到 key http://wthrcdn.etouch.cn/weather_mini?citykey=101280601
2017-10-18 23:51:43.649  INFO 15220 --- [nio-8080-exec-5] c.w.s.c.w.s.WeatherDataServiceImpl       : 找到 key http://wthrcdn.etouch.cn/weather_mini?citykey=101280601, value={"data":{"yesterday":{"date":"17日星期二","high":"高温 29℃","fx":"无持续风向","low":"低温 22℃","fl":"<![CDATA[<3级]]>","type":"多云"},"city":"深圳","aqi":"35","forecast":[{"date":"18日星期三","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 23℃","fengxiang":"无持续风向","type":"多云"},{"date":"19日星期四","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 23℃","fengxiang":"无持续风向","type":"多云"},{"date":"20日星期五","high":"高温 30℃","fengli":"<![CDATA[<3级]]>","low":"低温 22℃","fengxiang":"无持续风向","type":"多云"},{"date":"21日星期六","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"无持续风向","type":"多云"},{"date":"22日星期天","high":"高温 28℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"无持续风向","type":"多云"}],"ganmao":"各项气象条件适宜,无明显降温过程,发生感冒机率较低。","wendu":"25"},"status":1000,"desc":"OK"}
2017-10-18 23:51:46.700  INFO 15220 --- [nio-8080-exec-7] c.w.s.c.w.s.WeatherDataServiceImpl       : 找到 key http://wthrcdn.etouch.cn/weather_mini?citykey=101280601, value={"data":{"yesterday":{"date":"17日星期二","high":"高温 29℃","fx":"无持续风向","low":"低温 22℃","fl":"<![CDATA[<3级]]>","type":"多云"},"city":"深圳","aqi":"35","forecast":[{"date":"18日星期三","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 23℃","fengxiang":"无持续风向","type":"多云"},{"date":"19日星期四","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 23℃","fengxiang":"无持续风向","type":"多云"},{"date":"20日星期五","high":"高温 30℃","fengli":"<![CDATA[<3级]]>","low":"低温 22℃","fengxiang":"无持续风向","type":"多云"},{"date":"21日星期六","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"无持续风向","type":"多云"},{"date":"22日星期天","high":"高温 28℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"无持续风向","type":"多云"}],"ganmao":"各项气象条件适宜,无明显降温过程,发生感冒机率较低。","wendu":"25"},"status":1000,"desc":"OK"}
2017-10-18 23:51:50.513  INFO 15220 --- [nio-8080-exec-8] c.w.s.c.w.s.WeatherDataServiceImpl       : 找到 key http://wthrcdn.etouch.cn/weather_mini?citykey=101280601, value={"data":{"yesterday":{"date":"17日星期二","high":"高温 29℃","fx":"无持续风向","low":"低温 22℃","fl":"<![CDATA[<3级]]>","type":"多云"},"city":"深圳","aqi":"35","forecast":[{"date":"18日星期三","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 23℃","fengxiang":"无持续风向","type":"多云"},{"date":"19日星期四","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 23℃","fengxiang":"无持续风向","type":"多云"},{"date":"20日星期五","high":"高温 30℃","fengli":"<![CDATA[<3级]]>","low":"低温 22℃","fengxiang":"无持续风向","type":"多云"},{"date":"21日星期六","high":"高温 29℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"无持续风向","type":"多云"},{"date":"22日星期天","high":"高温 28℃","fengli":"<![CDATA[<3级]]>","low":"低温 21℃","fengxiang":"无持续风向","type":"多云"}],"ganmao":"各项气象条件适宜,无明显降温过程,发生感冒机率较低。","wendu":"25"},"status":1000,"desc":"OK"}
2017-10-18 23:51:53.140  INFO 15220 --- [nio-8080-exec-9] c.w.s.c.w.s.WeatherDataServiceImpl       : 未找到 key http://wthrcdn.etouch.cn/weather_mini?citykey=101280601

从上述日志可以看到,第一次(23:51:41)访问接口时,没有找到 Redis 里面的数据,所以,就初始化了数据。 后面几次访问,都是访问 Redis 里面的数据。最后一次(23:51:53),由于超时了,所以 Redis 里面又没有数据了,所以又会拿天气接口的数据。

源码

本章节的源码,见 https://github.com/waylau/spring-cloud-tutorial/ samples目录下的 micro-weather-redis目录下。

参考资料

© 著作权归作者所有

共有 人打赏支持
waylau

waylau

粉丝 478
博文 86
码字总数 159744
作品 2
深圳
架构师
加载中

评论(8)

黑暗骑士C
黑暗骑士C
谢谢,楼主写得很好。Java主流技术分享:SpringBoot、SpringCloud、Docker、dubbo、redis、多线程高并发、数据库性能调优等等。https://charllee.github.io/2018/02/27/Java%E6%9C%80%E6%96%B0%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99-2018/
黑暗骑士C
黑暗骑士C
谢谢,楼主写得很好。Java主流技术分享:SpringBoot、SpringCloud、Docker、dubbo、redis、多线程高并发、数据库性能调优等等。https://charllee.github.io/2018/02/27/Java%E6%9C%80%E6%96%B0%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99-2018/
waylau
waylau

引用来自“sliver9530”的评论

看了某网站实战项目,然后把代码拿过来?
可以理解为实战项目的课程笔记,本来就是同个作者。
waylau
waylau

引用来自“Leoops”的评论

用 spring cache实现更容易
额,是的
s
sliver9530
看了某网站实战项目,然后把代码拿过来?
Leoops
Leoops
用 spring cache实现更容易
waylau
waylau

引用来自“A_NOOB”的评论

提供qps啥的 更有说服力吧
这个只能是上生产环境才能拿到真实的数据~ 对于示例来说,可能并不需要~
A_NOOB
A_NOOB
提供qps啥的 更有说服力吧
阿里巴巴开源框架-通用缓存访问JetCache介绍

JetCache是由阿里巴巴开源的通用缓存访问框架,如果你对Spring Cache很熟悉的话,请一定花一点时间了解一下JetCache,它更好用。 JetCache提供的核心能力包括: 提供统一的,类似jsr-107风格...

黄理
01/24
0
0
Spring Boot + Mybatis + Redis as L2cache

1.背景 Spring-Boot因其提供了各种开箱即用的插件,使得它成为了当今最为主流的Java Web开发框架之一。Mybatis是一个十分轻量好用的ORM框架。Redis是当今十分主流的分布式key-value型数据库,...

梨加橙
2017/08/08
0
0
阿里巴巴开源的通用缓存访问框架JetCache介绍

JetCache是由阿里巴巴开源的通用缓存访问框架,如果你对Spring Cache很熟悉的话,请一定花一点时间了解一下JetCache,它更好用。 JetCache提供的核心能力包括: 提供统一的,类似jsr-107风格...

黄理
04/24
0
0
论代码所需要的环境、版本的重要性

学员们在参与“基于Spring Boot的博客系统实战”课程的时候,可能没有太注意版本的问题。其实,版本是一个非常重要也是一个非常容易忽略的问题。 版本不一致会导致各种奇怪的问题,比如: 应...

老卫
06/26
0
0
Spring Boot学习笔记

Spring Boot整合Redis spring boot 1.5.4 整合redis、拦截器、过滤器、监听器、静态资源配置(十六) spring boot 1.5.9 整合redis springboot集成redis详解 spring boot使用redisTemplate存...

OSC_fly
07/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring Cloud Gateway真的有那么差吗?

前言 Spring Cloud从一开始最受大家质疑的就是网关性能,那是由于Spring Cloud最初选择了使用Netflix几年前开源的Zuul作为基础,而高性能版的Zuul 2在经过了多次跳票之后,对于Spring这样的整...

Java小铺
42分钟前
1
0
SpringBoot远程调试,远程debug你的线上项目

开发环境中代码出错了,可以利用IDE的debug功能来进行调试。那线上环境出错呢? 一、假设我们的项目是部署在tomcat中,那我们就需要对tomcat进行一定对配置,配置如下。 1. windows系统中,找...

nonnetta
47分钟前
0
0
JAVA秒杀优化方向

秒杀优化方向 将请求尽量拦截在系统上游:传统秒杀系统之所以挂,请求都压倒了后端数据层,数据读写锁冲突严重,几乎所有请求都超时,流量虽大,下单成功的有效流量甚小,我们可以通过限流、...

小贱是个程序员
54分钟前
0
0
C# 统计字符串中大写字母和小写字母的个数

static void Main() { int count1 = 0; int count2 = 0; Console.WriteLine("请输入字符串"); string str = Convert.ToString(Consol......

熊二的爸爸是谁
56分钟前
0
0
分布式服务框架之远程通讯技术及原理分析

在分布式服务框架中,一个最基础的问题就是远程服务是怎么通讯的,在Java领域中有很多可实现远程通讯的技术,例如:RMI、MINA、ESB、Burlap、Hessian、SOAP、EJB和JMS等,这些名词之间到底是...

老道士
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部