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


声明:本文转载自https://my.oschina.net/waylau/blog/1621710,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

有时,为了提升整个网站的性能,我们会将经常需要访问数据缓存起来,这样,在下次查询的时候,能快速的找到这些数据。 缓存的使用与系统的时效性有着非常大的关系。当我们的系统时效性要求不高时,则选择使用缓存是极好的。当系统要求的时效性比较高时,则并不适合用缓存。 本文,我们将演示如何通过集成 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目录下。

参考资料

本文发表于2018年02月13日 08:31
(c)注:本文转载自https://my.oschina.net/waylau/blog/1621710,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1879 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1