如何做一个简单的开放接口(4)-常见Handler的参考实现
来源:互联网 发布:康辉旅游网站源码 编辑:程序博客网 时间:2024/05/16 13:38
1、概述
核心引擎搞定了,接下来的主要工作就是逐个开发 Handler 了。
常用的Handler包括授权(AuthHandler)、流量控制(TrafficControlHandler)、加解密(EncryptHandler)、安全(SecurityHandler)、压缩(ZipHandler)、序列化(KryoHandler)等。
其他外围功能还包括对调用方的管理功能,开放接口介绍网站等,不再冗述。
2、常用Handler
2.1、授权
实现一个达到实用级别的授权实现需要另开一个专题来讲,这里只讲一些基本原则,并提供一个简单的实现。
2.1.1、自定义Session
每次调用接口都传递调用方id和密码,是最简单粗暴的做法。使用定时失效的token向前走了一步。
Token 可以基于 HttpSession 实现,这种实现同Servlet API极其机密地耦合了。
我们完全可以自己实现类似Session的机制,采用 Redis 等分布式缓存中间件来实现,还自动具备了分布式属性。
代码极其简单,如下所示。
/** * @author liuhailong2008#foxmail */public class ApiSession implements Serializable { private static final long serialVersionUID = 1055965810150154404L; /**Session ID*/ private final String id; /**Session创建时间*/ private long creationTime; /**Session最后一次访问时间*/ private long lastAccessedTime; /**Session的最大空闲时间间隔*/ private int maxInactiveInterval; /**是否是新建Session*/ private boolean newSession; private static final String SESSION_KEY_PREFIX = "SESS_"; //private Set<String> attrNameSet = Collections.synchronizedSet(new HashSet<String>()); private final String sessionKey ; /** * 创建新的Session。 * @param maxIdleSeconds */ public ApiSession(int maxIdleSeconds){ id = StringUtil.getUUID(); long now = System.currentTimeMillis(); creationTime = now; lastAccessedTime = now; this.maxInactiveInterval = maxIdleSeconds; newSession = true; //this.attrNameSet.clear(); sessionKey = SESSION_KEY_PREFIX + id; CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); CacheElement ce = new CacheElement(sessionKey,this); ce.setTimeToIdleSeconds(this.getMaxInactiveInterval()); cb.put(ce); } /** * 通过Session id获取已经存在的Session,如果没有,返回null。 * @return */ public static ApiSession get(String id){ String sessionKey = SESSION_KEY_PREFIX + id; CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); ApiSession ret = (ApiSession) cb.get(sessionKey); if(ret!=null){ ret.newSession = false; ret.refresh(); } return ret; } /** * 更新 lastAccessedTime 。 */ public void refresh() { this.lastAccessedTime = System.currentTimeMillis(); CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); CacheElement ce = new CacheElement(sessionKey,this); ce.setTimeToIdleSeconds(this.getMaxInactiveInterval()); cb.put(ce); } /** * 是否超时过期。 * * @param session * @return */ public boolean isExpired() { CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); ApiSession _this = (ApiSession) cb.get(this.sessionKey); // 先查看缓存层面的超时控制 if(_this==null){ return false; } long now = System.currentTimeMillis(); long last = this.getLastAccessedTime(); long interal = now - last; if(interal>this.getMaxInactiveInterval()){ this.invalidate(); return true; }else{ return false; } } /** * 强制Session立即失效。 */ public synchronized void invalidate() { CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); cb.remove(this.sessionKey); } /** * 移除属性。 * * @param attrName * @return */ public synchronized Object removeAttribute(String attrName){ this.refresh(); String attrSessionKey = getAttrSessionKey(attrName); CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); Object ret = cb.remove(attrSessionKey); return ret; } /** * 设置属性。 * @param attrName * @param attrValue */ public synchronized void setAttribute(String attrName,Object attrValue){ this.refresh(); String attrSessionKey = getAttrSessionKey(attrName); CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); CacheElement ce = new CacheElement(attrSessionKey,attrValue); ce.setTimeToIdleSeconds(this.getMaxInactiveInterval()); cb.put(ce); } /** * 获取属性的值。 * @param attrName * @return */ public Object getAttribute(String attrName){ this.refresh(); String attrSessionKey = getAttrSessionKey(attrName); CacheBlock cb = CacheManager.getBlock(Const.CACHE_BLOCK_INDEX); Object retObject = cb.get(attrSessionKey); return retObject; } private String getAttrSessionKey(String attrName){ String attrSessionKey = sessionKey + attrName; return attrSessionKey; } public int getMaxInactiveInterval() { if(maxInactiveInterval==-1){ maxInactiveInterval = 3600; } return maxInactiveInterval; } public void setMaxInactiveInterval(int maxInactiveInterval) { this.maxInactiveInterval = maxInactiveInterval; } public String getId() { return id; } public long getCreationTime() { return creationTime; } public long getLastAccessedTime() { return lastAccessedTime; } public boolean isNewSession() { return newSession; }}
2.1.2、基于 Token 的简单授权机制
调用方发送加密的用户名、密码消息,调用 getToken 接口。校验通过后,服务器返回 Token 。
调用方使用临时的 Token 访问其他接口。空闲固定时间间隔后,Token失效。
简单来说,Token 可以理解为 Session Id。
2.2、流量控制
可以提供3种流量控制方法,以减轻服务器压力。
2.2.1、访问间隔限制
避免大批量并发形成波峰。
long latestTime = parseLong(ApiCacheBlock.get(latestTimeCacheKey));//上次调用时间long intervalLimit = (apiOrgBO.getIntervalLimit()==null)?0L:apiOrgBO.getIntervalLimit().longValue();if(intervalLimit>0){// 需要执行时间间隔限制 // 触发事件间隔限制 if(now - latestTime < intervalLimit){ throw new ApiException(String.format("连续两次调用之间的时间间隔不能小于%d毫秒。",intervalLimit)); }}
2.2.2、访问时间段限制
以下代码只是范例,需要根据实际服务器负载情况调整。
String timeString = timeFormat.format(nowDate);String timeRangeLimitStart = StringUtil.safe2String(apiOrgBO.getTimeRangeLimitStart());String timeRangeLimitEnd = StringUtil.safe2String(apiOrgBO.getTimeRangeLimitEnd());if(!StringUtils.isEmpty(timeRangeLimitStart)){ if(timeString!=null && timeString.compareTo(timeRangeLimitStart)<0){ throw new ApiException(String.format("系统不早于%s提供服务。",timeRangeLimitStart)); }}if(!StringUtils.isEmpty(timeRangeLimitEnd)){ if(timeString!=null && timeString.compareTo(timeRangeLimitStart)>0){ throw new ApiException(String.format("系统不晚于%s提供服务。",timeRangeLimitEnd)); }}
2.2.3、当天访问次数限制
计数器自增操作的原子性依赖于 Redis 的 INCR 命令。
long cntInDay = cntInDayPlus(latestDate,dateString,cntInDayCacheKey);long dayCntLimit = (apiOrgBO.getDayCntLimit()==null)?0L:apiOrgBO.getDayCntLimit().longValue();if(dayCntLimit>0){ if(cntInDay>dayCntLimit){ throw new ApiException(String.format("同一天内调用接口不能超过%d次。",dayCntLimit)); }}
3、欢迎探讨
未尽事宜,欢迎留言探讨。
1 1
- 如何做一个简单的开放接口(4)-常见Handler的参考实现
- 如何做一个简单的开放接口(1)-功能设计
- 如何做一个简单的开放接口(2)-核心引擎(上)
- 如何做一个简单的开放接口(3)-核心引擎(下)
- 一个简单的接口实现
- 一个简单的解决方案文档-仅做参考.
- 一个简单的heat模板,仅做自己参考
- 如何做一个简单的APP
- PHP如何实现简单的接口
- 一个简单的java web程序(servlet实现接口)
- Handler 实现简单的计时器
- 开放的AJAX接口
- 如何做一个简单的Guide(引导)页面
- 如何做一个简单的自动化压力测试
- 如何实现一个简单的remoteing实例
- 如何实现一个简单的rpc框架
- 做一个简单的太阳系(二)
- 做一个简单的井字棋(一)
- Python - pwntools
- 【hive内置基本数据类型】 和 【内置复合数据类型用法】
- iOS开发笔记10--App store最新审核标准(2015.3)公布
- 使用Auto Layout导致调用addSubView时出现的问题
- 我叫许大方
- 如何做一个简单的开放接口(4)-常见Handler的参考实现
- 基于Redis实现分布式Session
- Debian用apt-get 安装软件时出现的问题!
- 黑马程序员_Java基础(方法,数组)
- FCkEditor编辑器漏洞(IIS 6.0)
- 模版方法模式
- 题目2:MySQL----------Second Highest Salary
- 《Java程序设计》第二次作业:MySQL数据库及Java操作MySQL数据库
- 开放接口平台 in Action(汇总目录)