/** * Copyright 2016 LinkedIn Corp. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ package com.github.ambry.network; import com.github.ambry.utils.SystemTime; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // The request at the network layer class SocketServerRequest implements Request { private final int processor; private final String connectionId; private final InputStream input; private final long startTimeInMs; private Logger logger = LoggerFactory.getLogger(getClass()); public SocketServerRequest(int processor, String connectionId, InputStream input) throws IOException { this.processor = processor; this.connectionId = connectionId; this.input = input; this.startTimeInMs = SystemTime.getInstance().milliseconds(); logger.trace("Processor {} received request : {}", processor, connectionId); } @Override public InputStream getInputStream() { return input; } @Override public long getStartTimeInMs() { return startTimeInMs; } public int getProcessor() { return processor; } public String getConnectionId() { return connectionId; } } // The response at the network layer class SocketServerResponse implements Response { private final int processor; private final Request request; private final Send output; private final ServerNetworkResponseMetrics metrics; private long startQueueTimeInMs; public SocketServerResponse(Request request, Send output, ServerNetworkResponseMetrics metrics) { this.request = request; this.output = output; this.processor = ((SocketServerRequest) request).getProcessor(); this.metrics = metrics; } public Send getPayload() { return output; } public Request getRequest() { return request; } public int getProcessor() { return processor; } public void onEnqueueIntoResponseQueue() { this.startQueueTimeInMs = SystemTime.getInstance().milliseconds(); } public void onDequeueFromResponseQueue() { if (metrics != null) { metrics.updateQueueTime(SystemTime.getInstance().milliseconds() - startQueueTimeInMs); } } public NetworkSendMetrics getMetrics() { return metrics; } } interface ResponseListener { public void onResponse(int processorId); } /** * RequestResponse channel for socket server */ public class SocketRequestResponseChannel implements RequestResponseChannel { private final int numProcessors; private final int queueSize; private final ArrayBlockingQueue<Request> requestQueue; private final ArrayList<BlockingQueue<Response>> responseQueues; private final ArrayList<ResponseListener> responseListeners; public SocketRequestResponseChannel(int numProcessors, int queueSize) { this.numProcessors = numProcessors; this.queueSize = queueSize; this.requestQueue = new ArrayBlockingQueue<Request>(this.queueSize); responseQueues = new ArrayList<BlockingQueue<Response>>(this.numProcessors); responseListeners = new ArrayList<ResponseListener>(); for (int i = 0; i < this.numProcessors; i++) { responseQueues.add(i, new LinkedBlockingQueue<Response>()); } } /** Send a request to be handled, potentially blocking until there is room in the queue for the request */ @Override public void sendRequest(Request request) throws InterruptedException { requestQueue.put(request); } /** Send a response back to the socket server to be sent over the network */ @Override public void sendResponse(Send payloadToSend, Request originalRequest, ServerNetworkResponseMetrics metrics) throws InterruptedException { SocketServerResponse response = new SocketServerResponse(originalRequest, payloadToSend, metrics); response.onEnqueueIntoResponseQueue(); responseQueues.get(response.getProcessor()).put(response); for (ResponseListener listener : responseListeners) { listener.onResponse(response.getProcessor()); } } /** * Closes the connection and does not send any response */ @Override public void closeConnection(Request originalRequest) throws InterruptedException { SocketServerResponse response = new SocketServerResponse(originalRequest, null, null); responseQueues.get(response.getProcessor()).put(response); for (ResponseListener listener : responseListeners) { listener.onResponse(response.getProcessor()); } } /** Get the next request or block until there is one */ @Override public Request receiveRequest() throws InterruptedException { return requestQueue.take(); } /** Get a response for the given processor if there is one */ public Response receiveResponse(int processor) throws InterruptedException { return responseQueues.get(processor).poll(); } public void addResponseListener(ResponseListener listener) { responseListeners.add(listener); } public int getRequestQueueSize() { return requestQueue.size(); } public int getResponseQueueSize(int processor) { return responseQueues.get(processor).size(); } public int getNumberOfProcessors() { return numProcessors; } public void shutdown() { requestQueue.clear(); } }