Why do an access count? My previous personal blog was Bu Suanzi for site visit counting, which was very good, but the response was slow for many times. Secondly, my personal blog was too few to access and the data was not good to watch ?…
The previous blog post briefly introduced the configuration and use of RedisTemplate in Spring. This article is considered a simple application case, mainly based on Redis counters to implement statistics.
I. Design
A simple access counter mainly uses the hash structure of redis, and the corresponding storage structure is as follows:
The storage structure is relatively simple. In order to expand, each application (or site) corresponds to an APP, and then paginate statistics based on the path path. Finally, there is a special access count for stating the entire site.
II. Implementation
The main thing is to use the hash structure of Redis and then implement data statistics. It is not too difficult. You can refer to the Redis environment in Spring environment:
Spring's RedisTemplate configuration and use
1. Redis encapsulation class
For several commonly used ones, we have made simple encapsulation, and we directly use the Excut method of RedisTemplate. Of course, we can also use template.opsForValue() and other convenient methods. Here we use JSON to serialize and deserialize objects.
public class QuickRedisClient { private static final Charset CODE = Charset.forName("UTF-8"); private static RedisTemplate<String, String> template; public static void register(RedisTemplate<String, String> template) { QuickRedisClient.template = template; } public static void nullCheck(Object... args) { for (Object obj : args) { if (obj == null) { throw new IllegalArgumentException("redis argument can not be null!"); } } } public static byte[] toBytes(String key) { nullCheck(key); return key.getBytes(CODE); } public static byte[][] toBytes(List<String> keys) { byte[][] bytes = new byte[keys.size()][]; int index = 0; for (String key : keys) { bytes[index++] = toBytes(key); } return bytes; } public static String getStr(String key) { return template.execute((RedisCallback<String>) con -> { byte[] val = con.get(toBytes(key)); return val == null ? null : new String(val); }); } public static void putStr(String key, String value) { template.execute((RedisCallback<Void>) con -> { con.set(toBytes(key), toBytes(value)); return null; }); } public static Long incr(String key, long add) { return template.execute((RedisCallback<Long>) con -> { Long record = con.incrBy(toBytes(key), add); return record == null ? 0L : record; }); } public static Long hIncr(String key, String field, long add) { return template.execute((RedisCallback<Long>) con -> { Long record = con.hIncrBy(toBytes(key), toBytes(field), add); return record == null ? 0L : record; }); } public static <T> T hGet(String key, String field, Class<T> clz) { return template.execute((RedisCallback<T>) con -> { byte[] records = con.hGet(toBytes(key), toBytes(field)); if (records == null) { return null; } return JSON.parseObject(records, clz); }); } public static <T> Map<String, T> hMGet(String key, List<String> fields, Class<T> clz) { List<byte[]> list = template.execute((RedisCallback<List<byte[]>>) con -> con.hMGet(toBytes(key), toBytes(fields))); if (CollectionUtils.isEmpty(list)) { return Collections.emptyMap(); } Map<String, T> result = new HashMap<>(); for (int i = 0; i < fields.size(); i++) { if (list.get(i) == null) { continue; } result.put(fields.get(i), JSON.parseObject(list.get(i), clz)); } return result; }} Corresponding configuration class
package com.git.hui.story.cache.redis;import com.git.hui.story.cache.redis.serializer.DefaultStrSerializer;import org.springframework.cache.CacheManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.PropertySource;import org.springframework.core.env.Environment;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.connection.RedisPassword;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;/** * Created by yihui in 18:45 18/6/11. */@Configuration@PropertySource(value = "classpath:application.yml")public class RedisConf { private final Environment environment; public RedisConf(Environment environment) { this.environment = environment; } @Bean public CacheManager cacheManager() { return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory()).build(); } @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, String> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); DefaultStrSerializer serializer = new DefaultStrSerializer(); redisTemplate.setValueSerializer(serializer); redisTemplate.setHashValueSerializer(serializer); redisTemplate.setKeySerializer(serializer); redisTemplate.setHashKeySerializer(serializer); redisTemplate.afterPropertiesSet(); QuickRedisClient.register(redisTemplate); return redisTemplate; } @Bean public RedisConnectionFactory redisConnectionFactory() { LettuceConnectionFactory fac = new LettuceConnectionFactory(); fac.getStandaloneConfiguration().setHostName(environment.getProperty("spring.redis.host")); fac.getStandaloneConfiguration().setPort(Integer.parseInt(environment.getProperty("spring.redis.port"))); fac.getStandaloneConfiguration().setPassword(RedisPassword.of(environment.getProperty("spring.redis.password"))); fac.afterPropertiesSet(); return fac; }} 2. Controller Support
First, define the request parameters:
@Datapublic class WebCountReqDO implements Serializable { private String appKey; private String referer;}The second is to implement the Controller interface. Pay a little attention to the logic of counting according to the path:
@Slf4j@RestController@RequestMapping(path = "/count")public class WebCountController { @RequestMapping(path = "cc", method = {RequestMethod.GET}) public ResponseWrapper<CountDTO> addCount(WebCountReqDO webCountReqDO) { String appKey = webCountReqDO.getAppKey(); if (StringUtils.isBlank(appKey)) { return ResponseWrapper.errorReturnMix(Status.StatusEnum.ILLEGAL_PARAMS_MIX, "Please specify APPKEY!"); } String referer = ReqInfoContext.getReqInfo().getReferer(); if (StringUtils.isBlank(referer)) { referer = webCountReqDO.getReferer(); } if (StringUtils.isBlank(referer)) { return ResponseWrapper.errorReturnMix(Status.StatusEnum.FAIL_MIX, "Cannot get requester!"); } return ResponseWrapper.successReturn(doUpdateCnt(appKey, referer)); } private CountDTO doUpdateCnt(String appKey, String referer) { try { if (!referer.startsWith("http")) { referer = "https://" + referer; } URI uri = new URI(referer); String host = uri.getHost(); String path = uri.getPath(); long count = QuickRedisClient.hIncr(appKey, path, 1); long total = QuickRedisClient.hIncr(appKey, host, 1); return new CountDTO(count, total); } catch (Exception e) { log.error("get referer path error! referer: {}, e: {}", referer, e); return new CountDTO(1L, 1L); } }}The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.