固定频率调用接口方案
本文来源于一次工作中,对某第三方平台进行二次开发。这个第三方平台提供的接口有一个很扯淡的限制:限制每秒调用频率为最多2次。解决这个问题,有下面几个问题需要解决:
- 第三方调用频率策略修改,能快速修改,适应其策略
- 在具体的业务逻辑不需要关注调用频率的变化
由此,想到使用自定义线程池来解决,调用时候,只需要忘线程池提交任务。其他接口调用频率交给线程池来处理。我使用单线程池,同时修改等待队列为DelayQueue,DelayQueue支持延迟固定时间take一个元素。
![](/assets/blank.gif)
![](/assets/blank.gif)
1 public E take() throws InterruptedException { 2 final ReentrantLock lock = this.lock; 3 lock.lockInterruptibly(); 4 try { 5 for (;;) { 6 E first = q.peek(); 7 if (first == null) 8 available.await(); 9 else { 10 long delay = first.getDelay(NANOSECONDS); 11 if (delay <= 0) 12 return q.poll(); 13 first = null; // don't retain ref while waiting 14 if (leader != null) 15 available.await(); 16 else { 17 Thread thisThread = Thread.currentThread(); 18 leader = thisThread; 19 try { 20 available.awaitNanos(delay); 21 } finally { 22 if (leader == thisThread) 23 leader = null; 24 } 25 } 26 } 27 } 28 } finally { 29 if (leader == null && q.peek() != null) 30 available.signal(); 31 lock.unlock(); 32 } 33 }
DelayQueue的take方法
其中有最关键的是:
![](/assets/blank.gif)
![](/assets/blank.gif)
1 long delay = first.getDelay(NANOSECONDS); 2 if (delay <= 0) 3 return q.poll();
View Code
所以最为关键的试试实现自定义的getDelay方法。由于该对列只支持Delay类型,而普通的线程提交只支持Runnable和Callble,需要做一个适配。
![](/assets/blank.gif)
![](/assets/blank.gif)
1 public class DelayedThreadPoolFactoryBean extends ThreadPoolExecutorFactoryBean { 2 Logger logger = LoggerFactory.getLogger(this.getClass()); 3 private TimeUnit timeUnit; 4 private long delayed; 5 6 public void setTimeUnit(TimeUnit timeUnit) { 7 this.timeUnit = timeUnit; 8 } 9 10 public void setDelayed(long delayed) { 11 this.delayed = delayed; 12 } 13 @Override 14 protected BlockingQueue<Runnable> createQueue(int queueCapacity) { 15 return new DelegateDelayedQueue(); 16 } 17 18 @Override 19 protected ThreadPoolExecutor createExecutor(int corePoolSize,int maxPoolSize,int keepAliveSeconds,final BlockingQueue<Runnable> queue,ThreadFactory threadFactory,RejectedExecutionHandler rejectedExecutionHandler) { 20 21 return new ThreadPoolExecutor(corePoolSize, maxPoolSize, 22 keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler){ 23 @Override 24 public void execute(Runnable command) { 25 final DelegateDelayedQueue queue1 = (DelegateDelayedQueue)queue; 26 DelegateDelaye delegateDelaye = new DelegateDelaye(command,queue1,timeUnit,delayed); 27 super.execute(delegateDelaye); 28 } 29 }; 30 } 31 private class DelegateDelaye implements Delayed,Runnable{ 32 private Runnable runnable; 33 private DelegateDelayedQueue delegateDelayedQueue; 34 private TimeUnit timeUnit; 35 private long delayed; 36 public DelegateDelaye(Runnable runnable,DelegateDelayedQueue delegateDelayedQueue,TimeUnit unit,long delayed){ 37 this.runnable = runnable; 38 this.delegateDelayedQueue = delegateDelayedQueue; 39 this.timeUnit = timeUnit; 40 this.delayed = delayed; 41 } 42 43 @Override 44 public long getDelay(TimeUnit unit) { 45 if(delegateDelayedQueue.getLast() == 0){ 46 return -1; 47 } 48 return unit.convert(delegateDelayedQueue.getLast() + 49 TimeUnit.MILLISECONDS.convert(5,TimeUnit.SECONDS) - 50 System.currentTimeMillis(),TimeUnit.MILLISECONDS); 51 } 52 53 @Override 54 public int compareTo(Delayed o) { 55 return 0; 56 } 57 58 @Override 59 public void run() { 60 runnable.run(); 61 } 62 } 63 private class DelegateDelayedQueue extends DelayQueue{ 64 private long last = 0;//默认为0 65 66 @Override 67 public Delayed poll() { 68 Delayed delayed = super.poll(); 69 if(delayed != null){ 70 this.last = System.currentTimeMillis(); 71 } 72 return delayed; 73 } 74 75 @Override 76 public Delayed take() throws InterruptedException { 77 Delayed delayed = super.take(); 78 if(delayed != null){ 79 this.last = System.currentTimeMillis(); 80 } 81 return delayed; 82 } 83 84 @Override 85 public Delayed poll(long timeout,TimeUnit unit) throws InterruptedException { 86 Delayed delayed = super.poll(timeout,unit); 87 if(delayed != null){ 88 this.last = System.currentTimeMillis(); 89 } 90 return delayed; 91 } 92 93 public long getLast() { 94 return last; 95 } 96 } 97 }
完整实现的线程池
简单测试:
![](/assets/blank.gif)
![](/assets/blank.gif)
1 @Test 2 public void testSinglePool(){ 3 ExecutorService executorService = executoryServiceFactory.executorService(ExecutoryServiceFactory.Pool.DELAYED); 4 5 executorService.submit(new Callable<String>() { 6 @Override 7 public String call() throws Exception { 8 System.out.println(TimeUnit.SECONDS.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS)); 9 return null; 10 } 11 }); 12 executorService.execute(new Runnable() { 13 @Override 14 public void run() { 15 System.out.println(TimeUnit.SECONDS.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS)); 16 } 17 }); 18 executorService.execute(new Runnable() { 19 @Override 20 public void run() { 21 System.out.println(TimeUnit.SECONDS.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS)); 22 } 23 }); 24 executorService.execute(new Runnable() { 25 @Override 26 public void run() { 27 System.out.println(TimeUnit.SECONDS.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS)); 28 } 29 }); 30 executorService.execute(new Runnable() { 31 @Override 32 public void run() { 33 System.out.println(TimeUnit.SECONDS.convert(System.currentTimeMillis(),TimeUnit.MILLISECONDS)); 34 } 35 }); 36 executorService.shutdown(); 37 try { 38 executorService.awaitTermination(5,TimeUnit.MINUTES); 39 } catch (Exception e){} 40 }
View Code
结果符合预期的效果,固定频率执行任务。
转载于:https://www.cnblogs.com/dragonfei/p/5908900.html
固定频率调用接口方案相关推荐
- Oracle调用接口(OCI)源码剖析(2):执行SQL语句并获取结果
概述 接着上一篇文章<Oracle调用接口(OCI)源码剖析(1):创建数据库连接>,我们继续对OCI中执行SQL语句并获取结果的源码进行剖析.该操作主要是由两个函数完成的:CDbExec ...
- navision系统和sap区别_SAP那些事-实战篇-89-浅谈金税接口方案
以前金税接口这块一直是销售顾问在做,虽然和财务相关,也没有怎么关注.这次项目把金税接口分到了财务模块,结果遇到了一些问题,趁此机会把这块总结一下方案,供各位看官参考. 方案1: 文本方案,这个方案最早 ...
- PHP微信防止token过期,微信调用接口,防止Access_token过期的方法
大家都知道,微信中调用订阅用户接口中需要Access_token,而根据微信官方文档中说明: access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.正常情 ...
- android 播放固定频率的声音,Android 播放指定频率正弦波声音
用Android设备来播放指定频率的纯音,首先考虑到纯音是一种固定频率的声波,其实也就是正弦声波了.其实所有声音都是有正弦波组成,只不过纯音是固定频率的正弦波.那么要实现Andoird播放纯音,那么首 ...
- 演示淘宝API调用接口
很简单,新建一个工程,拖一个IDHttp,Button和Memo到窗体上去 然后在这个Button的OnClick事件中写入如下代码:[delphi] view plaincopyprocedureT ...
- android 播放固定频率的声音,Android 播放指定频率的纯音
用Android设备来播放指定频率的纯音,首先考虑到纯音是一种固定频率的声波,其实也就是正弦声波了.其实所有声音都是有正弦波组成,只不过纯音是固定频率的正弦波.那么要实现Andoird播放纯音,那么首 ...
- 两个系统如何调用接口获取返回值
1.使用场景 两个公司进行合作,但是是两个毫不相关的项目,所以就需要使用http请求远程访问接口获取返回值. 2.如何做到 使用http请求建立连接访问接口获取返回值并解析 调用其他系统接口工具类如下 ...
- java调用php接口_java 如何调用接口
在实际开发过程中,我们经常需要调用对方提供的接口或测试自己写的接口是否合适,所以,问题来了,java如何调用接口?很多项目都会封装规定好本身项目的接口规范,所以大多数需要去调用对方提供的接口或第三方接 ...
- java调用接口失败重调_接口调用失败的退避策略
退避策略简介 在开发过程中我们经常会遇到调用接口失败的情况.遇到这种情况,我们有时候需要重试机制,常用的重试(退避)策略有: 固定的时间间隔重试一次,最多重试N次:比如我现在一个接口调用失败了,不是立 ...
最新文章
- break与continue的的用法以及区别
- python怎么写中文至excel_Python 解决中文写入Excel时抛异常的问题
- Cannot open precompiled header file: 'Debug/***.pch': No such file or directory
- HarmonyOS 2.0:正式开源,年底面向开发者发布智能手机 Beta 版本
- windows服务与前台交互
- 广播接收者的特点和版本差异
- 银河麒麟系统10服务器安装教程,麒麟系统下安装win10的详细教程
- 战舰 Linux服务端,战舰帝国一键端手游服务端+GM工具+视频教程
- 《计算机科学与工程导论:基于IoT和机器人的可视化编程实践方法第2版》一2.1 工作流和可视化编程...
- 【后端学习之路】Browsers and how they work?
- 简述igp和egp_igp egp
- mysql字符串分割为数组_mysql下将分隔字符串转换为数组
- 一次Spring Cache使用不当带来的生产环境问题
- Riverbed SteelConnect荣获2017 Interop东京展“最佳展示奖”
- 面试要谈的计算机网络
- 工作四年,分享15个对Java 程序员有用的库
- 文件操作安全之-文件解析原理篇
- wp7开发实例:Baby Sign Language
- python 处理csv文件 一个简单的数据处理任务
- b ascll 对照表