/*
* Copyright 2009 Joubin Houshyar
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jredis.ri.alphazero.wip;
//import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import org.jredis.ClientRuntimeException;
import org.jredis.ProviderException;
import org.jredis.protocol.Command;
import org.jredis.protocol.Response;
import org.jredis.protocol.Response.Type;
import org.jredis.ri.alphazero.protocol.ProtocolBase;
import org.jredis.ri.alphazero.support.Convert;
import org.jredis.ri.alphazero.support.FastBufferedInputStream;
import org.jredis.ri.alphazero.support.Log;
public class AsynchConnection {
public static void main(String[] args) throws Exception {
AsynchConnection temp = new AsynchConnection();
String host = "localhost";
temp.doConnect (host);
temp.test();
// temp.doClose();
}
private void test() throws Exception {
System.out.println("Hi!");
startRequesters(100, 10000, true);
boolean throttle = false;
int cnt = 0;
while (throttle) {
cnt++;
// pingReq();
// pingReq();
// pingReq();
pingReq().completion.await();
// incrReqSync ("counter").completion.await();
incrReqSync ("counter");
// getReqSynch("foo").completion.await();
getReqSynch("foo").completion.await();
// Thread.currentThread().yield();
// getReq("counter");
}
keepWorking = false;
System.out.println("bye!");
}
public void startRequesters(int threads, final int reqCnt, final boolean waitForCompletion) throws InterruptedException{
// run 30 simultanuous threads
// int threads = 1;
final int counters[] = new int[threads];
Thread[] workers = new Thread[threads];
for(int t=0; t<threads; t++){
final int idx = t;
// final BlockingDeque<Pending> pending = new LinkedBlockingDeque<Pending>();
workers[t] = new Thread(new Runnable() {
int id = idx;
// @Override
public void run () {
String counterId = "counter<" + this.hashCode() + ">:" + System.currentTimeMillis();
// while(true){
int last = 0;
Pending pendingRequest = null;
for(int i=0; i<reqCnt; i++){
try {
// pendingRequest = pingReq();
pendingRequest = incrReqSync(counterId);
// pendingRequest = getReqSynch("foo");
if(waitForCompletion)
pendingRequest.completion.await();
}
catch (Exception e) { e.printStackTrace(); }
}
try {
if(!waitForCompletion)
pendingRequest.completion.await();
int lastValue = pendingRequest.intValue;
// String value = DefaultCodec.toStr(pendingRequest.bulkData);
System.out.format("%d\n", lastValue);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, "requester_" + t);
}
for(int i=0; i<threads; i++){
System.out.format("starting %d\n", i);
workers[i].start();
}
for(int i=0; i<threads; i++)
workers[i].join();
System.out.println("all done with requesting threads ...");
}
// ----------------------------------------------------------------------------------------------
// HACK Request Processing
// ----------------------------------------------------------------------------------------------
private void smembersReq(String key) throws Exception {
serviceRequest(Command.SMEMBERS, key);
}
private Pending getReqSynch(String key) throws Exception {
return serviceRequest(Command.GET, key);
}
private void authReq(String key) throws Exception {
serviceRequest(Command.AUTH, key);
}
private Pending pingReq() throws Exception {
return serviceRequest(Command.PING, "");
}
private int incrReq(String key) throws Exception {
// note that this is wrong since its not really waiting completion
return serviceRequest(Command.INCR, key).intValue;
}
private Pending incrReqSync(String key) throws Exception {
return serviceRequest(Command.INCR, key);
}
// ----------------------------------------------------------------------------------------------
// HACK RequestHandler
// ----------------------------------------------------------------------------------------------
// Semaphore service = new Semaphore(1);
final Object lock = new Object();
boolean keepWorking = true;
// ByteArrayOutputStream bout = new ByteArrayOutputStream();
private Pending serviceRequest (Command cmd, String key) throws IOException, InterruptedException{
// bout.reset();
//// ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
// bout.write(cmd.code.getBytes());
// bout.write((byte)32);
// bout.write(key.getBytes());
// bout.write(CRLF);
// byte[] data = bout.toByteArray();
byte[] b_cmd = cmd.bytes;
byte[] b_key = key.getBytes();
byte[] data = new byte[b_cmd.length + b_key.length + CRLF.length + 1];
System.arraycopy(b_cmd, 0, data, 0, b_cmd.length);
data[b_cmd.length] = (byte)32;
System.arraycopy(b_key, 0, data, b_cmd.length+1, b_key.length);
data[b_cmd.length + 1 + b_key.length] = CRLF[0];
data[b_cmd.length + 1 + b_key.length+1] = CRLF[1];
Pending request = new Pending(cmd, key);
request._data = data;
// -- begin synch block
// service.acquire();
synchronized (lock) { // seems to have higher peaks, but is not as even as a semaphore but it seems to be faster
out.write(data);
out.flush();
pendingQueue.add(request);
}
// service.release();
// -- END synch block
return request;
}
/************************************************************************************************
** Multi-Threaded Request Handler
* @throws InterruptedException **
************************************************************************************************/
// ----------------------------------------------------------------------------------------------
// Threaded workers
// ----------------------------------------------------------------------------------------------
private Runnable getResponseHandler (final Queue<Pending> pendingQueue) {
return new Runnable () {
public void run () {
try {
processPendingRequests (pendingQueue, in);
}
catch (IOException e) {
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
// private Runnable getRequestHandler (final Queue<Pending> requests, final Queue<Pending> pendingQueue){
// return new Runnable(){
// public void run() {
// while(true){
// Pending request = null;
// if((request = requests.poll()) != null){
// try {
//// System.out.format("=> %d\n", request._data.length);
// out.write(request._data);
// out.flush();
// request._data = null;
// pendingQueue.add(request);
//// System.out.format("<=Q %d\n", pendingQueue.size());
//// System.out.println (".");
// }
// catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// else {
// Thread.yield();
// }
// }
// }
// };
// }
// ----------------------------------------------------------------------------------------------
// HACK Connection
// ----------------------------------------------------------------------------------------------
private InetAddress address = null;
private InetSocketAddress socketAddress;
private int port = 6379;
private Socket socket;
private void doClose() throws IOException {
socket.close();
}
private void doConnect(String host) {
try {
initializeConnection (host);
initializeComponents ();
}
catch (Exception e) {
e.printStackTrace();
}
}
private void initializeComponents() {
// Runnable reader = getReader(in, inqueue);
// new Thread(reader, "input-reader").start();
// Runnable handler = getResponseHandler(inqueue, pendingQueue);
// Runnable writer = getRequestHandler(requestQueue, pendingQueue);
// new Thread(writer, "request-writer").start();
Runnable handler = getResponseHandler(pendingQueue);
new Thread(handler, "response-handler").start();
}
private void initializeConnection (String host) throws IOException {
address = InetAddress.getByName(host);
socket = new Socket ();
socket.setKeepAlive (true);
socket.setPerformancePreferences(0, 2, 1);
socketAddress = new InetSocketAddress(address, port);
socket.connect(socketAddress);
initializeStreams(socket);
}
// ----------------------------------------------------------------------------------------------
// I/O Streams
// ----------------------------------------------------------------------------------------------
private InputStream in;
private OutputStream out;
private void initializeStreams (Socket aSocket) throws IOException {
// out = new BufferedOutputStream (aSocket.getOutputStream());
out = aSocket.getOutputStream();
// in = new BufferedInputStream (aSocket.getInputStream(), BUFF_SIZE);
in = socket.getInputStream();
}
// ----------------------------------------------------------------------------------------------
// *** Globals ***
// ----------------------------------------------------------------------------------------------
/** Global Buffer Size -- all buffers, streams, and databuffers should be aligned on this */
int BUFF_SIZE = 1024 * 24;
/* -- CONTROL */
boolean keep_reading = true;
boolean keep_processing = true;
// ----------------------------------------------------------------------------------------------
// Request Processing Queues and Classes
// ----------------------------------------------------------------------------------------------
/** queue: FRESH DATA BLOCKS */
// LinkedBlockingDeque<DataBlock> inqueue = new LinkedBlockingDeque<DataBlock>();
// LinkedBlockingDeque<byte[]> inqueue = new LinkedBlockingDeque<byte[]>();
/** queue: PENDING REQUESTS */
// LinkedBlockingDeque<Pending> pendingQueue = new LinkedBlockingDeque<Pending>();
Queue<Pending> pendingQueue = new ConcurrentLinkedQueue<Pending>();
Queue<Pending> requestQueue = new ConcurrentLinkedQueue<Pending>();
// ----------------------------------------------------------------------------------------------
// HACK - Specialized Queue Classes
// ----------------------------------------------------------------------------------------------
/**
* TODO: Pending needs to wrap all possible commands, not just CMD + key type.
* important elements are the latches and the status and response.
*/
public static final class Pending {
public byte[] _data;
final public String id;
public CountDownLatch completion = new CountDownLatch(1);
public String key;
public Command cmd;
public final long request_time;
private long process_time;
public Object response;
public boolean isError = false;
public String status = null;
public int intValue;
public byte[] bulkData;
public List<byte[]> multiBulkData;
public Pending (Command cmd, String key){
this.cmd = cmd; this.key = key;
request_time = System.currentTimeMillis();
id = this.hashCode() + "@" + request_time;
}
public void setResponse(Object response){
this.response = response;
this.process_time = System.currentTimeMillis() - request_time;
}
public long getProcessTime() { return process_time; }
}
private void processPendingRequests (
Queue<Pending> _pendingQueue,
InputStream inStream
)
throws
IOException,
InterruptedException
{
// BlockingDequeDataSource datasource = new BlockingDequeDataSource(_dataQueue);
// InputStreamDataSource datasource = new InputStreamDataSource(inStream, BUFF_SIZE);
// DataSourceInputStream in = new DataSourceInputStream(datasource, BUFF_SIZE);
FastBufferedInputStream in = new FastBufferedInputStream(inStream, BUFF_SIZE);
Response.Type responseType = null;
Pending pending = null;
long mark=System.currentTimeMillis();
long delta=0;
int cnt = 0;
int errcnt = 0;
int gate = 100000;
while (keepWorking){ // <<< some master flag is needed here
// try {
// if(_pendingQueue.size() > 200) {
// System.out.format("pendingProcessor: pending queue size is %d\n", _pendingQueue.size());
// }
// pending = _pendingQueue.take(); // this is using Deque semantics
// try {
if((pending = _pendingQueue.poll()) == null) {
// System.out.format("<=Q %d\n", _pendingQueue.size());
Thread.yield();
}
else {
// System.out.format("<=proc: %s\n", pending.cmd.name());
switch (pending.cmd) {
case PING:
case AUTH:
responseType = Type.Status;
break;
case GET:
responseType = Type.Bulk;
break;
case INCR:
responseType = Type.Value;
break;
case SMEMBERS:
responseType = Type.MultiBulk;
break;
default:
System.out.format("processIncomingData() - Pending Processer => don't know cmd %s \n", pending.cmd);
continue;
}
try {
switch(responseType){
case Status:
getStatusResponse(pending, in);
break;
case Value:
getValueResponse(pending, in);
break;
case Bulk:
getBulkResponse (pending, in);
break;
case MultiBulk:
getMultiBulkResponse (pending, in);
break;
}
pending.process_time = System.currentTimeMillis();
}
catch (IOException e) {
Log.error("<" + Thread.currentThread().getName() + "> processing response" + e.getMessage());
}
pending.completion.countDown();
cnt++;
if(cnt == gate){
delta = System.currentTimeMillis() - mark;
float rate = (cnt * 1000)/delta;
Log.log("");
Log.log("<" + Thread.currentThread().getName() + "> processPendingRequests() -- REPORT:");
Log.log("<" + Thread.currentThread().getName() + "> throughput %6.2f /sec | %6d reqs | %5d msecs [errors: %d]", rate, cnt, delta, errcnt);
Log.log("<" + Thread.currentThread().getName() + "> last request processed: %s", pending.id);
mark = System.currentTimeMillis();
cnt=0;
errcnt=0;
}
}
// switch (pending.cmd) {
// case PING:
// case AUTH:
// responseType = Type.Status;
// break;
// case GET:
// responseType = Type.Bulk;
// break;
// case INCR:
// responseType = Type.Value;
// break;
// case SMEMBERS:
// responseType = Type.MultiBulk;
// break;
// default:
// System.out.format("processIncomingData() - Pending Processer => don't know cmd %s \n", pending.cmd);
// continue;
// }
// }
// catch (InterruptedException e) {
// Log.error("<" + Thread.currentThread().getName() + "> removing from pending queue" + e.getMessage());
// }
// try {
// switch(responseType){
// case Status:
// getStatusResponse(pending, in);
// break;
// case Value:
// getValueResponse(pending, in);
// break;
// case Bulk:
// getBulkResponse (pending, in);
// break;
// case MultiBulk:
// getMultiBulkResponse (pending, in);
// break;
// }
// pending.process_time = System.currentTimeMillis();
// }
// catch (InterruptedException e) {
// Log.error("<" + Thread.currentThread().getName() + "> processing response" + e.getMessage());
// }
// catch (IOException e) {
// Log.error("<" + Thread.currentThread().getName() + "> processing response" + e.getMessage());
// }
// catch (ProviderException e) {
// Log.error("<" + Thread.currentThread().getName() + "> processing response" + e.getMessage());
// errcnt++;
// }
// pending.completion.countDown();
// cnt++;
// if(cnt == gate){
// delta = System.currentTimeMillis() - mark;
// float rate = (cnt * 1000)/delta;
// Log.log("");
// Log.log("<" + Thread.currentThread().getName() + "> processPendingRequests() -- REPORT:");
// Log.log("<" + Thread.currentThread().getName() + "> throughput %6.2f /sec | %6d reqs | %5d msecs [errors: %d]", rate, cnt, delta, errcnt);
// Log.log("<" + Thread.currentThread().getName() + "> last request processed: %s", pending.id);
// mark = System.currentTimeMillis();
// cnt=0;
// errcnt=0;
// }
// System.out.format("Request %s %s processed in %d msecs\n", pending.cmd, pending.key, pending.getProcessTime());
}
}
/* -------------------------------------------------------------------- */
/* RESPONSE PROTOCOL STREAM PROCESSING -- REPLACE SYNCH PROTOCOL STUFF */
/* -------------------------------------------------------------------- */
/**
* @param pending
* @param in2
*/
private byte[] lineBuffer = new byte[128];
private int lineBufferOffset = 0;
/**
* Reads a single CRLF terminated line to lineBuffer. lineBufferOffset will point to the next writable byte in lineBuffer.
* @param in
* @throws IOException
*/
private void readLine (InputStream in) throws IOException {
lineBufferOffset = 0;
try {
int c = -1;
while((c = in.read(lineBuffer, lineBufferOffset, 1)) != -1){
lineBufferOffset += c;
if(lineBufferOffset > 2 && lineBuffer[lineBufferOffset-2]==CRLF[0] && lineBuffer[lineBufferOffset-1]==CRLF[1]){
break; // we're done
}
if(lineBuffer.length-lineBufferOffset == 0) {
byte[] newbuff = new byte[lineBuffer.length * 2];
System.arraycopy(lineBuffer, 0, newbuff, 0, lineBuffer.length);
lineBuffer = newbuff;
}
}
}
catch (IOException e){
e.printStackTrace();
throw e;
}
}
public final byte[] readBulkData (InputStream in, int length)
throws IOException, RuntimeException
{
byte[] data = new byte[length]; // TODO: optimize me
byte[] term = new byte[CRLF.length];
int readcnt = -1;
int offset = 0;
while(offset < length){
if((readcnt = in.read (data, offset, length-offset)) ==-1 ) throw new ClientRuntimeException("IO - read returned -1 -- problem");
offset += readcnt;
}
if((readcnt = in.read (term, 0, CRLF.length)) != CRLF.length) {
throw new RuntimeException ("Only read " + readcnt + " bytes for CRLF!");
}
return data;
}
/**
* @param pending
* @param in
* @throws IOException
*/
private void getStatusResponse (Pending pending, InputStream in) throws IOException {
readLine (in);
byte code = lineBuffer[0];
if(code != ProtocolBase.OK_BYTE && code != ProtocolBase.ERR_BYTE) {
throw new ProviderException ("Got [ " + (char)code + " | "+code+" ] when expecing either - or + pending.status: " + pending.status);
}
pending.isError = code == ProtocolBase.ERR_BYTE;
pending.status = new String(lineBuffer, 1, lineBufferOffset - 3);
}
private void getValueResponse (Pending pending, InputStream in) throws IOException {
readLine (in);
byte code = lineBuffer[0];
if(code != ProtocolBase.NUM_BYTE && code != ProtocolBase.ERR_BYTE) {
throw new ProviderException ("Got [ " + (char)code + " | "+code+" ] when expecing either - or : pending.status: " + pending.status);
}
if((pending.isError = (code == ProtocolBase.ERR_BYTE)) != true){
// flavor switch goes here
pending.intValue = Convert.toInt(lineBuffer, 1, lineBufferOffset - 3);
}
else {
pending.status = new String(lineBuffer, 1, lineBufferOffset - 3);
}
}
private void getBulkResponse(Pending pending, InputStream in) throws IOException {
readLine (in);
byte code = lineBuffer[0];
if(code != ProtocolBase.SIZE_BYTE && code != ProtocolBase.ERR_BYTE) {
throw new ProviderException ("Got [ " + (char)code + " | "+code+" ] when expecing either - or $ pending.status: " + pending.status);
}
pending.isError = (code == ProtocolBase.ERR_BYTE);
byte[] data = new byte[0];
if(!pending.isError){
int length = Convert.toInt(lineBuffer, 1, lineBufferOffset - 3);
if(length > 0) {
data = readBulkData(in, length);
pending.bulkData = data;
}
}
else {
pending.status = new String(lineBuffer, 1, lineBufferOffset - 3);
}
}
private void getMultiBulkResponse(Pending pending, InputStream in) {
List<byte[]> multibulk = null;
throw new RuntimeException("NOT IMPLEMENTED!");
}
// ----------------------------------------------------------------------------------------------
// Response Type Handlers --
// ----------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------
// Response Data Protocol -- should be utility on byte[], Stream, or DataBuffer by now ..
// ----------------------------------------------------------------------------------------------
static final
byte[] CRLF = {(byte)13, (byte)10};
}