实现频控 在广告行业中,经常会有频控的限制,来防止同一个广告被同一个用户多次的访问
使用redis实现 可以使用redis来实现频控,首先想一下redis存储的key是什么?我们要限制的是一波广告投放下同一个用户的访问次数,那么key可以设置为uid:campid,也就是用户id拼上投放id作为频控。
那么value存储什么内容呢?如果只是单纯的整个投放周期下的频控的话,那其实设置为访问次数就可以了。但是有的时候周期不是整个投放周期(如按天频控、按周频控、按月频控等)
使用以下逻辑
1 2 3 4 5 private void addExpose (String uid, String campaignId) { String key = uid+":" +campaignId; jedis.lpush(key, String.valueOf(System.currentTimeMillis())); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private boolean isOver (String uid, String campaignId, long range,int limit) { String key = uid+":" +campaignId; long cur = System.currentTimeMillis(); List<String> exposes = jedis.lrange(key, 0 , 10 ); int count = 0 ; if (exposes == null || exposes.isEmpty()) { return false ; } else { for (String expose: exposes) { long time = Long.parseLong(expose); if (time > cur - range) { count++; } else { break ; } } } return count > limit; }
使用hbase 由于广告投放中的数据量是很大的,存在redis中内存扛不住,所以后来使用了hbase来进行实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 private boolean isOver (String uid, String campId, long range,int limit) { long cur = System.currentTimeMillis(); List<Long> exposes = getExposeFromHbase(uid, campId); int count = 0 ; if (exposes == null || exposes.isEmpty()) { return false ; } else { for (long time: exposes) { if (time > cur - range) { count++; } else { break ; } } } return count > limit; } public List<Long> getExposeFromHbase (String uid, String campId) { if (StringUtils.isBlank(uid) || StringUtils.isBlank(campId)) { return null ; } Table htable = null ; try { htable = connection.getTable(TableName.valueOf("expose" )); Get get = new Get(Bytes.toBytes(uid)); get.addColumn(Bytes.toBytes("f" ),Bytes.toBytes(campId)); Result result = htable.get(get); if (result != null && !result.isEmpty()) { byte [] value = result.getValue(Bytes.toBytes("f" ),Bytes.toBytes(campId)); String s = new String(value); Gson gson = new Gson(); List<Long> tsList = gson.fromJson(s, new TypeToken<ArrayList<Long>>() { }.getType()); return tsList; } } catch (IOException e) { LOGGER.error("get camp list failed: {}" , uid); }finally { if (htable != null ) { try { htable.close(); } catch (Exception closeExcption) { LOGGER.error("close connection failed: " , closeExcption); } } } return null ; }
添加曝光
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public void addExpose (String uid, String campId) { long ts = System.currentTimeMillis(); List<Long> tsList; try { tsList = getExposeFromHbase(uid, campId); } catch (Exception e) { LOGGER.error(e.toString()); return ; } if (tsList == null ) { tsList = new ArrayList<>(); } tsList.add(ts); addExposeInHbase(uid, campId,tsList); } public void addExposeInHbase (String uid, String campId,List<Long> tsList) { Table htable = null ; try { htable = connection.getTable(TableName.valueOf("expose" )); Gson gson = new Gson(); Put put = new Put(Bytes.toBytes(uid)); put.addColumn(Bytes.toBytes("f" ), Bytes.toBytes(campId), Bytes.toBytes(gson.toJson(tsList))); put.setTTL(3600 * 24 * 90 * 1000L ); htable.put(put); } catch (IOException e) { LOGGER.error("write to hbase failed: {}" , uid + ":" + e.getMessage()); } finally { if (htable != null ) { try { htable.close(); } catch (Exception closeExcption) { LOGGER.error("close connection failed: " , closeExcption); } } } }