Demo项目(统计接口访问次数的全技术总结)

​ Demo项目(统计接口访问次数的全技术总结)

数据库表的设计:

目的:我们需要统计出单个接口的在指定时间内被访问次数,所需要的变量有id,count次数,和date时间。需要满足,用户每进行一次访问,count次数加1,数据库记录一条新数据,该数据包含此用户的id,和时间,以及count次数加一。

idcountdate
112022/12/12 17: 36 GMT
212022/12/12 17: 36 GMT
312022/12/12 17: 36 GMT

拓展:表的粒度问题,我们可以把12月12号这一天所有的访问次数整理成一条记录,这样就可以方便访问在12月12号的总访问次数。

idcountdate
152022/12/12
262022/12/13
372022/12/14
MYBATIS plus+部分与springboot整合:

目的:相对于mybatis,mybatis plus自动化了所有CRUD操作,让对数据库的操作更加简单。

pom.xml配置(引入pom依赖)

 <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

创建mapper,该mapper继承了basemapper,basemapper中封装了基础的CRUD操作,无需自己再重新写xml来配置CRUD操作,可以直接调用。

@Mapper
//表明这是一个Mapper,也可以在启动类上加上包扫描
//Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
public interface UserMapper extends BaseMapper<User> {

}
Redis + Spring boot的整合

目的:redis在本项目中作为一个key-value结构的缓存,把访问用户id和访问次数存入redis中,然后再把redis中缓存的数据同步到mysql数据库中。

pom.xml配置(spring支持redis的集成,同时需要下载redis服务器,启动项目前先启动redis-server)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.7.7-SNAPSHOT</version>
</dependency>

application.properties中指定redis的host 和port(默认ip地址为localhost 127.0.0.1,默认port为6379)

对redis中的key和value进行序列化操作

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig2 {

    /**
     * 设置 redisTemplate 的序列化设置
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        // 1.创建 redisTemplate 模版
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        // 2.关联 redisConnectionFactory
        template.setConnectionFactory(redisConnectionFactory);
        // 3.创建 序列化类
        GenericToStringSerializer genericToStringSerializer = new GenericToStringSerializer(Object.class);
        // 6.序列化类,对象映射设置
        // 7.设置 value 的转化格式和 key 的转化格式
        template.setValueSerializer(genericToStringSerializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
RabbitMq与springboot的整合:

目的:使用rabbitmq作为消息队列,异步地将redis缓存信息同步到mysql数据库中。

pom.xml配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

spring中amqp协议支持rabbitmq的集成

public class RabbitTest {
    private static final String EXCHANGE_NAME = "exchange_demo";
    private static final String ROUTING_KEY = "routingky_demo";
    private static final String QUEUE_NAME = "queue_demo";
    private static final String IP_ADDRESS = "127.0.0.1";

    private static final int PORT = 5672;

    public static void main(String[] args) throws IOException, TimeoutException {
        final ConnectionFactory factory = new ConnectionFactory();
        factory.setHost(IP_ADDRESS);
        factory.setPort(PORT);
        factory.setUsername("guest");
        factory.setPassword("guest");
        final Connection connection = factory.newConnection();
        final Channel channel = connection.createChannel();
        // 创建一个 type=direct 持久化、非自动删除的交换器
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null);
        // 创建一个:持久化、非排他的、非自动删除的队列
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        // 将交换器与队列通过 路由键 绑定
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);

//        String message = "Hello World!";
//        channel.basicPublish(EXCHANGE_NAME,
//                ROUTING_KEY,
//                MessageProperties.PERSISTENT_TEXT_PLAIN,
//                message.getBytes());

        // 关闭资源
        channel.close();
        connection.close();
    }
}

通过rabbitmq中的connectionfactory创建consumer类,定义channel和queue并对交换器和队列通过路由键进行一个绑定,创建完之后关闭资源。

整体业务逻辑实现:

1.初始化redis util,设定一个key,该key用来记录用户访问的id;

@PostConstruct
public void init(){
    log.info("初始化");
    int viewid = userMapper.selectById(1).getId();
    redisUtil.set("pageid",viewid);
    log.info("完成初始化");
}

2.每进行一次访问,就会新增一条用户访问id,该id通过redisUtil里面的incr方法进行递增,并把访问次数count设置为1。通过fastjson把对象转成JSONstring 并使用rebbittemplete中的convertandsend方法,把对象转为信息发送到消息队列中。

@GetMapping("/kay")
public String tokay(){
    redisUtil.incr("pageid",1);
    User user = new User(Integer.parseInt(redisUtil.get("pageid").toString()),1 ,date);
    String s = JSON.toJSONString(user);
    rabbitTemplate.convertAndSend("","queue_demo",s);
    log.info("添加用户成功");
    return "访问量次数加1"+   ": " + redisUtil.get("pageid");
}

3.设置一个rabbitmq 监听器,用于实时监听在consumer方发布的信息,并对此进行消费。使用log.info打印出接受到的消息,并把消息转换为User对象,再使用usermapper把User对象添加到数据库中,这样就实现了异步同步redis缓存到数据库中。

@RabbitListener(queues = "queue_demo")
@RabbitHandler
public void process(@Payload String msg){
    User u1 = JSON.parseObject(msg, User.class);
    log.info("Userinfo"+u1);
    userMapper.insert(u1);
    log.info("添加完毕");
}

4.最后使用定时任务,使用springboot中的@Scheduled方法,每隔一段时间对数据库中的访问次数进行一个统计。(未实现的点:传入指定的时间,并判断在该时间内的接口访问次数)

@Scheduled(cron = "*/20 * * * * ?")
public void count(){

    QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
    queryWrapper.gt("count",0);
    long count = userMapper.selectCount(queryWrapper);
    log.info("统计数为"+ count);
}
项目难点总结:
  1. rabbitMq 基本概念理解,需了解整个消息队列的运作过程。
  2. 时间格式化处理,如何在将input时间转成统一时间戳,并进行接口访问次数的判断。
  3. 表的粒度问题,并发问题,如何实现负载均衡。
  4. 代码逻辑,如何确定正确的代码逻辑。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值