国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Java > 正文

java發(fā)送短信系列之限制日發(fā)送次數(shù)

2019-11-26 14:33:22
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

在前兩篇文章中, 我們實(shí)現(xiàn)了同步/異步發(fā)送短信以及限制發(fā)送短信頻率.這一篇, 我們介紹一下限制每日向同一個(gè)用戶(根據(jù)手機(jī)號(hào)和ip判斷)發(fā)送短信的次數(shù)

1、數(shù)據(jù)表結(jié)構(gòu)

由于需要記錄整天的發(fā)送記錄, 因此這里我們將數(shù)據(jù)保存到數(shù)據(jù)庫(kù)中. 數(shù)據(jù)表結(jié)構(gòu)如下:

type為驗(yàn)證碼的類型, 比如注冊(cè), 重置密碼等.
sendTime的默認(rèn)值為當(dāng)前時(shí)間.

2、限制日發(fā)送次數(shù)

我們這里需要用到上一篇中提到的接口和實(shí)體類.

DailyCountFilter.java

public class DailyCountFilter implements SmsFilter {  private int ipDailyMaxSendCount;  private int mobileDailyMaxSendCount;  private SmsDao smsDao;  // 省略了部分無(wú)用代碼  @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;  }}

主要代碼很簡(jiǎn)單, 首先判斷向指定的手機(jī)號(hào)發(fā)送的次數(shù)是否達(dá)到了日最大發(fā)送次數(shù), 之后再判斷指定的ip請(qǐng)求發(fā)送的次數(shù)是否達(dá)到了最大次數(shù). 如果都沒(méi)有, 則將本次發(fā)送的手機(jī)號(hào), ip等信息保存到數(shù)據(jù)庫(kù)中.

當(dāng)然, 這個(gè)類存在一定的問(wèn)題: 在判斷是否超過(guò)最大次數(shù)到保存實(shí)體數(shù)據(jù)之間可能已經(jīng)有其他線程保存了新的數(shù)據(jù). 造成上面的兩個(gè)判斷并不是絕對(duì)的準(zhǔn)確.

我們可以使用序列化等級(jí)的事務(wù)保證不會(huì)發(fā)生錯(cuò)誤, 但是代價(jià)太高. 因此我們這里不做處理. 因?yàn)槲覀兦懊嬉呀?jīng)實(shí)現(xiàn)了限制發(fā)送頻率. 如果先使用FrequencyFilter過(guò)濾一次, 限制發(fā)送頻率, 那么基本上不可能出現(xiàn)前面說(shuō)的問(wèn)題.

還有一個(gè)問(wèn)題: 隨著時(shí)間的推移, 這個(gè)表會(huì)越來(lái)越大, 造成查詢的性能相當(dāng)?shù)牟? 我們可以向上一篇中那樣, 每隔一段時(shí)間就刪除無(wú)用的數(shù)據(jù); 也可以動(dòng)態(tài)的創(chuàng)建表, 然后向新表中插入數(shù)據(jù).

3、使用動(dòng)態(tài)表

這里我們采用第二種方案: 數(shù)據(jù)表的名字為"sms_四位年_兩位月", 比如"sms_2016_02". 插入數(shù)據(jù)時(shí)根據(jù)現(xiàn)在的時(shí)間獲得表名, 然后再插入. 另外使用Quartz在每月的20號(hào)2點(diǎn)生成下個(gè)月以及下下個(gè)月的數(shù)據(jù)表:

我們首先修改DailyCountFilter類, 在這個(gè)類中添加任務(wù)計(jì)劃, 定時(shí)生成數(shù)據(jù)表:

DailyCountFilter.java

// 在上面代碼的基礎(chǔ)上, 再添加如下代碼public class DailyCountFilter implements SmsFilter {  private Scheduler sched;  @Override  public void init() throws SchedulerException {    smsDao.createTable(0); // 創(chuàng)建這個(gè)月的數(shù)據(jù)表    smsDao.createTable(1); // 創(chuàng)建下個(gè)月的數(shù)據(jù)表    SchedulerFactory sf = new StdSchedulerFactory();    sched = sf.getScheduler(); // 創(chuàng)建Quartz容器    JobDataMap jobDataMap = new JobDataMap();    jobDataMap.put("smsDao", smsDao);  // 創(chuàng)建運(yùn)行任務(wù)時(shí)需要使用的數(shù)據(jù)map     // 創(chuàng)建job對(duì)象, 該對(duì)象執(zhí)行實(shí)際的任務(wù)    JobDetail job = JobBuilder.newJob(CreateSmsTableJob.class)        .usingJobData(jobDataMap)        .withIdentity("create sms table job").build();    // 創(chuàng)建trigger對(duì)象, 該對(duì)象用來(lái)描述觸發(fā)執(zhí)行job的時(shí)間規(guī)則    // 比如這里的每月20號(hào)2點(diǎn)    CronTrigger trigger = TriggerBuilder.newTrigger()        .withIdentity("create sms table trigger")        .withSchedule(CronScheduleBuilder.cronSchedule("0 0 2 20 * ?"))// 每月的20號(hào)2點(diǎn)        .build();    sched.scheduleJob(job, trigger);  // 注冊(cè)任務(wù)和觸發(fā)規(guī)則    sched.start(); // 啟動(dòng)調(diào)度  }  @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"); // 獲得傳過(guò)來(lái)的smsDao對(duì)象      smsDao.createTable(1); // 創(chuàng)建下個(gè)月的數(shù)據(jù)表      smsDao.createTable(2); // 創(chuàng)建下下個(gè)月的數(shù)據(jù)表     }  }}

接下來(lái), 我們看看SmsDao的部分代碼:

SmsDao.java

public class SmsDao {  /**   * 創(chuàng)建新的日志表   *   * @param monthExcursion 偏移的月數(shù)   */  public void createTable(int monthExcursion){    String sql = "CREATE TABLE IF NOT EXISTS "      + getTableName(monthExcursion) + " LIKE sms";    // 執(zhí)行sql語(yǔ)句  }  /**   * 保存SmsEntity實(shí)體對(duì)象   */  public void saveEntity(SmsEntity smsEntity){    String sql = "INSERT INTO "      + getNowTableName() + " (mobile, ip, type) VALUES(?, ?, ?)";    // 執(zhí)行sql語(yǔ)句  }  /**   * 獲得指定手機(jī)號(hào)今天請(qǐng)求發(fā)送短信的次數(shù)   *   * @param mobile 用戶手機(jī)號(hào)   * @return 今天請(qǐng)求發(fā)送短信的次數(shù)   */  public long getMobileCount(String mobile){    String sql = "SELECT count(id) FROM "      + getNowTableName() + " WHERE mobile=? AND time >= CURDATE()";    // 執(zhí)行sql語(yǔ)句, 返回查詢結(jié)果  }  // 省略了getIPCount方法  /**   * 獲得現(xiàn)在使用的表的名字   */  private String getNowTableName() {    return getTableName(0);  }  private DateFormat dateFormat = new SimpleDateFormat("yyyy_MM");  /**   * 獲得相對(duì)現(xiàn)在偏移monthExcursion月的表名   *   * @param monthExcursion 偏移的月數(shù)   * @return 對(duì)應(yīng)月的表名   */  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方法成功運(yùn)行有個(gè)前提, 就是存在sms數(shù)據(jù)表. createTable方法會(huì)復(fù)制sms表的結(jié)構(gòu)創(chuàng)建新的數(shù)據(jù)表.

我們保留發(fā)送短信的數(shù)據(jù)(手機(jī)號(hào), ip, 時(shí)間等), 而不是直接刪除, 是因?yàn)橐院罂赡苄枰治鲞@些數(shù)據(jù), 獲取我們想要的信息, 比如判斷服務(wù)商短信的到達(dá)率、是否有人惡意發(fā)送短信等. 甚至可能獲得意外的"驚喜".

以上就是本文的全部?jī)?nèi)容,希望大家可以繼續(xù)關(guān)注。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 汝阳县| 县级市| 枞阳县| 华容县| 新龙县| 五大连池市| 界首市| 永城市| 平南县| 驻马店市| 六安市| 札达县| 仙桃市| 双城市| 昌吉市| 修水县| 巴南区| 望谟县| 肇庆市| 永寿县| 政和县| 湘乡市| 色达县| 慈利县| 乐平市| 郓城县| 济南市| 安平县| 米泉市| 枣阳市| 城步| 泰来县| 兴山县| 中江县| 登封市| 福贡县| 客服| 临猗县| 鄂州市| 绥中县| 金华市|