/**
* Copyright 2014 Comcast Cable Communications Management, LLC
*
* 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 com.comcast.viper.flume2storm.connection.sender;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.comcast.viper.flume2storm.event.F2SEvent;
import com.esotericsoftware.kryonet.Connection;
import com.google.common.base.Preconditions;
/**
* This is a single-thread strategy that load-balances the events to send
* between each client. If a client fail to receive the event, it tries another
* client (for a configurable amount of retries).
*/
public class KryoNetSimpleRealtimeStrategy implements KryoNetEventSenderStrategy, Serializable {
private static final long serialVersionUID = 3735055103606316628L;
private static final Logger LOG = LoggerFactory.getLogger(KryoNetSimpleRealtimeStrategy.class);
private final KryoNetEventSender knEventSender;
private final int maxRetries;
private final int writeBufferSize;
private final int objectBufferSize;
/**
* @param knEventSender
* The KryoNet event sender associated with this strategy
* implementation
* @param maxRetries
* The maximum number of times the event sender will attempt to send
* an event before giving up
* @param writeBufferSize
* KryoNet write buffer size
* @param objectBufferSize
* KryoNet object buffer size
*/
public KryoNetSimpleRealtimeStrategy(KryoNetEventSender knEventSender, final int maxRetries,
final int writeBufferSize, final int objectBufferSize) {
this.knEventSender = knEventSender;
this.maxRetries = maxRetries;
this.writeBufferSize = writeBufferSize;
this.objectBufferSize = objectBufferSize;
}
/**
* @see com.comcast.viper.flume2storm.connection.sender.KryoNetEventSenderStrategy#send(java.util.List)
*/
@Override
public int send(List<F2SEvent> events) {
Preconditions.checkNotNull(events);
int result = 0;
int retries = 0;
F2SEvent eventToSend = null;
Iterator<F2SEvent> it = events.iterator();
while (knEventSender.getStatus().isOn() && (it.hasNext() || eventToSend != null)) {
try {
// Getting the next event to send if necessary
if (eventToSend == null) {
eventToSend = it.next();
if (eventToSend != null) {
// Got a new event from the generator
knEventSender.getStats().incrEventsIn();
retries = 0;
} else {
continue;
}
}
assert eventToSend != null;
// Getting next client
Connection client = knEventSender.getClients().getNext();
if (client == null) {
// No more client
break;
}
// Verifying that we have enough space in the write buffer to
// send the message
int bufSz = client.getTcpWriteBufferSize();
if (bufSz + objectBufferSize >= writeBufferSize) {
LOG.debug("KryoNet write buffer is almost full ({} / {}), not sending message: {}", bufSz, writeBufferSize,
eventToSend);
retries++;
if (retries > maxRetries) {
LOG.info("Failed to send {} after {} retries", eventToSend, maxRetries);
knEventSender.getStats().incrEventsFailed();
eventToSend = null;
}
continue;
}
LOG.trace("Sending: {} to {}", eventToSend, client);
if (client.sendTCP(eventToSend) == 0) {
LOG.debug("Failed to send {} to {}", eventToSend, client);
retries++;
if (retries > maxRetries) {
LOG.info("Failed to send {} after {} retries", eventToSend, maxRetries);
knEventSender.getStats().incrEventsFailed();
eventToSend = null;
}
} else {
// Event sent successfully
knEventSender.getStats().incrEventsOut();
result++;
LOG.trace("Sent {} to {}", eventToSend, client);
eventToSend = null;
}
} catch (final Exception e) {
StringBuilder sb = new StringBuilder("Failed to send batch of events (");
sb.append(result);
sb.append(" sent out of ");
sb.append(events.size());
sb.append("): ");
sb.append(e.getLocalizedMessage());
LOG.error(sb.toString(), e);
}
}
return result;
}
}