/**
* Tencent is pleased to support the open source community by making MSEC available.
*
* Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the GNU General Public 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
*
* https://opensource.org/licenses/GPL-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.msec.net;
import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBufferOutputStream;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.msec.rpc.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class NettyClient {
private static Logger log = Logger.getLogger(NettyClient.class.getName());
private String host = "127.0.0.1";
private int port = 7963;
private ClientBootstrap bootstrap;
private boolean connected = false;
private Channel connectChannel;
public static final int connectTimeoutInMS = 1000;
public static int requestTimeoutInMS = 500;
public static Map<Long, Callback> sessions = new ConcurrentHashMap<Long, Callback>();
private static ExecutorService bossExecutor = Executors.newCachedThreadPool();
private static ExecutorService workExecutor = Executors.newCachedThreadPool();
private static ChannelFactory channelFactory = new NioClientSocketChannelFactory(bossExecutor,
workExecutor, 1, Runtime.getRuntime().availableProcessors());
public NettyClient(String host, int port){
this.host = host;
this.port = port;
this.bootstrap = new ClientBootstrap(channelFactory);;
this.bootstrap.setPipelineFactory(new NettyClientPipelineFactory(this));
this.bootstrap.setOption("tcpNoDelay", true);
this.bootstrap.setOption("keepAlive", true);
this.bootstrap.setOption("reuseAddress", true);
this.bootstrap.setOption("connectTimeoutMillis", connectTimeoutInMS);
}
public synchronized boolean connect() {
//System.out.println("Connect begin: " + System.currentTimeMillis());
if (connected) return true;
ChannelFuture future = null;
//log.info("Connect begin: " + System.currentTimeMillis());
try {
future = bootstrap.connect(new InetSocketAddress(host, port));
//log.info("Connect called: " + System.currentTimeMillis());
if (future.awaitUninterruptibly(connectTimeoutInMS, TimeUnit.MILLISECONDS)) {
if (future.isSuccess()) {
Channel newChannel = future.getChannel();
try {
//Close old connections
Channel oldChannel = this.connectChannel;
if (oldChannel != null) {
//log.info("close old channel " + oldChannel);
try {
oldChannel.close();
} catch (Throwable t) {
}
}
} finally {
this.connectChannel = newChannel;
}
//log.debug("client is connected to " + this.host + ":" + this.port);
this.connected = true;
} else {
//log.debug("client is not connected to " + this.host + ":" + this.port);
}
} else {
log.error("timeout while connecting to " + this.host + ":" + this.port);
}
} catch (Throwable e) {
log.error("failed to connect to " + host + ":" + port, e);
}
//log.info("Connect end: " + System.currentTimeMillis());
return connected;
}
public ChannelFuture sendRequest(Object request) {
ChannelFuture future = null;
if (connectChannel == null) {
System.out.println("sending request but no connection!");
return null;
}
try {
future = connectChannel.write(request);
//future.addListener(new MsgWriteListener(request));
} catch (Exception ex) {
System.out.println("write channel failed.");
connected = false;
return null;
}
return future;
}
public RpcResponse sendRequestAndWaitResponse(RpcRequest request, int timeoutMillis)
{
if (sendRequest(request) == null) return null;
CallbackFuture callback = new CallbackFuture(request, this);
sessions.put(request.getSeq(), callback);
RpcResponse response = null;
try {
response = callback.getResponse(timeoutMillis);
} catch (Exception ex) {
//TODO: set exception
System.out.println("Exception occurs: " + ex);
response = new RpcResponse();
response.setErrno(-1);
response.setError(ex);
} finally {
sessions.remove(request.getSeq());
}
return response;
}
//��RPC��
public RpcResponse sendRequestAndWaitResponse(CustomPackageCodec packageCodec, RpcRequest request, int timeoutMillis)
{
RpcResponse response = null;
ChannelBufferOutputStream bout = new ChannelBufferOutputStream(ChannelBuffers.dynamicBuffer(1024));
long sequence = request.getSeq();
int ret = 0;
try {
ret = packageCodec.encode(sequence, bout);
} catch (IOException ex) {
response = new RpcResponse();
response.setErrno(-1);
response.setError(ex);
return response;
}
if (ret != 0) {
response = new RpcResponse();
response.setErrno(-1);
response.setError(new Exception("Encode request failed."));
return response;
}
Object[] objects = new Object[3];
objects[0] = bout.buffer();
objects[1] = packageCodec;
objects[2] = null;
if (sendRequest(objects) == null) return null;
CallbackFuture callback = new CallbackFuture(request, this);
sessions.put(request.getSeq(), callback);
try {
response = callback.getResponse(timeoutMillis);
} catch (Exception ex) {
//TODO: set exception
System.out.println("Exception occurs: " + ex);
response = new RpcResponse();
response.setErrno(-1);
response.setError(ex);
} finally {
sessions.remove(request.getSeq());
}
return response;
}
//��RPC��
public RpcResponse sendRequestAndWaitResponse(CustomPackageLengthChecker lengthChecker, byte[] requestData, RpcRequest request, int timeoutMillis)
{
RpcResponse response = null;
ChannelBufferOutputStream bout = new ChannelBufferOutputStream(ChannelBuffers.dynamicBuffer(1024));
Object[] objects = new Object[3];
objects[0] = requestData;
objects[1] = lengthChecker;
objects[2] = new Long(request.getSeq());
if (sendRequest(objects) == null) return null;
CallbackFuture callback = new CallbackFuture(request, this);
sessions.put(request.getSeq(), callback);
try {
response = callback.getResponse(timeoutMillis);
} catch (Exception ex) {
//TODO: set exception
System.out.println("Exception occurs: " + ex);
response = new RpcResponse();
response.setErrno(-1);
response.setError(ex);
} finally {
sessions.remove(request.getSeq());
}
return response;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public boolean isConnected() {
return connected;
}
public void setConnected(boolean connected) {
this.connected = connected;
}
public void close() {
connected = false;
connectChannel.close();
}
public class MsgWriteListener implements ChannelFutureListener {
private RpcRequest request;
public MsgWriteListener(RpcRequest request) {
this.request = request;
}
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
return;
}
log.error("operation complete failed.");
}
}
}