You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by li...@apache.org on 2022/03/10 03:27:44 UTC

[rocketmq-client-cpp] branch main updated: Use gRPC naming scheme for name server address (#408)

This is an automated email from the ASF dual-hosted git repository.

lizhanhui pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/rocketmq-client-cpp.git


The following commit(s) were added to refs/heads/main by this push:
     new 571c7b7  Use gRPC naming scheme for name server address (#408)
571c7b7 is described below

commit 571c7b74e4984920dc071fce70bfd06aeca5a707
Author: Zhanhui Li <li...@gmail.com>
AuthorDate: Thu Mar 10 11:27:40 2022 +0800

    Use gRPC naming scheme for name server address (#408)
    
    * Use gRPC naming scheme for name server address
    
    * Pin bazel version to LTS 4.2.0
---
 .bazelversion                                      |  1 +
 example/rocketmq/ExampleProducer.cpp               |  2 +-
 example/rocketmq/ExamplePushConsumer.cpp           |  3 +-
 src/main/cpp/client/ClientManagerImpl.cpp          |  3 +-
 src/main/cpp/rocketmq/ClientImpl.cpp               | 32 ++++++---
 .../cpp/rocketmq/DynamicNameServerResolver.cpp     | 21 +-----
 src/main/cpp/rocketmq/NamingScheme.cpp             | 81 ++++++++++++++++++++++
 src/main/cpp/rocketmq/StaticNameServerResolver.cpp | 27 +++-----
 .../rocketmq/include/DynamicNameServerResolver.h   |  9 ++-
 src/main/cpp/rocketmq/include/NameServerResolver.h |  6 +-
 .../{NameServerResolver.h => NamingScheme.h}       | 21 +++---
 .../rocketmq/include/StaticNameServerResolver.h    | 26 +++----
 .../mocks/include/NameServerResolverMock.h         |  6 +-
 src/test/cpp/ut/rocketmq/BUILD.bazel               | 11 +++
 .../ut/rocketmq/DynamicNameServerResolverTest.cpp  | 15 ++--
 .../cpp/ut/rocketmq/NamingSchemeTest.cpp}          | 39 +++++------
 .../ut/rocketmq/StaticNameServerResolverTest.cpp   | 16 ++---
 17 files changed, 194 insertions(+), 125 deletions(-)

diff --git a/.bazelversion b/.bazelversion
new file mode 100644
index 0000000..6aba2b2
--- /dev/null
+++ b/.bazelversion
@@ -0,0 +1 @@
+4.2.0
diff --git a/example/rocketmq/ExampleProducer.cpp b/example/rocketmq/ExampleProducer.cpp
index 2fda81f..3e1cfc5 100644
--- a/example/rocketmq/ExampleProducer.cpp
+++ b/example/rocketmq/ExampleProducer.cpp
@@ -51,7 +51,7 @@ int main(int argc, char* argv[]) {
   DefaultMQProducer producer("TestGroup");
 
   const char* topic = "cpp_sdk_standard";
-  const char* name_server = "121.43.42.193:80";
+  const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80";
 
   producer.setNamesrvAddr(name_server);
   producer.compressBodyThreshold(256);
diff --git a/example/rocketmq/ExamplePushConsumer.cpp b/example/rocketmq/ExamplePushConsumer.cpp
index 8a2e9a6..d45e52a 100644
--- a/example/rocketmq/ExamplePushConsumer.cpp
+++ b/example/rocketmq/ExamplePushConsumer.cpp
@@ -49,11 +49,12 @@ int main(int argc, char* argv[]) {
   const char* group_id = "GID_cpp_sdk_standard";
   const char* topic = "cpp_sdk_standard";
   const char* resource_namespace = "MQ_INST_1080056302921134_BXuIbML7";
+  const char* name_server = "mq-inst-1080056302921134-bxuibml7.mq.cn-hangzhou.aliyuncs.com:80";
 
   DefaultMQPushConsumer push_consumer(group_id);
   push_consumer.setResourceNamespace(resource_namespace);
   push_consumer.setCredentialsProvider(std::make_shared<ConfigFileCredentialsProvider>());
-  push_consumer.setNamesrvAddr("121.43.42.193:80");
+  push_consumer.setNamesrvAddr(name_server);
   MessageListener* listener = new SampleMQMessageListener;
   push_consumer.setInstanceName("instance_0");
   push_consumer.subscribe(topic, "*");
diff --git a/src/main/cpp/client/ClientManagerImpl.cpp b/src/main/cpp/client/ClientManagerImpl.cpp
index db77dc8..d8a43e5 100644
--- a/src/main/cpp/client/ClientManagerImpl.cpp
+++ b/src/main/cpp/client/ClientManagerImpl.cpp
@@ -590,7 +590,8 @@ void ClientManagerImpl::addClientObserver(std::weak_ptr<Client> client) {
 void ClientManagerImpl::resolveRoute(const std::string& target_host, const Metadata& metadata,
                                      const QueryRouteRequest& request, std::chrono::milliseconds timeout,
                                      const std::function<void(const std::error_code&, const TopicRouteDataPtr&)>& cb) {
-
+  SPDLOG_DEBUG("Name server connection URL: {}", target_host);
+  SPDLOG_DEBUG("Query route request: {}", request.DebugString());
   RpcClientSharedPtr client = getRpcClient(target_host, false);
   if (!client) {
     SPDLOG_WARN("Failed to create RPC client for name server[host={}]", target_host);
diff --git a/src/main/cpp/rocketmq/ClientImpl.cpp b/src/main/cpp/rocketmq/ClientImpl.cpp
index 0d22e95..9602969 100644
--- a/src/main/cpp/rocketmq/ClientImpl.cpp
+++ b/src/main/cpp/rocketmq/ClientImpl.cpp
@@ -38,6 +38,7 @@
 #include "InvocationContext.h"
 #include "LoggerImpl.h"
 #include "MessageAccessor.h"
+#include "NamingScheme.h"
 #include "Signature.h"
 #include "rocketmq/MQMessageExt.h"
 #include "rocketmq/MessageListener.h"
@@ -160,7 +161,24 @@ void ClientImpl::getRouteFor(const std::string& topic,
 void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) {
   std::vector<std::pair<std::string, std::uint16_t>> pairs;
   {
-    std::vector<std::string> name_server_list = name_server_resolver_->resolve();
+    std::string naming_address = name_server_resolver_->resolve();
+    absl::string_view host_port_csv;
+
+    if (absl::StartsWith(naming_address, NamingScheme::DnsPrefix)) {
+      endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME);
+      host_port_csv = absl::StripPrefix(naming_address, NamingScheme::DnsPrefix);
+    } else if (absl::StartsWith(naming_address, NamingScheme::IPv4Prefix)) {
+      endpoints->set_scheme(rmq::AddressScheme::IPv4);
+      host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv4Prefix);
+    } else if (absl::StartsWith(naming_address, NamingScheme::IPv6Prefix)) {
+      endpoints->set_scheme(rmq::AddressScheme::IPv6);
+      host_port_csv = absl::StripPrefix(naming_address, NamingScheme::IPv6Prefix);
+    } else {
+      SPDLOG_WARN("Unsupported naming scheme");
+    }
+
+    std::vector<std::string> name_server_list = absl::StrSplit(host_port_csv, ',');
+
     for (const auto& name_server_item : name_server_list) {
       std::string::size_type pos = name_server_item.rfind(':');
       if (std::string::npos == pos) {
@@ -179,20 +197,12 @@ void ClientImpl::setAccessPoint(rmq::Endpoints* endpoints) {
       address->set_host(host_port.first);
       endpoints->mutable_addresses()->AddAllocated(address);
     }
-
-    if (MixAll::isIPv4(pairs.begin()->first)) {
-      endpoints->set_scheme(rmq::AddressScheme::IPv4);
-    } else if (absl::StrContains(pairs.begin()->first, ':')) {
-      endpoints->set_scheme(rmq::AddressScheme::IPv6);
-    } else {
-      endpoints->set_scheme(rmq::AddressScheme::DOMAIN_NAME);
-    }
   }
 }
 
 void ClientImpl::fetchRouteFor(const std::string& topic,
                                const std::function<void(const std::error_code&, const TopicRouteDataPtr&)>& cb) {
-  std::string name_server = name_server_resolver_->current();
+  std::string name_server = name_server_resolver_->resolve();
   if (name_server.empty()) {
     SPDLOG_WARN("No name server available");
     return;
@@ -201,7 +211,7 @@ void ClientImpl::fetchRouteFor(const std::string& topic,
   auto callback = [this, topic, name_server, cb](const std::error_code& ec, const TopicRouteDataPtr& route) {
     if (ec) {
       SPDLOG_WARN("Failed to resolve route for topic={} from {}", topic, name_server);
-      std::string name_server_changed = name_server_resolver_->next();
+      std::string name_server_changed = name_server_resolver_->resolve();
       if (!name_server_changed.empty()) {
         SPDLOG_INFO("Change current name server from {} to {}", name_server, name_server_changed);
       }
diff --git a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp
index b1a9614..f9981bc 100644
--- a/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp
+++ b/src/main/cpp/rocketmq/DynamicNameServerResolver.cpp
@@ -23,8 +23,8 @@
 #include <memory>
 
 #include "absl/strings/str_join.h"
-#include "spdlog/spdlog.h"
 
+#include "LoggerImpl.h"
 #include "SchedulerImpl.h"
 
 ROCKETMQ_NAMESPACE_BEGIN
@@ -69,7 +69,7 @@ DynamicNameServerResolver::DynamicNameServerResolver(absl::string_view endpoint,
                                                      std::string(remains.data(), remains.length()));
 }
 
-std::vector<std::string> DynamicNameServerResolver::resolve() {
+std::string DynamicNameServerResolver::resolve() {
   bool fetch_immediately = false;
   {
     absl::MutexLock lk(&name_server_list_mtx_);
@@ -84,7 +84,7 @@ std::vector<std::string> DynamicNameServerResolver::resolve() {
 
   {
     absl::MutexLock lk(&name_server_list_mtx_);
-    return name_server_list_;
+    return naming_scheme_.buildAddress(name_server_list_);
   }
 }
 
@@ -126,19 +126,4 @@ void DynamicNameServerResolver::shutdown() {
   scheduler_->shutdown();
 }
 
-std::string DynamicNameServerResolver::current() {
-  absl::MutexLock lk(&name_server_list_mtx_);
-  if (name_server_list_.empty()) {
-    return std::string();
-  }
-
-  std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size();
-  return name_server_list_[index];
-}
-
-std::string DynamicNameServerResolver::next() {
-  index_.fetch_add(1, std::memory_order_relaxed);
-  return current();
-}
-
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/NamingScheme.cpp b/src/main/cpp/rocketmq/NamingScheme.cpp
new file mode 100644
index 0000000..4646af9
--- /dev/null
+++ b/src/main/cpp/rocketmq/NamingScheme.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+#include "NamingScheme.h"
+
+#include <cstdint>
+
+#include "absl/container/flat_hash_map.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_join.h"
+#include "absl/strings/str_split.h"
+
+ROCKETMQ_NAMESPACE_BEGIN
+
+const char* NamingScheme::DnsPrefix = "dns:";
+const char* NamingScheme::IPv4Prefix = "ipv4:";
+const char* NamingScheme::IPv6Prefix = "ipv6:";
+
+const char* NamingScheme::IPv4Regex =
+    "(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
+
+const char* NamingScheme::IPv6Regex = "((([0-9a-fA-F]){1,4})\\:){7}([0-9a-fA-F]){1,4}";
+
+NamingScheme::NamingScheme() : ipv4_pattern_(IPv4Regex), ipv6_pattern_(IPv6Regex) {
+}
+
+std::string NamingScheme::buildAddress(const std::vector<std::string>& list) {
+  absl::flat_hash_map<std::string, std::uint32_t> ipv4;
+  absl::flat_hash_map<std::string, std::uint32_t> ipv6;
+
+  for (const auto& segment : list) {
+    std::vector<std::string> host_port = absl::StrSplit(segment, ':');
+    if (2 != host_port.size()) {
+      continue;
+    }
+
+    if (re2::RE2::FullMatch(host_port[0], ipv4_pattern_)) {
+      std::uint32_t port;
+      if (absl::SimpleAtoi(host_port[1], &port)) {
+        ipv4.insert_or_assign(host_port[0], port);
+      }
+      continue;
+    }
+
+    if (re2::RE2::FullMatch(host_port[0], ipv6_pattern_)) {
+      std::uint32_t port;
+      if (absl::SimpleAtoi(host_port[1], &port)) {
+        ipv6.insert_or_assign(host_port[0], port);
+      }
+      continue;
+    }
+
+    // Once we find a domain name record, use it as the final result.
+    host_port.insert(host_port.begin(), "dns");
+    return absl::StrJoin(host_port, ":");
+  }
+
+  if (!ipv4.empty()) {
+    return "ipv4:" + absl::StrJoin(ipv4, ",", absl::PairFormatter(":"));
+  }
+
+  if (!ipv6.empty()) {
+    return "ipv6:" + absl::StrJoin(ipv4, ",", absl::PairFormatter(":"));
+  }
+  return std::string();
+}
+
+ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp
index 85e865f..c7f0804 100644
--- a/src/main/cpp/rocketmq/StaticNameServerResolver.cpp
+++ b/src/main/cpp/rocketmq/StaticNameServerResolver.cpp
@@ -17,27 +17,22 @@
 #include "StaticNameServerResolver.h"
 
 #include "absl/strings/str_split.h"
-#include <atomic>
-#include <cstdint>
 
-ROCKETMQ_NAMESPACE_BEGIN
-
-StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list)
-    : name_server_list_(absl::StrSplit(name_server_list, ';')) {
-}
+#include "LoggerImpl.h"
 
-std::string StaticNameServerResolver::current() {
-  std::uint32_t index = index_.load(std::memory_order_relaxed) % name_server_list_.size();
-  return name_server_list_[index];
-}
+ROCKETMQ_NAMESPACE_BEGIN
 
-std::string StaticNameServerResolver::next() {
-  index_.fetch_add(1, std::memory_order_relaxed);
-  return current();
+StaticNameServerResolver::StaticNameServerResolver(absl::string_view name_server_list) {
+  std::vector<std::string> segments = absl::StrSplit(name_server_list, ';');
+  name_server_address_ = naming_scheme_.buildAddress(segments);
+  if (name_server_address_.empty()) {
+    SPDLOG_WARN("Failed to create gRPC naming scheme compliant address from {}",
+                std::string(name_server_list.data(), name_server_list.length()));
+  }
 }
 
-std::vector<std::string> StaticNameServerResolver::resolve() {
-  return name_server_list_;
+std::string StaticNameServerResolver::resolve() {
+  return name_server_address_;
 }
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h
index bac08bf..5bc2c53 100644
--- a/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h
+++ b/src/main/cpp/rocketmq/include/DynamicNameServerResolver.h
@@ -30,6 +30,7 @@
 #include "absl/synchronization/mutex.h"
 
 #include "NameServerResolver.h"
+#include "NamingScheme.h"
 #include "Scheduler.h"
 #include "TopAddressing.h"
 
@@ -44,11 +45,7 @@ public:
 
   void shutdown() override;
 
-  std::string current() override LOCKS_EXCLUDED(name_server_list_mtx_);
-
-  std::string next() override LOCKS_EXCLUDED(name_server_list_mtx_);
-
-  std::vector<std::string> resolve() override LOCKS_EXCLUDED(name_server_list_mtx_);
+  std::string resolve() override LOCKS_EXCLUDED(name_server_list_mtx_);
 
   void injectHttpClient(std::unique_ptr<HttpClient> http_client);
 
@@ -69,6 +66,8 @@ private:
 
   bool ssl_{false};
   std::unique_ptr<TopAddressing> top_addressing_;
+
+  NamingScheme naming_scheme_;
 };
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NameServerResolver.h
index 443f360..00ec0d9 100644
--- a/src/main/cpp/rocketmq/include/NameServerResolver.h
+++ b/src/main/cpp/rocketmq/include/NameServerResolver.h
@@ -31,11 +31,7 @@ public:
 
   virtual void shutdown() = 0;
 
-  virtual std::string next() = 0;
-
-  virtual std::string current() = 0;
-
-  virtual std::vector<std::string> resolve() = 0;
+  virtual std::string resolve() = 0;
 };
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/include/NameServerResolver.h b/src/main/cpp/rocketmq/include/NamingScheme.h
similarity index 71%
copy from src/main/cpp/rocketmq/include/NameServerResolver.h
copy to src/main/cpp/rocketmq/include/NamingScheme.h
index 443f360..d9b4f62 100644
--- a/src/main/cpp/rocketmq/include/NameServerResolver.h
+++ b/src/main/cpp/rocketmq/include/NamingScheme.h
@@ -19,23 +19,28 @@
 #include <string>
 #include <vector>
 
+#include "re2/re2.h"
+
 #include "rocketmq/RocketMQ.h"
 
 ROCKETMQ_NAMESPACE_BEGIN
 
-class NameServerResolver {
+class NamingScheme {
 public:
-  virtual ~NameServerResolver() = default;
-
-  virtual void start() = 0;
+  NamingScheme();
 
-  virtual void shutdown() = 0;
+  std::string buildAddress(const std::vector<std::string>& list);
 
-  virtual std::string next() = 0;
+  static const char* DnsPrefix;
+  static const char* IPv4Prefix;
+  static const char* IPv6Prefix;
 
-  virtual std::string current() = 0;
+private:
+  static const char* IPv4Regex;
+  static const char* IPv6Regex;
 
-  virtual std::vector<std::string> resolve() = 0;
+  re2::RE2 ipv4_pattern_;
+  re2::RE2 ipv6_pattern_;
 };
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h
index 2648b4a..b38a86e 100644
--- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h
+++ b/src/main/cpp/rocketmq/include/StaticNameServerResolver.h
@@ -16,14 +16,10 @@
  */
 #pragma once
 
-#include <atomic>
-#include <cstdint>
-#include <vector>
-
 #include "absl/strings/string_view.h"
 
 #include "NameServerResolver.h"
-#include "rocketmq/RocketMQ.h"
+#include "NamingScheme.h"
 
 ROCKETMQ_NAMESPACE_BEGIN
 
@@ -37,15 +33,21 @@ public:
   void shutdown() override {
   }
 
-  std::string current() override;
-
-  std::string next() override;
-
-  std::vector<std::string> resolve() override;
+  std::string resolve() override;
 
 private:
-  std::vector<std::string> name_server_list_;
-  std::atomic<std::uint32_t> index_{0};
+  /**
+   * @brief Name server addresses, following gRPC URI schemes described in
+   * https://github.com/grpc/grpc/blob/master/doc/naming.md
+   *
+   * Sample values are:
+   * dns:[//authority/]host[:port]
+   * ipv4:address[:port][,address[:port],...]
+   * ipv6:address[:port][,address[:port],...]
+   */
+  std::string name_server_address_;
+
+  NamingScheme naming_scheme_;
 };
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h
index f572a04..4f8f7f3 100644
--- a/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h
+++ b/src/main/cpp/rocketmq/mocks/include/NameServerResolverMock.h
@@ -27,11 +27,7 @@ public:
 
   MOCK_METHOD(void, shutdown, (), (override));
 
-  MOCK_METHOD(std::string, next, (), (override));
-
-  MOCK_METHOD(std::string, current, (), (override));
-
-  MOCK_METHOD((std::vector<std::string>), resolve, (), (override));
+  MOCK_METHOD(std::string, resolve, (), (override));
 };
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/test/cpp/ut/rocketmq/BUILD.bazel b/src/test/cpp/ut/rocketmq/BUILD.bazel
index 0fcfa1b..205cee1 100644
--- a/src/test/cpp/ut/rocketmq/BUILD.bazel
+++ b/src/test/cpp/ut/rocketmq/BUILD.bazel
@@ -250,4 +250,15 @@ cc_test(
         "//src/main/cpp/rocketmq:rocketmq_library",
         "@com_google_googletest//:gtest_main",
     ],
+)
+
+cc_test(
+    name = "naming_scheme_test",
+    srcs = [
+        "NamingSchemeTest.cpp",
+    ],
+    deps = [
+        "//src/main/cpp/rocketmq:rocketmq_library",
+        "@com_google_googletest//:gtest_main",
+    ],
 )
\ No newline at end of file
diff --git a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp
index f296f93..a693fd6 100644
--- a/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp
+++ b/src/test/cpp/ut/rocketmq/DynamicNameServerResolverTest.cpp
@@ -19,12 +19,14 @@
 #include <chrono>
 #include <map>
 #include <memory>
+#include <string>
 
 #include "absl/memory/memory.h"
 #include "absl/strings/str_join.h"
+#include "absl/strings/str_replace.h"
+#include "absl/strings/str_split.h"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
-#include <string>
 
 #include "HttpClientMock.h"
 
@@ -66,15 +68,8 @@ protected:
 
 TEST_F(DynamicNameServerResolverTest, testResolve) {
   auto name_server_list = resolver_->resolve();
-  ASSERT_FALSE(name_server_list.empty());
-  std::string resolved = absl::StrJoin(name_server_list, ";");
-  ASSERT_EQ(name_server_list_, resolved);
-
-  std::string first{"10.0.0.0:9876"};
-  EXPECT_EQ(first, resolver_->current());
-
-  std::string second{"10.0.0.1:9876"};
-  EXPECT_EQ(second, resolver_->next());
+  std::string result = absl::StrReplaceAll(name_server_list_, {std::pair<std::string, std::string>(";", ",")});
+  ASSERT_EQ(name_server_list, "ipv4:" + result);
 }
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp
similarity index 57%
copy from src/main/cpp/rocketmq/include/StaticNameServerResolver.h
copy to src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp
index 2648b4a..336f702 100644
--- a/src/main/cpp/rocketmq/include/StaticNameServerResolver.h
+++ b/src/test/cpp/ut/rocketmq/NamingSchemeTest.cpp
@@ -14,38 +14,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
 
-#include <atomic>
-#include <cstdint>
-#include <vector>
-
-#include "absl/strings/string_view.h"
-
-#include "NameServerResolver.h"
+#include "NamingScheme.h"
 #include "rocketmq/RocketMQ.h"
 
+#include "gtest/gtest.h"
+
 ROCKETMQ_NAMESPACE_BEGIN
 
-class StaticNameServerResolver : public NameServerResolver {
+class NamingSchemeTest : public testing::Test {
 public:
-  explicit StaticNameServerResolver(absl::string_view name_server_list);
-
-  void start() override {
+  void SetUp() override {
   }
 
-  void shutdown() override {
+  void TearDown() override {
   }
 
-  std::string current() override;
+protected:
+  NamingScheme naming_scheme_;
+};
 
-  std::string next() override;
+TEST_F(NamingSchemeTest, testBuildAddress) {
+  std::string address = "www.baidu.com:80";
+  std::string result = naming_scheme_.buildAddress({address});
+  ASSERT_EQ("dns:www.baidu.com:80", result);
 
-  std::vector<std::string> resolve() override;
+  address = "8.8.8.8:1234";
+  result = naming_scheme_.buildAddress({address});
+  ASSERT_EQ("ipv4:8.8.8.8:1234", result);
 
-private:
-  std::vector<std::string> name_server_list_;
-  std::atomic<std::uint32_t> index_{0};
-};
+  result = naming_scheme_.buildAddress({address, "4.4.4.4:1234"});
+  ASSERT_EQ("ipv4:8.8.8.8:1234,4.4.4.4:1234", result);
+}
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file
diff --git a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp
index cf16502..0bca7e4 100644
--- a/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp
+++ b/src/test/cpp/ut/rocketmq/StaticNameServerResolverTest.cpp
@@ -16,6 +16,7 @@
  */
 #include "StaticNameServerResolver.h"
 
+#include "absl/strings/str_replace.h"
 #include "absl/strings/str_split.h"
 
 #include "gtest/gtest.h"
@@ -42,18 +43,9 @@ protected:
 };
 
 TEST_F(StaticNameServerResolverTest, testResolve) {
-  std::vector<std::string> segments = absl::StrSplit(name_server_list_, ';');
-  ASSERT_EQ(segments, resolver_.resolve());
-}
-
-TEST_F(StaticNameServerResolverTest, testCurrentNext) {
-  std::string&& name_server_1 = resolver_.current();
-  std::string expected = "10.0.0.1:9876";
-  EXPECT_EQ(expected, name_server_1);
-
-  expected = "10.0.0.2:9876";
-  std::string&& name_server_2 = resolver_.next();
-  EXPECT_EQ(expected, name_server_2);
+  std::string result =
+      "ipv4:" + absl::StrReplaceAll(name_server_list_, {std::pair<std::string, std::string>(";", ",")});
+  ASSERT_EQ(result, resolver_.resolve());
 }
 
 ROCKETMQ_NAMESPACE_END
\ No newline at end of file