From a23df96790ee81147abfb38fe1c58cbbf8941828 Mon Sep 17 00:00:00 2001 From: huangli Date: Fri, 28 May 2021 10:22:19 +0800 Subject: [PATCH] [ISSUE 2883] Improve performance of string2messageProperties/messageProperties2String, and save 1 byte for each message. --- .../common/message/MessageDecoder.java | 41 ++++++- .../common/message/MessageDecoderTest.java | 108 ++++++++++++++++++ .../rocketmq/store/BatchPutMessageTest.java | 18 +-- 3 files changed, 144 insertions(+), 23 deletions(-) diff --git a/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java b/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java index 2936c18e..c94700e5 100644 --- a/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java +++ b/common/src/main/java/org/apache/rocketmq/common/message/MessageDecoder.java @@ -409,7 +409,23 @@ public class MessageDecoder { } public static String messageProperties2String(Map properties) { - StringBuilder sb = new StringBuilder(); + if (properties == null) { + return ""; + } + int len = 0; + for (final Map.Entry entry : properties.entrySet()) { + final String name = entry.getKey(); + final String value = entry.getValue(); + if (value == null) { + continue; + } + if (name != null) { + len += name.length(); + } + len += value.length(); + len += 2; // separator + } + StringBuilder sb = new StringBuilder(len); if (properties != null) { for (final Map.Entry entry : properties.entrySet()) { final String name = entry.getKey(); @@ -423,6 +439,9 @@ public class MessageDecoder { sb.append(value); sb.append(PROPERTY_SEPARATOR); } + if (sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + } } return sb.toString(); } @@ -430,12 +449,22 @@ public class MessageDecoder { public static Map string2messageProperties(final String properties) { Map map = new HashMap(); if (properties != null) { - String[] items = properties.split(String.valueOf(PROPERTY_SEPARATOR)); - for (String i : items) { - String[] nv = i.split(String.valueOf(NAME_VALUE_SEPARATOR)); - if (2 == nv.length) { - map.put(nv[0], nv[1]); + int len = properties.length(); + int index = 0; + while (index < len) { + int newIndex = properties.indexOf(PROPERTY_SEPARATOR, index); + if (newIndex < 0) { + newIndex = len; + } + if (newIndex - index >= 3) { + int kvSepIndex = properties.indexOf(NAME_VALUE_SEPARATOR, index); + if (kvSepIndex > index && kvSepIndex < newIndex - 1) { + String k = properties.substring(index, kvSepIndex); + String v = properties.substring(kvSepIndex + 1, newIndex); + map.put(k, v); + } } + index = newIndex + 1; } } diff --git a/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java b/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java index fde523db..b27f2466 100644 --- a/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java +++ b/common/src/test/java/org/apache/rocketmq/common/message/MessageDecoderTest.java @@ -25,6 +25,8 @@ import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.util.Map; +import static org.apache.rocketmq.common.message.MessageDecoder.NAME_VALUE_SEPARATOR; +import static org.apache.rocketmq.common.message.MessageDecoder.PROPERTY_SEPARATOR; import static org.apache.rocketmq.common.message.MessageDecoder.createMessageId; import static org.assertj.core.api.Assertions.assertThat; @@ -265,4 +267,110 @@ public class MessageDecoderTest { } } + @Test + public void testString2messageProperties() { + StringBuilder sb = new StringBuilder(); + sb.append("k1").append(NAME_VALUE_SEPARATOR).append("v1"); + Map m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("k1")).isEqualTo("v1"); + + m = MessageDecoder.string2messageProperties(""); + assertThat(m).size().isEqualTo(0); + + m = MessageDecoder.string2messageProperties(" "); + assertThat(m).size().isEqualTo(0); + + m = MessageDecoder.string2messageProperties("aaa"); + assertThat(m).size().isEqualTo(0); + + sb.setLength(0); + sb.append("k1").append(NAME_VALUE_SEPARATOR); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(0); + + sb.setLength(0); + sb.append(NAME_VALUE_SEPARATOR).append("v1"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(0); + + sb.setLength(0); + sb.append("k1").append(NAME_VALUE_SEPARATOR).append("v1").append(PROPERTY_SEPARATOR); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("k1")).isEqualTo("v1"); + + sb.setLength(0); + sb.append("k1").append(NAME_VALUE_SEPARATOR).append("v1").append(PROPERTY_SEPARATOR) + .append("k2").append(NAME_VALUE_SEPARATOR).append("v2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(2); + assertThat(m.get("k1")).isEqualTo("v1"); + assertThat(m.get("k2")).isEqualTo("v2"); + + sb.setLength(0); + sb.append("k1").append(NAME_VALUE_SEPARATOR).append("v1").append(PROPERTY_SEPARATOR) + .append(NAME_VALUE_SEPARATOR).append("v2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("k1")).isEqualTo("v1"); + + sb.setLength(0); + sb.append("k1").append(NAME_VALUE_SEPARATOR).append("v1").append(PROPERTY_SEPARATOR) + .append("k2").append(NAME_VALUE_SEPARATOR); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("k1")).isEqualTo("v1"); + + sb.setLength(0); + sb.append(NAME_VALUE_SEPARATOR).append("v1").append(PROPERTY_SEPARATOR) + .append("k2").append(NAME_VALUE_SEPARATOR).append("v2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("k2")).isEqualTo("v2"); + + sb.setLength(0); + sb.append("k1").append(NAME_VALUE_SEPARATOR).append(PROPERTY_SEPARATOR) + .append("k2").append(NAME_VALUE_SEPARATOR).append("v2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("k2")).isEqualTo("v2"); + + sb.setLength(0); + sb.append("1").append(NAME_VALUE_SEPARATOR).append("1").append(PROPERTY_SEPARATOR) + .append("2").append(NAME_VALUE_SEPARATOR).append("2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(2); + assertThat(m.get("1")).isEqualTo("1"); + assertThat(m.get("2")).isEqualTo("2"); + + sb.setLength(0); + sb.append("1").append(NAME_VALUE_SEPARATOR).append(PROPERTY_SEPARATOR) + .append("2").append(NAME_VALUE_SEPARATOR).append("2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("2")).isEqualTo("2"); + + sb.setLength(0); + sb.append(NAME_VALUE_SEPARATOR).append("1").append(PROPERTY_SEPARATOR) + .append("2").append(NAME_VALUE_SEPARATOR).append("2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("2")).isEqualTo("2"); + + sb.setLength(0); + sb.append("1").append(NAME_VALUE_SEPARATOR).append("1").append(PROPERTY_SEPARATOR) + .append("2").append(NAME_VALUE_SEPARATOR); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("1")).isEqualTo("1"); + + sb.setLength(0); + sb.append("1").append(NAME_VALUE_SEPARATOR).append("1").append(PROPERTY_SEPARATOR) + .append(NAME_VALUE_SEPARATOR).append("2"); + m = MessageDecoder.string2messageProperties(sb.toString()); + assertThat(m).size().isEqualTo(1); + assertThat(m.get("1")).isEqualTo("1"); + } + } \ No newline at end of file diff --git a/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java b/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java index b3a7c196..2c1fd25f 100644 --- a/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java +++ b/store/src/test/java/org/apache/rocketmq/store/BatchPutMessageTest.java @@ -23,7 +23,6 @@ import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageExtBatch; -import org.apache.rocketmq.common.sysflag.MessageSysFlag; import org.apache.rocketmq.store.config.FlushDiskType; import org.apache.rocketmq.store.config.MessageStoreConfig; import org.apache.rocketmq.store.stats.BrokerStatsManager; @@ -39,6 +38,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static org.apache.rocketmq.common.message.MessageDecoder.messageProperties2String; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertTrue; @@ -230,22 +230,6 @@ public class BatchPutMessageTest { return msgLen; } - public String messageProperties2String(Map properties) { - StringBuilder sb = new StringBuilder(); - if (properties != null) { - for (final Map.Entry entry : properties.entrySet()) { - final String name = entry.getKey(); - final String value = entry.getValue(); - - sb.append(name); - sb.append(NAME_VALUE_SEPARATOR); - sb.append(value); - sb.append(PROPERTY_SEPARATOR); - } - } - return sb.toString(); - } - private class MyMessageArrivingListener implements MessageArrivingListener { @Override public void arriving(String topic, int queueId, long logicOffset, long tagsCode, long msgStoreTime, -- GitLab