/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 io.jafka.producer;
import io.jafka.api.MultiProducerRequest;
import io.jafka.api.ProducerRequest;
import io.jafka.common.annotations.ThreadSafe;
import io.jafka.message.ByteBufferMessageSet;
import io.jafka.mx.SyncProducerStats;
import io.jafka.network.BlockingChannel;
import io.jafka.network.Request;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import static java.lang.String.format;
/**
* file{producer/SyncProducer.scala}
*
* @author adyliu (imxylz@gmail.com)
* @since 1.0
*/
@ThreadSafe
public class SyncProducer implements Closeable {
private final Logger logger = LoggerFactory.getLogger(SyncProducer.class);
//private static final RequestKeys RequestKey = RequestKeys.Produce;//0
/////////////////////////////////////////////////////////////////////
private final SyncProducerConfig config;
private final BlockingChannel blockingChannel;
private final Object lock = new Object();
private volatile boolean shutdown = false;
private final String host;
private final int port;
public SyncProducer(SyncProducerConfig config) {
super();
this.config = config;
this.host = config.getHost();
this.port = config.getPort();
//
this.blockingChannel = new BlockingChannel(host, port, BlockingChannel.DEFAULT_BUFFER_SIZE, config.bufferSize, config.socketTimeoutMs);
}
public void send(String topic, ByteBufferMessageSet message) {
send(topic, ProducerRequest.RandomPartition, message);
}
public void send(String topic, int partition, ByteBufferMessageSet messages) {
messages.verifyMessageSize(config.maxMessageSize);
send(new ProducerRequest(topic, partition, messages));
}
private void send(Request request) {
synchronized (lock) {
long startTime = System.nanoTime();
int written = -1;
try {
written = connect().send(request);
} catch (IOException e) {
// no way to tell if write succeeded. Disconnect and re-throw exception to let client handle retry
disconnect();
throw new RuntimeException(e);
} finally {
if (logger.isDebugEnabled()) {
logger.debug(format("write %d bytes data to %s:%d", written, host, port));
}
}
final long endTime = System.nanoTime();
SyncProducerStats.recordProduceRequest(endTime - startTime);
}
}
private BlockingChannel connect() {
if (!blockingChannel.isConnected() && !shutdown) {
try {
blockingChannel.connect();
} catch (IOException ioe) {
throw new RuntimeException(ioe.getMessage(), ioe);
} finally {
if (!blockingChannel.isConnected()) {
disconnect();
}
}
}
return blockingChannel;
}
private void disconnect() {
if (blockingChannel.isConnected()) {
logger.info("Disconnecting from " + config.getHost() + ":" + config.getPort());
blockingChannel.disconnect();
}
}
public void close() {
synchronized (lock) {
try {
disconnect();
} finally {
shutdown = true;
}
}
}
public void multiSend(List<ProducerRequest> produces) {
for (ProducerRequest request : produces) {
request.messages.verifyMessageSize(config.maxMessageSize);
}
send(new MultiProducerRequest(produces));
}
}