@Service
@Slf4j
public class WeatherServiceImpl implements IWeatherService {
@Resource
private WeatherServiceApi weatherServiceApi;
@Resource
private RedisTemplate redisTemplate;
@Value("${redis.weather}")
private Integer redisWeather;
private ThreadLocal hourlyThreadLocal = new ThreadLocal();
private ThreadLocal dailyThreadLocal = new ThreadLocal<>();
@Override
public WeatherHourlyAndLifestyleNow queryCurrentDayWeather(String location,String lifestyle) {
Long st = System.currentTimeMillis();
String weatherKey = RedisEnum.CpspWeather.LOCATION_LIFESTYLE_SAVE_HOURLY_WEATHER.getKey(location,lifestyle);
WeatherHourlyAndLifestyleNow data = (WeatherHourlyAndLifestyleNow)redisTemplate.opsForValue().get(weatherKey);
if (null != data){
return data;
}
//data = new WeatherHourlyAndLifestyleNow();
OnTheDayHourlyWeatherThreadLocal onTheDayHourlyWeatherThreadLocal = new OnTheDayHourlyWeatherThreadLocal(location,weatherServiceApi,redisTemplate,redisWeather,null,null,true);
hourlyThreadLocal.set(onTheDayHourlyWeatherThreadLocal.queryCurrentDayWeather(location, lifestyle));
data = hourlyThreadLocal.get();
// CountDownLatch countDownLatch = new CountDownLatch(hourlyCountDownLatch);
// FutureTask weatherHourlyTask = new FutureTask(new WeatherHourlyTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather));
// FutureTask lifestyleTask = new FutureTask(new LifestyleTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather,lifestyle));
// FutureTask weatherForecastTask = new FutureTask(new WeatherForecastTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather,null,null,true));
// FutureTask airQualityTask = new FutureTask(new AirQualityTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather,null,null,true));
// ExecutorService executorService = Executors.newFixedThreadPool(hourlyCountDownLatch);
// executorService.submit(weatherHourlyTask);
// executorService.submit(lifestyleTask);
// executorService.submit(weatherForecastTask);
// executorService.submit(airQualityTask);
// Long ft = System.currentTimeMillis();
// log.debug("==> ft run consume time :{}, countDownLatch :{}",ft-st,countDownLatch);
// try {
// countDownLatch.await();
// data.setHourlyList((List) weatherHourlyTask.get());
// data.setLifestyleNows((List) lifestyleTask.get());
// WeatherDailyForecastVo dailyForecastVo = (WeatherDailyForecastVo)weatherForecastTask.get();
// if (null != dailyForecastVo){
// data.setDailyForecast(dailyForecastVo.getDailyForecastList().get(0));
// data.setBaseInfoVo(dailyForecastVo.getBaseInfoVo());
// }
// List airQualityList = (List)airQualityTask.get();
// if(!CollectionUtils.isEmpty(airQualityList)){
// data.setAirQuality(airQualityList.get(0));
// }
// } catch (InterruptedException | ExecutionException e) {
// log.error("error:",e);
// throw ExceptionEnum.QUERY_WEATHER_ERROR.getException();
// }finally {
// executorService.shutdown();
// }
if (null == data){
redisTemplate.opsForValue().set(weatherKey,data,redisWeather,TimeUnit.SECONDS);
}
log.debug("");
Long ed = System.currentTimeMillis();
log.debug("===> weatherServiceImpl queryCurrentDayWeather consume time total : {}",ed - st);
log.debug("");
return data;
}
}
@Slf4j
public class OnTheDayHourlyWeatherThreadLocal {
private String location;
private WeatherServiceApi weatherServiceApi;
private RedisTemplate redisTemplate;
private Integer redisWeather;
private String startDate;
private String endDate;
private Boolean onTheDay;
private int hourlyCountDownLatch = 4;
private static ThreadLocal threadLocal = new ThreadLocal();
private CountDownLatch countDownLatch = new CountDownLatch(4);
public OnTheDayHourlyWeatherThreadLocal(String location, WeatherServiceApi weatherServiceApi,
RedisTemplate redisTemplate, Integer redisWeather, String startDate, String endDate,
Boolean onTheDay){
this.location = location;
this.weatherServiceApi = weatherServiceApi;
this.redisTemplate = redisTemplate;
this.redisWeather = redisWeather;
this.startDate = startDate;
this.endDate = endDate;
this.onTheDay = onTheDay;
}
public WeatherHourlyAndLifestyleNow queryCurrentDayWeather(String location, String lifestyle) {
Long st = System.currentTimeMillis();
String weatherKey = RedisEnum.CpspWeather.LOCATION_LIFESTYLE_SAVE_HOURLY_WEATHER.getKey(location,lifestyle);
WeatherHourlyAndLifestyleNow data = (WeatherHourlyAndLifestyleNow)redisTemplate.opsForValue().get(weatherKey);
if (null != data){
return data;
}
data = new WeatherHourlyAndLifestyleNow();
FutureTask weatherHourlyTask = new FutureTask(new WeatherHourlyTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather));
FutureTask lifestyleTask = new FutureTask(new LifestyleTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather,lifestyle));
FutureTask weatherForecastTask = new FutureTask(new WeatherForecastTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather,null,null,true));
FutureTask airQualityTask = new FutureTask(new AirQualityTask(countDownLatch,location,weatherServiceApi,redisTemplate,redisWeather,null,null,true));
ExecutorService executorService = Executors.newFixedThreadPool(hourlyCountDownLatch);
executorService.submit(weatherHourlyTask);
executorService.submit(lifestyleTask);
executorService.submit(weatherForecastTask);
executorService.submit(airQualityTask);
try {
countDownLatch.await(3,TimeUnit.SECONDS);
data.setHourlyList((List) weatherHourlyTask.get());
data.setLifestyleNows((List) lifestyleTask.get());
WeatherDailyForecastVo dailyForecastVo = (WeatherDailyForecastVo)weatherForecastTask.get();
if (null != dailyForecastVo){
data.setDailyForecast(dailyForecastVo.getDailyForecastList().get(0));
data.setBaseInfoVo(dailyForecastVo.getBaseInfoVo());
}
List airQualityList = (List)airQualityTask.get();
if(!CollectionUtils.isEmpty(airQualityList)){
data.setAirQuality(airQualityList.get(0));
}
} catch (InterruptedException | ExecutionException e) {
log.error("error:",e);
throw ExceptionEnum.QUERY_WEATHER_ERROR.getException();
}finally {
executorService.shutdown();
}
Long ed = System.currentTimeMillis();
log.debug("===> WeatherHourlyThreadLocal queryCurrentDayWeather consume time total : {}",ed - st);
log.debug("");
return data;
}
}
其中一个FutureTask
@Slf4j
public class AirQualityTask implements Callable> {
private CountDownLatch countDownLatch;
private String location;
private WeatherServiceApi weatherServiceApi;
private RedisTemplate redisTemplate;
private Integer redisWeather;
private String startDate;
private String endDate;
private Boolean onTheDay;
public AirQualityTask (CountDownLatch countDownLatch,String location,WeatherServiceApi weatherServiceApi,
RedisTemplate redisTemplate,Integer redisWeather,String startDate,String endDate,Boolean onTheDay){
this.countDownLatch = countDownLatch;
this.location = location;
this.weatherServiceApi = weatherServiceApi;
this.redisTemplate = redisTemplate;
this.redisWeather = redisWeather;
this.startDate = startDate;
this.endDate = endDate;
this.onTheDay = onTheDay;
}
@Override
public List call() throws Exception {
try{
Long st = System.currentTimeMillis();
List forecastAirQuality = null;
if (onTheDay){
startDate = LocalDate.now().toString();
forecastAirQuality = queryAirQualityForecast(location,startDate,startDate);
}else{
forecastAirQuality = queryAirQualityForecast(location,startDate,startDate);
}
Long ed = System.currentTimeMillis();
log.debug("");
log.debug("==> AirQualityTask consume time :{},countDownLatch:{}",ed-st,countDownLatch);
return forecastAirQuality;
}catch (Exception e){
throw e;
}finally {
countDownLatch.countDown();
}
}
/**
* 获取未来几天空气质量预报 (第三方文档只写了 3-7天)
*
* @param location
* @param startDate 开始日期
* @param endDate 结束日期
*/
public List queryAirQualityForecast(String location,String startDate, String endDate) {
LocalDate queryStart = DateUtil.stringTransLocalDate(startDate);
LocalDate queryEnd = DateUtil.stringTransLocalDate(endDate);
return queryForecastAir(location, queryStart, queryEnd);
}
private List queryForecastAir(String location, LocalDate queryStart, LocalDate queryEnd) {
Long period = DateUtil.localDatePeriod(queryStart, queryEnd);
if (period > 9) {
throw new BusinessException(ExceptionEnum.QUERY_OUT_RANGE);
}
boolean noCache = false;
String redisKey = null;
LocalDate startDate = null;
List list = new ArrayList<>();
for (int index = 0; index < period; index++) {
startDate = queryStart;
startDate = startDate.plusDays(index);
redisKey = RedisEnum.CpspWeather.LOCATION_DATE_SAVE_FORECAST_AIR_QUALITY.getKey(location, startDate.toString());
AirQuality airQuality = (AirQuality) redisTemplate.opsForValue().get(redisKey);
if (airQuality == null) {
list = new ArrayList<>();
noCache = true;
break;
} else {
list.add(airQuality);
}
}
if (noCache) {
List aqList = getAirQualityForecast(location, ServiceConstant.AIR_QUALITY_FORECAST);
if (CollectionUtils.isEmpty(aqList)) {
log.info("==> queryCacheDailyAir no data back!");
throw ExceptionEnum.CPSP_AIR_PARSE_ERROR.getException();
}
for (AirQuality aq : aqList) {
for (int day = 0; day < period; day++) {
startDate = queryStart;
startDate = startDate.plusDays(day);
if (startDate.toString().equals(aq.getQualityDate())) {
list.add(aq);
}
}
//setRedisForecastAir(aq,location);
redisKey = RedisEnum.CpspWeather.LOCATION_DATE_SAVE_FORECAST_AIR_QUALITY.getKey(location, aq.getQualityDate());
RedisOpsForValueAsync redisOpsForValueAsync = new RedisOpsForValueAsync(redisTemplate,redisWeather,redisKey,aq);
WeatherExecutorService executorService = new WeatherExecutorService();
executorService.submit(redisOpsForValueAsync);
}
}
return list;
}
子线程的子线程这个注解是不起作用的,只有主线程的才会被spring管理
// @Async
// public void setRedisForecastAir(AirQuality aq,String location){
// String redisKey = RedisEnum.CpspWeather.LOCATION_DATE_SAVE_FORECAST_AIR_QUALITY.getKey(location, aq.getQualityDate());
// redisTemplate.opsForValue().set(redisKey, aq, redisWeather, TimeUnit.SECONDS);
// }
/**
* 查询未来几天空气质量 不包含当天
*
* @param location
* @param queryType
* @return
*/
private List getAirQualityForecast(String location, String queryType) {
JSONObject response = weatherServiceApi.queryAirQualityForecast(location);
//log.debug("==> cpsp getAirQualityForecast response : {}", response);
if (Objects.isNull(response)) {
log.error("==> cpsp response is null!");
throw new BusinessException(ExceptionEnum.CPSP_RESPONSE_TIMEOUT);
}
return CommonWeather.setAirQualityForecast(response,location);
}
}