java发送短信系列之限制日发送次数

在前两篇文章中, 我们实现了同步/异步发送短信以及限制发送短信频率.这一篇, 我们介绍一下限制每日向同一个用户(根据手机号和ip判断)发送短信的次数

1、数据表结构

由于需要记录整天的发送记录, 因此这里我们将数据保存到数据库中. 数据表结构如下:

type为验证码的类型, 比如注册, 重置密码等.
sendTime的默认值为当前时间.

2、限制日发送次数

我们这里需要用到上一篇中提到的接口和实体类.

DailyCountFilter.java

public class DailyCountFilter implements SmsFilter {

  private int ipDailyMaxSendCount;
  private int mobileDailyMaxSendCount;
  private SmsDao smsDao;

  // 省略了部分无用代码

  @Override
  public boolean filter(SmsEntity smsEntity) {
    if (smsDao.getMobileCount(smsEntity.getMobile()) >= mobileDailyMaxSendCount) {
      return false;
    }
    if (smsDao.getIPCount(smsEntity.getIp()) >= ipDailyMaxSendCount) {
      return false;
    }
    smsDao.saveEntity(smsEntity);
    return true;
  }

}

主要代码很简单, 首先判断向指定的手机号发送的次数是否达到了日最大发送次数, 之后再判断指定的ip请求发送的次数是否达到了最大次数. 如果都没有, 则将本次发送的手机号, ip等信息保存到数据库中.

当然, 这个类存在一定的问题: 在判断是否超过最大次数到保存实体数据之间可能已经有其他线程保存了新的数据. 造成上面的两个判断并不是绝对的准确.

我们可以使用序列化等级的事务保证不会发生错误, 但是代价太高. 因此我们这里不做处理. 因为我们前面已经实现了限制发送频率. 如果先使用FrequencyFilter过滤一次, 限制发送频率, 那么基本上不可能出现前面说的问题.

还有一个问题: 随着时间的推移, 这个表会越来越大, 造成查询的性能相当的差. 我们可以向上一篇中那样, 每隔一段时间就删除无用的数据; 也可以动态的创建表, 然后向新表中插入数据.

3、使用动态表

这里我们采用第二种方案: 数据表的名字为"sms_四位年_两位月", 比如"sms_2016_02". 插入数据时根据现在的时间获得表名, 然后再插入. 另外使用Quartz在每月的20号2点生成下个月以及下下个月的数据表:

我们首先修改DailyCountFilter类, 在这个类中添加任务计划, 定时生成数据表:

DailyCountFilter.java

// 在上面代码的基础上, 再添加如下代码
public class DailyCountFilter implements SmsFilter {

  private Scheduler sched;

  @Override
  public void init() throws SchedulerException {
    smsDao.createTable(0); // 创建这个月的数据表
    smsDao.createTable(1); // 创建下个月的数据表

    SchedulerFactory sf = new StdSchedulerFactory();
    sched = sf.getScheduler(); // 创建Quartz容器

    JobDataMap jobDataMap = new JobDataMap();
    jobDataMap.put("smsDao", smsDao);  // 创建运行任务时需要使用的数据map 

    // 创建job对象, 该对象执行实际的任务
    JobDetail job = JobBuilder.newJob(CreateSmsTableJob.class)
        .usingJobData(jobDataMap)
        .withIdentity("create sms table job").build();

    // 创建trigger对象, 该对象用来描述触发执行job的时间规则
    // 比如这里的每月20号2点
    CronTrigger trigger = TriggerBuilder.newTrigger()
        .withIdentity("create sms table trigger")
        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 20 * ?"))// 每月的20号2点
        .build();

    sched.scheduleJob(job, trigger);  // 注册任务和触发规则
    sched.start(); // 启动调度
  }

  @Override
  public void destroy() {
    try {
      sched.shutdown();
    }
    catch (SchedulerException e) {}
  }

  public static class CreateSmsTableJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
      JobDataMap dataMap = context.getJobDetail().getJobDataMap();
      SmsDao smsDao = (SmsDao) dataMap.get("smsDao"); // 获得传过来的smsDao对象
      smsDao.createTable(1); // 创建下个月的数据表
      smsDao.createTable(2); // 创建下下个月的数据表
     }
  }
}

接下来, 我们看看SmsDao的部分代码:

SmsDao.java

public class SmsDao {

  /**
   * 创建新的日志表
   *
   * @param monthExcursion 偏移的月数
   */
  public void createTable(int monthExcursion){
    String sql = "CREATE TABLE IF NOT EXISTS "
      + getTableName(monthExcursion) + " LIKE sms";
    // 执行sql语句
  }

  /**
   * 保存SmsEntity实体对象
   */
  public void saveEntity(SmsEntity smsEntity){
    String sql = "INSERT INTO "
      + getNowTableName() + " (mobile, ip, type) VALUES(?, ?, ?)";
    // 执行sql语句
  }

  /**
   * 获得指定手机号今天请求发送短信的次数
   *
   * @param mobile 用户手机号
   * @return 今天请求发送短信的次数
   */
  public long getMobileCount(String mobile){
    String sql = "SELECT count(id) FROM "
      + getNowTableName() + " WHERE mobile=? AND time >= CURDATE()";
    // 执行sql语句, 返回查询结果
  }

  // 省略了getIPCount方法

  /**
   * 获得现在使用的表的名字
   */
  private String getNowTableName() {
    return getTableName(0);
  }

  private DateFormat dateFormat = new SimpleDateFormat("yyyy_MM");

  /**
   * 获得相对现在偏移monthExcursion月的表名
   *
   * @param monthExcursion 偏移的月数
   * @return 对应月的表名
   */
  private String getTableName(int monthExcursion) {
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MONTH, monthExcursion);
    Date date = calendar.getTime();
    return "sms_" + dateFormat.format(date);
  }

}

SmsDao中的createTable方法成功运行有个前提, 就是存在sms数据表. createTable方法会复制sms表的结构创建新的数据表.

我们保留发送短信的数据(手机号, ip, 时间等), 而不是直接删除, 是因为以后可能需要分析这些数据, 获取我们想要的信息, 比如判断服务商短信的到达率、是否有人恶意发送短信等. 甚至可能获得意外的"惊喜".

以上就是本文的全部内容,希望大家可以继续关注。

(0)

相关推荐

  • java发送短信系列之同步、异步发送短信

    本篇本章是发送短信的第一部分, 说一下同步/异步发送短信的代码, 以后几篇我们稍微完善一下功能, 添加发送频率的限制和日发送次数的限制. 发送短信的方法可能不少, 我们的方法是使用服务商提供的服务. 一般来说, 这些服务都是和语言无关的, 这里我们使用java写示例程序. 1.发送短信的接口 根据自己的情况选择服务商. 2.开发文档 从开发文档中我们可以看到. 可以直接使用http请求也可以使用WebService请求发送短信. 由于DEMO文件夹下的java和jsp文件夹中的代码都是使用htt

  • java发送短信系列之限制发送频率

    本篇是发送短信的第二部分, 这里我们介绍一下如何限制向同一个用户(根据手机号和ip)发送短信的频率. 1.使用session 如果是web程序, 那么在session中记录上次发送的时间也可以, 但是可以被绕过去. 最简单的, 直接重启浏览器 或者 清除cache等可以标记session的数据, 那么就可以绕过session中的记录. 虽然很多人都不是计算机专业的, 也没学过这些. 但是我们需要注意的是, 之所以限制发送频率, 是为了防止"短信炸弹", 也就是有人恶意的频繁的请求向某个

  • java发送短信的实现步骤

    一.在中国网建中注册用户:本程序是通过中国网建提供的SMS短信平台实现的,该平台新用户注册可以拥有免费5条普通短信和3条彩信,足够进行尝试和体验了.中国网建注册地址:http://sms.webchinese.cn/reg.shtml: 二.修改短信签名:注册成功后登陆,用户登陆有首先要修改短信签名,因为中国网建中规定了,发送的短信如果没有正规的签名是不能成功发送的,提示性信息见下图: 修改短信签名的步骤:用户信息修改--->修改用户信息--->保存信息,如下图: 三.修改验证码网关和绑定短信

  • JAVA实现利用第三方平台发送短信验证码

    前段时间自己做的一个小项目中,涉及到用短信验证码登录.注册的问题,之前没涉及过这一块,看了别人的博客其实也是似懂非懂的,现在就将自己做的利用第三方短信平台来发送验证码这个功能记下来. 本文以注册为例,在SpringMVC+Spring+Mybatis框架的基础上完成该短信验证码功能. 发送短信验证码的原理是:随机生成一个6位数字,将该6位数字保存到session当中,客户端通过sessionid判断对应的session,用户输入的验证码再与session记录的验证码进行比较. 为了防止有广告嫌疑

  • Java使用云片API发送短信验证码

    下面开始介绍的是如何利用机器完成批量操作,将短信业务自动化. 获取APIKEY 云片网提供了完整的SDK和API,可以帮助开发者快速完成业务开发. 在开始Coding前,需要先获取APIKEY,如下所示. 获取APIKEY 点击眼睛按钮后,输入验证码,就可以查看APIKEY了. 这里需要说明的是,APIKEY特别重要,一定要保护好它,避免泄露.云片这边提供了几重保护机制,例如验证.敏感处理.子账号独立APIKEY等,看得出来他们的安全意识还是挺不错的. 开始Coding 有了APIKEY,就可以

  • java使用短信设备发送sms短信的示例(java发送短信)

    复制代码 代码如下: import gnu.io.*;import java.util.*;import java.io.*; public class CommTest{    static CommPortIdentifier portId;    static Enumeration portList;    static int bauds[] = { 9600, 19200, 57600, 115200 };    //检测端口所支持的波特率 public static void ma

  • java使用smslib连接短信猫发送短信代码分享

    复制代码 代码如下: import java.util.ArrayList;import java.util.List; import org.apache.log4j.Logger;import org.smslib.ICallNotification;import org.smslib.IInboundMessageNotification;import org.smslib.IOutboundMessageNotification;import org.smslib.InboundMess

  • java发送短信系列之限制日发送次数

    在前两篇文章中, 我们实现了同步/异步发送短信以及限制发送短信频率.这一篇, 我们介绍一下限制每日向同一个用户(根据手机号和ip判断)发送短信的次数 1.数据表结构 由于需要记录整天的发送记录, 因此这里我们将数据保存到数据库中. 数据表结构如下: type为验证码的类型, 比如注册, 重置密码等. sendTime的默认值为当前时间. 2.限制日发送次数 我们这里需要用到上一篇中提到的接口和实体类. DailyCountFilter.java public class DailyCountFi

  • 利用VBS发送短信的实现代码(通过飞信)

    光看标题就已经觉得很牛逼了,听说过可以用 PHP 发送短信(飞信),也使用过 Python 实现的 PyFetion 发送过短信(飞信).我也看过对应的 PHP 和 Python 源码,实现起来还是比较复杂的,难道可以用 VBS 来实现? 看到代码后更觉得牛逼,竟然是使用 10086.cn (移动官网)上面的接口来实现的,飞信官方难道已经公布飞信接口了?若不是,难道是代码的作者自己发现的接口?那也太强大了!Google 了一下才发现,哦,都不是,而是 WAP 飞信.像我这种还在用着 2005 年

  • java实现短信通信的完整教程

    前言 短信信息的发送目前已经是项目中必不可少的部分,我们怎么通过web页面来实现把信息推送到别人手机上呢?简单点,编码的方式简单点!看完本篇文章,以后要实现短信的发送都只需要知道发什么?发给谁?就OK了,代码如下,是不是很简单^_^ String result= "";//返回状态 Note note = new Note(); String Tel = "17089490559";//接收人手机号 String message = "您好!";

  • Android如何实现接收和发送短信

    每一部手机都具有短信接收和发送功能,下面我们通过代码来实现接收和发送短信功能. 一.接收短信 1.创建内部广播接收器类,接收系统发出的短信广播 2.从获得的内容中解析出短信发送者和短信内容 3.在Activity中注册广播 4.添加接收短信权限 下面放上具体的代码  activity_main.xml文件用于显示短信发送者号码和显示短信内容 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout

  • iOS发送短信功能的实现代码

    发短信的功能对于一个需要渠道扩展的APP来说,必不可少.但是,当第一次看到这个需求时,我却一脸懵逼,因为之前并没有接触过,出于对未知事物的恐惧,被分配做这个任务的时候其实我是拒绝的,但是,没办法谁让我是小兵一个呢,只能硬着头皮强上了.在查阅了一番资料之后,发现这个功能做起来其实非常简单,不到十分钟就可以解决.下面我们就来聊一下如何实现这个功能. 首先,我们来介绍一个最为简单的方法,只需要一行代码就可以搞定,代码如下: [[UIApplication sharedApplication] open

  • Java实现发送短信验证码功能

    一个发送短信验证码的功能,使用的是信易通的短信平台接口,然后在Java中使用HttpClient模拟POST请求或者GET请求(看短信平台要求,一般的情况下都是POST请求),调用短信平台提供的接口(遵循短信平台的接口规范即可).具体看代码: 使用HttpClient的时候需要在项目中引入: commons-httpclient-3.1.jar 这个jar包, 项目结构: 1.创建一个Http的模拟请求工具类,然后写一个POST方法或者GET方法 /** * 文件说明 * @Descriptio

随机推荐