我正在开发一个discord机器人,它可以让你使用知乎的API来搜索myanimelist的动漫。然而,该API每秒只允许2个请求,任何违反该请求的行为都将导致IP禁令。因此,我必须对搜索功能(将请求发送到知乎API)进行编程,使其只能同时搜索两样东西。然而,这比看起来要复杂得多。
对于那些不知道Discord API是如何工作的人来说,有一个被覆盖的消息接收方法,当一个用户在包含机器人的服务器上发送消息时,这个方法就会被运行。这个方法也有点像线程,因为每个发送消息的用户都会有一个单独的实例运行。
这使程序变得复杂,因为如果超过3个用户同时搜索一个动漫,我需要以某种方式发送两个请求,同时对第三个请求进行排队,直到另外两个用户中的一个返回响应。但是这样一来,我还得把响应返回给实例的 messageReceived
方法发送的。
对于这个问题,我没有任何代码可以展示。我只是希望能得到一个解释,伪代码,或者java代码来解决这个问题。非常感谢您
我不知道我是否完全理解了你的问题,但似乎你可以用消费者生产者模式解决这个问题,中间有一个阻塞队列。
因此,当用户发送消息时,它不会直接进入知乎API,而是作为一个任务提交到一个阻塞队列中。这将是应用的生产者部分。
消费者部分将通过接收队列中的任务(用户消息)并发送请求来完成向Jihan Rest API的提交。
你可以通过将Callable提交到一个有两个线程的约束线程池中,确保每秒只提交和返回两个请求。由于Callable会阻塞直到完成,所以你可以在两者之间计时一秒.或者你可以使用像semaphore这样的同步器,这将是一个更好的做法。
我看到ilot的回复,我同意他的建议。他已经提到使用semaphores是个好主意,我想我可以分享一个使用该库的Java例子。我没有任何使用Discord的经验,但希望你或其他人会觉得这个例子很有用。
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
final static int TOTAL_REQUESTS = 10;
final static int MAX_SIMULTANEOUS_REQUESTS = 2;
final Semaphore semaphore;
// the purpose of this array is for synchronization of the demo only
final Thread[] threads = new Thread[TOTAL_REQUESTS];
public static void main(String[] args) throws InterruptedException {
new SemaphoreDemo().start();
}
SemaphoreDemo() {
semaphore = new Semaphore(MAX_SIMULTANEOUS_REQUESTS);
}
void start() throws InterruptedException {
System.out.printf("Start sending... [permits = %d]\n", semaphore.availablePermits());
// trigger incoming messages
for (int i = 0; i < TOTAL_REQUESTS; i++) {
messageReceived(new Request(i));
}
// synchronize with threads
for (int i = 0; i < TOTAL_REQUESTS; i++) {
threads[i].join();
}
System.out.println("Done!");
}
void messageReceived(Request request) {
RequestHandler requestHandler = new RequestHandler(request, semaphore);
Thread thread = createThread(request.i, requestHandler);
thread.start();
}
Thread createThread(int i, RequestHandler requestHandler) {
threads[i] = new Thread(requestHandler);
return threads[i];
}
}
class Request {
final int i;
Request(int i) {
this.i = i;
}
void sendHttpRequest() {
// do your thing
}
@Override
public String toString() {
return "Request " + i;
}
}
class RequestHandler implements Runnable {
final Request request;
final Semaphore semaphore;
RequestHandler(Request request, Semaphore semaphore) {
this.request = request;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
// try to acquire one of the permits if available, else wait...
semaphore.acquire();
System.out.printf("%s: sending request [permits = %d]\n", request, semaphore.availablePermits());
// Simulate sending a request with a random response time between 0 and 3 seconds
request.sendHttpRequest();
Thread.sleep(Math.round(3000 * Math.random()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// the request succeeded or was interrupted; always release the permit so another request can take it
semaphore.release();
System.out.printf("%s: received response [permits = %d]\n", request, semaphore.availablePermits());
}
}
}
这个例子模拟了使用一个只有2个许可的旗语的10个同时请求。这意味着当所有的许可都被占用时,剩下的线程如果想从旗语中获取许可,就必须等待其中一个许可被释放。
我得到的一个结果是这样的。
Start sending... [permits = 2]
Request 0: sending request [permits = 1]
Request 1: sending request [permits = 0]
Request 1: received response [permits = 1]
Request 2: sending request [permits = 0]
Request 2: received response [permits = 1]
Request 3: sending request [permits = 0]
Request 0: received response [permits = 1]
Request 4: sending request [permits = 0]
Request 3: received response [permits = 1]
Request 5: sending request [permits = 0]
Request 4: received response [permits = 1]
Request 6: sending request [permits = 0]
Request 5: received response [permits = 1]
Request 7: sending request [permits = 0]
Request 6: received response [permits = 1]
Request 8: sending request [permits = 0]
Request 8: received response [permits = 1]
Request 9: sending request [permits = 0]
Request 9: received response [permits = 1]
Request 7: received response [permits = 2]
Done!
需要注意的是,请求可能会按顺序完成, 但总是会有0到2个可用的许可。