/**
* Dianping.com Inc.
* Copyright (c) 2003-2013 All Rights Reserved.
*/
package com.dianping.pigeon.remoting.netty.invoker;
import java.util.List;
import com.dianping.pigeon.remoting.invoker.client.ClientConfig;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import com.dianping.pigeon.remoting.common.channel.ChannelFactory;
import com.dianping.pigeon.remoting.common.domain.InvocationRequest;
import com.dianping.pigeon.remoting.common.domain.InvocationResponse;
import com.dianping.pigeon.remoting.common.domain.generic.UnifiedRequest;
import com.dianping.pigeon.remoting.common.exception.NetworkException;
import com.dianping.pigeon.remoting.common.pool.ChannelPool;
import com.dianping.pigeon.remoting.common.pool.ChannelPoolException;
import com.dianping.pigeon.remoting.common.pool.DefaultChannelPool;
import com.dianping.pigeon.remoting.common.pool.PoolProperties;
import com.dianping.pigeon.remoting.common.util.Constants;
import com.dianping.pigeon.remoting.invoker.AbstractClient;
import com.dianping.pigeon.remoting.invoker.domain.ConnectInfo;
import com.dianping.pigeon.remoting.invoker.process.ResponseProcessor;
import com.dianping.pigeon.remoting.netty.channel.NettyChannel;
import com.dianping.pigeon.remoting.netty.channel.NettyChannelFactory;
import com.dianping.pigeon.remoting.provider.util.ProviderUtils;
import com.dianping.pigeon.util.NetUtils;
public class NettyClient extends AbstractClient {
private String protocol = Constants.PROTOCOL_DEFAULT;
private ConnectInfo connectInfo;
private String remoteHost;
private int remotePort;
private int timeout;
private ClientBootstrap bootstrap;
private String remoteAddressString;
private ChannelPool<NettyChannel> channelPool;
private PoolProperties poolProperties;
private org.jboss.netty.channel.ChannelFactory channelFactory;
public NettyClient(ClientConfig clientConfig,
org.jboss.netty.channel.ChannelFactory channelFactory,
ConnectInfo connectInfo,
ResponseProcessor responseProcessor) {
super(clientConfig, responseProcessor);
this.channelFactory = channelFactory;
this.connectInfo = connectInfo;
this.remoteHost = connectInfo.getHost();
this.remotePort = connectInfo.getPort();
this.remoteAddressString = NetUtils.toAddress(remoteHost, remotePort);
this.timeout = clientConfig.getConnectTimeout();
poolProperties = new PoolProperties(
clientConfig.getInitialSize(),
clientConfig.getNormalSize(),
clientConfig.getMaxActive(),
clientConfig.getMaxWait(),
clientConfig.getTimeBetweenCheckerMillis());
}
@Override
public void doOpen() {
try {
initBootstrap();
initChannelPool();
logger.info("[open] client opened. remoteAddress: " + remoteAddressString);
} catch (Exception e) {
logger.info("[open] client open failed. remoteAddress: " + remoteAddressString);
}
}
private void initBootstrap() {
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", timeout);
this.bootstrap.setOption("writeBufferHighWaterMark", clientConfig.getHighWaterMark());
this.bootstrap.setOption("writeBufferLowWaterMark", clientConfig.getLowWaterMark());
}
private void initChannelPool() throws ChannelPoolException {
channelPool = new DefaultChannelPool<NettyChannel>(poolProperties, createChannelFactory());
}
public ClientBootstrap getBootstrap() {
return bootstrap;
}
public ChannelFactory createChannelFactory() {
return new NettyChannelFactory(this);
}
@Override
public InvocationResponse doWrite(InvocationRequest request) throws NetworkException {
NettyChannel channel = null;
try {
channel = channelPool.selectChannel();
ChannelFuture future = channel.write0(request);
afterWrite(request, channel);
if (request.getMessageType() == Constants.MESSAGE_TYPE_SERVICE
|| request.getMessageType() == Constants.MESSAGE_TYPE_HEART) {
future.addListener(new MessageWriteListener(request, channel));
}
} catch (Exception e) {
throw new NetworkException("[doRequest] remote call failed:" + request, e);
}
return null;
}
private void afterWrite(InvocationRequest request, NettyChannel channel) {
if (request instanceof UnifiedRequest) {
UnifiedRequest _request = (UnifiedRequest) request;
_request.setClientIp(channel.getLocalAddress().getAddress().getHostAddress());
}
}
@Override
public void doClose() {
try {
channelPool.close();
logger.info("[close] client closed. remoteAddress: " + remoteAddressString);
} catch (Exception e) {
logger.info("[close] client close failed. remoteAddress: " + remoteAddressString);
}
}
@Override
public List<NettyChannel> getChannels() {
return channelPool == null ? null : channelPool.getChannels();
}
@Override
public boolean isActive() {
return super.isActive() && poolActive();
}
private boolean poolActive() {
return channelPool != null && channelPool.isAvaliable();
}
@Override
public String getProtocol() {
return protocol;
}
@Override
public String getHost() {
return remoteHost;
}
@Override
public int getPort() {
return remotePort;
}
public int getTimeout() {
return timeout;
}
@Override
public String getAddress() {
return remoteAddressString;
}
@Override
public ConnectInfo getConnectInfo() {
return connectInfo;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
NettyClient that = (NettyClient) o;
if (remotePort != that.remotePort)
return false;
return !(remoteHost != null ? !remoteHost.equals(that.remoteHost) : that.remoteHost != null);
}
@Override
public int hashCode() {
int result = remoteHost != null ? remoteHost.hashCode() : 0;
result = 31 * result + remotePort;
return result;
}
@Override
public String toString() {
return "NettyClient[" + this.getAddress() + ", closed:" + isClosed() + ", active:" + isActive() + ", pool.Avaliable:" + poolActive() + "]";
}
public class MessageWriteListener implements ChannelFutureListener {
private InvocationRequest request;
private NettyChannel channel;
public MessageWriteListener(InvocationRequest request, NettyChannel channel) {
this.request = request;
this.channel = channel;
}
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
return;
}
InvocationResponse response = ProviderUtils.createFailResponse(request, future.getCause());
processResponse(response);
}
}
}