redis 计数 java_Spring之借助Redis设计一个简单访问计数器的示例

为什么要做一个访问计数?之前的个人博客用得是卜算子做站点访问计数,用起来挺好,但出现较多次的响应很慢,再其次就是个人博客实在是访问太少,数据不好看😢…

前面一篇博文简单介绍了spring中的redistemplate的配置与使用,那么这篇算是一个简单的应用case了,主要基于redis的计数器来实现统计

i. 设计

一个简单的访问计数器,主要利用redis的hash结构,对应的存储结构如下:

5c2821dead66228282448dae1a1c83fe.png

存储结构比较简单,为了扩展,每个应用(or站点)对应一个app,然后根据path路径进行分页统计,最后有一个特殊的用于统计全站的访问计数

ii. 实现

主要就是利用redis的hash结构,然后实现数据统计,并没有太多的难度,spring环境下搭建redis环境可以参考:

1. redis封装类

针对几个常用的做了简单的封装,直接使用redistemplate的excute方法进行的操作,当然也是可以使用 template.opsforvalue() 等便捷方式,这里采用json方式进行对象的序列化和反序列化

public class quickredisclient {

private static final charset code = charset.forname("utf-8");

private static redistemplate template;

public static void register(redistemplate 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 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) 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) con -> {

con.set(tobytes(key), tobytes(value));

return null;

});

}

public static long incr(string key, long add) {

return template.execute((rediscallback) 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) con -> {

long record = con.hincrby(tobytes(key), tobytes(field), add);

return record == null ? 0l : record;

});

}

public static t hget(string key, string field, class clz) {

return template.execute((rediscallback) con -> {

byte[] records = con.hget(tobytes(key), tobytes(field));

if (records == null) {

return null;

}

return json.parseobject(records, clz);

});

}

public static map hmget(string key, list fields, class clz) {

list list =

template.execute((rediscallback>) con -> con.hmget(tobytes(key), tobytes(fields)));

if (collectionutils.isempty(list)) {

return collections.emptymap();

}

map 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;

}

}

对应的配置类

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 redistemplate(redisconnectionfactory redisconnectionfactory) {

redistemplate 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 支持

首先是定义请求参数:

@data

public class webcountreqdo implements serializable {

private string appkey;

private string referer;

}

其次是实现controller接口,稍稍注意下,根据path进行计数的逻辑:

如果请求参数显示指定了referer参数,则用传入的参数进行统计

如果没有显示指定referer,则根据header获取referer

解析referer,分别对path和host进行统计+1,这样站点的统计计数就是根据host来的,而页面的统计计数则是根据path路径来的

@slf4j

@restcontroller

@requestmapping(path = "/count")

public class webcountcontroller {

@requestmapping(path = "cc", method = {requestmethod.get})

public responsewrapper addcount(webcountreqdo webcountreqdo) {

string appkey = webcountreqdo.getappkey();

if (stringutils.isblank(appkey)) {

return responsewrapper.errorreturnmix(status.statusenum.illegal_params_mix, "请指定appkey!");

}

string referer = reqinfocontext.getreqinfo().getreferer();

if (stringutils.isblank(referer)) {

referer = webcountreqdo.getreferer();

}

if (stringutils.isblank(referer)) {

return responsewrapper.errorreturnmix(status.statusenum.fail_mix, "无法获取请求referer!");

}

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);

}

}

}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持萬仟网。

希望与广大网友互动??

点此进行留言吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值