-
Notifications
You must be signed in to change notification settings - Fork 15.4k
KAFKA-2431: Easier Testing of SSL #217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,7 +19,9 @@ package kafka.tools | |
|
|
||
| import java.util | ||
|
|
||
| import kafka.tools.SimplePropertiesParser._ | ||
| import org.apache.kafka.common.TopicPartition | ||
| import org.apache.kafka.common.utils.Utils | ||
|
|
||
| import scala.collection.JavaConversions._ | ||
| import java.util.concurrent.atomic.AtomicLong | ||
|
|
@@ -36,6 +38,9 @@ import kafka.consumer.ConsumerTimeoutException | |
| import java.text.SimpleDateFormat | ||
| import java.util.concurrent.atomic.AtomicBoolean | ||
|
|
||
| import scala.collection.immutable | ||
| import scala.util.control.NonFatal | ||
|
|
||
| /** | ||
| * Performance test for the full zookeeper consumer | ||
| */ | ||
|
|
@@ -50,19 +55,17 @@ object ConsumerPerformance { | |
| val totalBytesRead = new AtomicLong(0) | ||
| val consumerTimeout = new AtomicBoolean(false) | ||
|
|
||
| if (!config.hideHeader) { | ||
| if (!config.showDetailedStats) | ||
| println("start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec") | ||
| else | ||
| println("time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec") | ||
| if (!config.hideHeader && (config.showDetailedStats || config.showAllStats)) { | ||
| println(s"Consumer Properties: ${config.props}\n") | ||
| println("time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec") | ||
| } | ||
|
|
||
| var startMs, endMs = 0L | ||
| if(config.useNewConsumer) { | ||
| if (config.useNewConsumer) { | ||
| val consumer = new KafkaConsumer[Array[Byte], Array[Byte]](config.props) | ||
| consumer.subscribe(List(config.topic)) | ||
| startMs = System.currentTimeMillis | ||
| consume(consumer, List(config.topic), config.numMessages, 1000, config, totalMessagesRead, totalBytesRead) | ||
| consume(consumer, List(config.topic), config.numMessages, config.testTimeout, config, totalMessagesRead, totalBytesRead) | ||
| endMs = System.currentTimeMillis | ||
| consumer.close() | ||
| } else { | ||
|
|
@@ -83,14 +86,17 @@ object ConsumerPerformance { | |
| thread.start | ||
| for (thread <- threadList) | ||
| thread.join | ||
| if(consumerTimeout.get()) | ||
| if (consumerTimeout.get()) | ||
| endMs = System.currentTimeMillis - consumerConfig.consumerTimeoutMs | ||
| else | ||
| endMs = System.currentTimeMillis | ||
| consumerConnector.shutdown() | ||
| } | ||
| val elapsedSecs = (endMs - startMs) / 1000.0 | ||
| if (!config.showDetailedStats) { | ||
| if (!config.showDetailedStats || config.showAllStats){ | ||
| if (!config.hideHeader) | ||
| println("start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec") | ||
|
|
||
| val totalMBRead = (totalBytesRead.get * 1.0) / (1024 * 1024) | ||
| println(("%s, %s, %.4f, %.4f, %d, %.4f").format(config.dateFormat.format(startMs), config.dateFormat.format(endMs), | ||
| totalMBRead, totalMBRead / elapsedSecs, totalMessagesRead.get, totalMessagesRead.get / elapsedSecs)) | ||
|
|
@@ -129,17 +135,17 @@ object ConsumerPerformance { | |
|
|
||
| while(messagesRead < count && System.currentTimeMillis() - lastConsumedTime <= timeout) { | ||
| val records = consumer.poll(100) | ||
| if(records.count() > 0) | ||
| if (records.count() > 0) | ||
| lastConsumedTime = System.currentTimeMillis | ||
| for(record <- records) { | ||
| messagesRead += 1 | ||
| if(record.key != null) | ||
| if (record.key != null) | ||
| bytesRead += record.key.size | ||
| if(record.value != null) | ||
| if (record.value != null) | ||
| bytesRead += record.value.size | ||
|
|
||
| if (messagesRead % config.reportingInterval == 0) { | ||
| if (config.showDetailedStats) | ||
| if (config.showDetailedStats || config.showAllStats) | ||
| printProgressMessage(0, bytesRead, lastBytesRead, messagesRead, lastMessagesRead, lastReportTime, System.currentTimeMillis, config.dateFormat) | ||
| lastReportTime = System.currentTimeMillis | ||
| lastMessagesRead = messagesRead | ||
|
|
@@ -201,17 +207,36 @@ object ConsumerPerformance { | |
| .withRequiredArg | ||
| .describedAs("count") | ||
| .ofType(classOf[java.lang.Integer]) | ||
| .defaultsTo(1) | ||
| .defaultsTo(1) | ||
| val propsFileOpt = parser.accepts("consumer.config", "External consumer properties file.") | ||
| .withRequiredArg | ||
| .describedAs("consumer properties") | ||
| .ofType(classOf[java.lang.String]) | ||
| .defaultsTo("") | ||
| val freeformPropsOpt = parser.accepts("consumer-properties", "Freeform consumer properties in the format key=val,key=val,key=val...") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need both consumer-properties and consumer.config? It seems that having one of the two is enough? If we want to keep both, perhaps we should do the same in ProducerPerformance. |
||
| .withRequiredArg | ||
| .describedAs("consumer properties") | ||
| .ofType(classOf[java.lang.String]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noticed that And it uses |
||
| val useNewConsumerOpt = parser.accepts("new-consumer", "Use the new consumer implementation.") | ||
| val testTimeoutOpt = parser.accepts("test-timeout", "ms of inactivity after which the test will time out.") | ||
| .withRequiredArg | ||
| .describedAs("time in ms") | ||
| .ofType(classOf[java.lang.Integer]) | ||
| .defaultsTo(5000) | ||
|
|
||
| val options = parser.parse(args: _*) | ||
|
|
||
| CommandLineUtils.checkRequiredArgs(parser, options, topicOpt, numMessagesOpt) | ||
|
|
||
| val useNewConsumer = options.has(useNewConsumerOpt) | ||
|
|
||
| val props = new Properties | ||
| if(useNewConsumer) { | ||
| val props = if (options.has(propsFileOpt)) Utils.loadProps(options.valueOf(propsFileOpt)) else new Properties | ||
|
|
||
| if (options.has(freeformPropsOpt)) { | ||
| props.putAll(propsFromFreeform(options.valueOf(freeformPropsOpt))) | ||
| } | ||
|
|
||
| if (useNewConsumer) { | ||
| import org.apache.kafka.clients.consumer.ConsumerConfig | ||
| props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, options.valueOf(bootstrapServersOpt)) | ||
| props.put(ConsumerConfig.GROUP_ID_CONFIG, options.valueOf(groupIdOpt)) | ||
|
|
@@ -232,12 +257,18 @@ object ConsumerPerformance { | |
| props.put("num.consumer.fetchers", options.valueOf(numFetchersOpt).toString) | ||
| } | ||
| val numThreads = options.valueOf(numThreadsOpt).intValue | ||
| val testTimeout = options.valueOf(testTimeoutOpt).intValue | ||
| val topic = options.valueOf(topicOpt) | ||
| val numMessages = options.valueOf(numMessagesOpt).longValue | ||
| val reportingInterval = options.valueOf(reportingIntervalOpt).intValue | ||
| val showDetailedStats = options.has(showDetailedStatsOpt) | ||
| val showAllStats = options.has(showAllStatsOpt) | ||
| val dateFormat = new SimpleDateFormat(options.valueOf(dateFormatOpt)) | ||
| val hideHeader = options.has(hideHeaderOpt) | ||
| val reportingInterval = { | ||
| val interval = options.valueOf(reportingIntervalOpt).longValue | ||
| if (interval == -1) numMessages / 10 | ||
| else interval | ||
| } | ||
| } | ||
|
|
||
| class ConsumerPerfThread(threadId: Int, name: String, stream: KafkaStream[Array[Byte], Array[Byte]], | ||
|
|
@@ -260,7 +291,7 @@ object ConsumerPerformance { | |
| bytesRead += messageAndMetadata.message.length | ||
|
|
||
| if (messagesRead % config.reportingInterval == 0) { | ||
| if (config.showDetailedStats) | ||
| if (config.showDetailedStats || config.showAllStats) | ||
| printProgressMessage(threadId, bytesRead, lastBytesRead, messagesRead, lastMessagesRead, lastReportTime, System.currentTimeMillis, config.dateFormat) | ||
| lastReportTime = System.currentTimeMillis | ||
| lastMessagesRead = messagesRead | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,11 +26,11 @@ class PerfConfig(args: Array[String]) { | |
| .withRequiredArg | ||
| .describedAs("count") | ||
| .ofType(classOf[java.lang.Long]) | ||
| val reportingIntervalOpt = parser.accepts("reporting-interval", "Interval at which to print progress info.") | ||
| val reportingIntervalOpt = parser.accepts("reporting-interval", "Interval at which to print progress info. If not supplied this is set dynamically") | ||
| .withRequiredArg | ||
| .describedAs("size") | ||
| .describedAs("number of messages after which we print progress") | ||
| .ofType(classOf[java.lang.Integer]) | ||
| .defaultsTo(5000) | ||
| .defaultsTo(-1)//this default is overridden dynamically based on the supplied batch size | ||
| val dateFormatOpt = parser.accepts("date-format", "The date format to use for formatting the time field. " + | ||
| "See java.text.SimpleDateFormat for options.") | ||
| .withRequiredArg | ||
|
|
@@ -39,6 +39,8 @@ class PerfConfig(args: Array[String]) { | |
| .defaultsTo("yyyy-MM-dd HH:mm:ss:SSS") | ||
| val showDetailedStatsOpt = parser.accepts("show-detailed-stats", "If set, stats are reported for each reporting " + | ||
| "interval as configured by reporting-interval") | ||
| val showAllStatsOpt = parser.accepts("show-all-stats", "Show intermediary stats based on the " + | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know exactly what you mean fella. In fact my first reaction was to just change the behaviour, but it's an external interface so people may have coupled to this behaviour. That's why I went down the additional option route.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I understand. It seems like we don't offer strong compatibility guarantees for these scripts though, see discussion in #242. Do you think that people would actually rely on the fact that there is no additional entry with the overall results at the end?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quite likely not I expect, but we should overhaul these scripts if we're going down that route. I should add that the way we use jopt a little problematic. We end up overriding the system's default values just because jopt leads you down the line of providing a default value. Lots of the values used don't make much sense. For settings that map directly to kafka properties it would be better if we enforced that mapping explicitly.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @ijuma . It seems it's simpler to just always print the final result instead of introducing a new option. This is a change of behavior, but it probably matches most people's expectation. ProducerPerformance is already like that. |
||
| "reporting interval and include the summary stats at the end of the run") | ||
| val hideHeaderOpt = parser.accepts("hide-header", "If set, skips printing the header for the stats ") | ||
| val messageSizeOpt = parser.accepts("message-size", "The size of each message.") | ||
| .withRequiredArg | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, why are we doing the
isEmptycheck here? We don't do the same check when setting the enabledProtocols. It seems to add noise for little benefit given how this is used.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because the enabled protocols have a kafka supplied default value. With the cipher suite I left it up to the JVM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, if you pass an empty collection, the JDK defaults are no longer used? The change makes sense then. We should probably be using the JDK defaults for enabled protocols by default too IMO, but that can be handled via KAFKA-2456
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - I agree. I don't think overriding the JVM defaults is a good idea unless there is a specific, documented intent.