/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package com.talent.nio.communicate.send;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.talent.nio.api.Packet;
import com.talent.nio.communicate.ChannelContext;
import com.talent.nio.communicate.monitor.vo.PacketVo;
import com.talent.nio.communicate.monitor.vo.StatVo;
import com.talent.nio.debug.DebugUtils;
import com.talent.nio.utils.SystemTimer;
import com.talent.platform.threadpool.AbstractQueueRunnable;
/**
*
* @author 谭耀武
* @date 2012-08-09
*
*/
public class SendRunnable extends AbstractQueueRunnable<Packet>
{
/**
*
*/
@SuppressWarnings("unused")
private static final long serialVersionUID = 4228217817295225588L;
private static final Logger log = LoggerFactory.getLogger(SendRunnable.class);
/**
* @param args
*/
public static void main(String[] args)
{
}
private ConcurrentLinkedQueue<PacketVo> sendFailQueue = null;
private ChannelContext channelContext = null;
/**
*
* @param socketChannelId
*/
public SendRunnable(ChannelContext channelContext)
{
this.channelContext = channelContext;
super.setRunnableName(SendRunnable.class.getSimpleName() + " [" + channelContext.getId() + "]");
}
/**
* 添加要处理的消息
*
* @param packet
*/
public void addMsg(Packet packet)
{
getMsgQueue().add(packet);
}
/**
* 清空消息队列
*/
public void clearMsgQueue()
{
getMsgQueue().clear();
}
@Override
public String getCurrentProcessor()
{
return this.getClass().getName();
}
/**
*
* @param isForceCreate
* true:有条件地强制创建对象,false:直接返回
* @return
*/
public ConcurrentLinkedQueue<PacketVo> getSendFailQueue(boolean isForceCreate)
{
if (!isForceCreate)
{
return sendFailQueue;
}
if (!this.channelContext.isNeedRecordSendFailMsg())
{
return null;
} else if (sendFailQueue == null)
{
sendFailQueue = new ConcurrentLinkedQueue<PacketVo>();
}
return sendFailQueue;
}
public void recordFailMsg(Packet packet, String failReason)
{
if (channelContext.isNeedRecordSendFailMsg())
{
ConcurrentLinkedQueue<PacketVo> sendFailQueue = getSendFailQueue(true);
if (sendFailQueue != null)
{
PacketVo packetPojo = PacketVo.createPacketVo(packet, SystemTimer.currentTimeMillis(), failReason);
if (sendFailQueue.size() >= channelContext.getCountOfRecordSendFail())
{
sendFailQueue.poll();
}
sendFailQueue.add(packetPojo);
}
}
}
/**
*
*/
@Override
public void run()
{
Packet packet = null;
try
{
while ((packet = getMsgQueue().poll()) != null)
{
sendPacket(packet);
}
} catch (IOException e)
{
recordFailMsg(packet, e.getMessage());
channelContext.getWriteIOErrorHandler().handle(channelContext.getSocketChannel(), e, channelContext,
"IOException occured when writing");
}
}
private int send(SocketChannel socketChannel, ByteBuffer dataBuffer) throws IOException
{
int sendSize = socketChannel.write(dataBuffer);
if (sendSize > 0)
{
channelContext.getStatVo().setCurrentSendTime(SystemTimer.currentTimeMillis());
channelContext.getStatVo().setSentBytes(sendSize + channelContext.getStatVo().getSentBytes());
StatVo.getAllSentBytes().addAndGet(sendSize);
getProcessedMsgByteCount().addAndGet(sendSize);
}
return sendSize;
}
/**
*
* @param data
* 要发送的数据
* @param socketChannel
* 发往的通道
* @return 数据发送成功:true;失败:false
* @throws IOException
*/
private boolean sendData(byte[] data, SocketChannel socketChannel) throws IOException
{
try
{
ByteBuffer dataBuffer = ByteBuffer.wrap(data);
long sendSize = send(socketChannel, dataBuffer);
long allSendSize = sendSize;
long starttime = SystemTimer.currentTimeMillis();
while (dataBuffer.hasRemaining() && socketChannel.isOpen()) // 还有数据没写干净,一般是网络不好导致,也可能是发送和接收量太大。
{
sendSize = send(socketChannel, dataBuffer);
if (sendSize == 0)
{
Thread.sleep(5);
} else
{
allSendSize += sendSize;
}
if (DebugUtils.isNeedDebug(channelContext))
{
try
{
long endtime = SystemTimer.currentTimeMillis();
long costtime = (endtime - starttime);
double speed = -1;
if (costtime > 0)
{
speed = allSendSize / costtime;
}
log.error("{} bytes was sent, {}/{}({}%), cost time: {} ms, {}bytes/ms,{}", sendSize, allSendSize, data.length,
(allSendSize * 100) / data.length, costtime, speed, channelContext);
} catch (Exception e)
{
log.error(e.getLocalizedMessage(), e);
}
}
}
StatVo.getAllSentMsgCount().incrementAndGet();
this.getProcessedMsgCount().incrementAndGet();
long endtime = SystemTimer.currentTimeMillis();
if (DebugUtils.isNeedDebug(channelContext))
{
try
{
long costtime = (endtime - starttime);
double speed = -1;
if (costtime > 0)
{
speed = allSendSize / costtime;
}
log.error("cost time: {} ms, {}bytes/ms, {}", costtime, speed, channelContext);
log.error("ok sent to " + channelContext + ",total num[" + StatVo.getAllSentMsgCount().get() + "],num to this ["
+ getProcessedMsgCount().get() + "],waiting for send to this [" + getMsgQueue().size() + "]");
} catch (Exception e)
{
log.error(e.getLocalizedMessage(), e);
}
}
return true;
} catch (InterruptedException e)
{
return false;
}
}
public void sendPacket(Packet packet) throws IOException
{
if (packet == null)
{
log.error("Packet is null,please check synchronize");
return;
}
if (channelContext.getId() == null)
{
log.error("target socketChannelId is null!");
return;
}
byte[] bytes = null;
try
{
bytes = channelContext.getPacketHandler().onSend(packet, channelContext);
} catch (Exception e)
{
log.error(e.getMessage(), e);
return;
}
if (log.isDebugEnabled())
{
log.debug("send:{}{}", Arrays.toString(bytes), packet);
}
if (bytes != null)
{
sendData(bytes, channelContext.getSocketChannel());
} else
{
log.error("bytes is null");
}
}
public void setRouteInfo(ChannelContext channelContext)
{
this.channelContext = channelContext;
}
public void setSendFailQueue(ConcurrentLinkedQueue<PacketVo> sendFailQueue)
{
this.sendFailQueue = sendFailQueue;
}
}