From e1c81187c00737b1eb491c7f139b0c68e01a6bf9 Mon Sep 17 00:00:00 2001 From: KomachiSion <263976490@qq.com> Date: Tue, 3 Dec 2024 13:52:44 +0800 Subject: [PATCH] Add 3.0 doc header. --- src/components/common/Header/DocsMenu.astro | 9 + src/components/starlight/Head.astro | 2 +- src/content/docs/v3.0/_sidebar.json | 224 ++ src/content/docs/v3.0/en/architecture.md | 135 + src/content/docs/v3.0/en/archive/activity.md | 50 + src/content/docs/v3.0/en/archive/cli-guide.md | 7 + .../docs/v3.0/en/archive/contributing-dev.md | 130 + .../docs/v3.0/en/archive/feature-list.md | 203 ++ src/content/docs/v3.0/en/archive/roadmap.md | 42 + .../docs/v3.0/en/archive/sdk-properties.md | 50 + .../v3.0/en/archive/use-nacos-with-istio.md | 7 + .../en/archive/use-nacos-with-springcloud.md | 19 + .../docs/v3.0/en/community/community.md | 30 + .../docs/v3.0/en/community/nacos-dev.md | 101 + src/content/docs/v3.0/en/concepts.md | 101 + .../v3.0/en/contribution/contributing-flow.md | 107 + .../docs/v3.0/en/contribution/contributing.md | 102 + .../en/contribution/how-to-reporting-bugs.md | 32 + .../docs/v3.0/en/contribution/pull-request.md | 32 + .../docs/v3.0/en/ecology/img/k8s-sync.jpg | Bin 0 -> 149811 bytes .../docs/v3.0/en/ecology/use-nacos-sync.md | 241 ++ .../v3.0/en/ecology/use-nacos-with-coredns.md | 53 + .../v3.0/en/ecology/use-nacos-with-dubbo.md | 473 +++ .../v3.0/en/ecology/use-nacos-with-istio.md | 176 ++ .../en/ecology/use-nacos-with-k8s-sync.md | 37 + .../en/ecology/use-nacos-with-spring-boot.md | 178 ++ .../en/ecology/use-nacos-with-spring-cloud.md | 201 ++ .../v3.0/en/ecology/use-nacos-with-spring.md | 455 +++ .../guide/admin/cluster-mode-quick-start.md | 165 + .../docs/v3.0/en/guide/admin/console-guide.md | 181 ++ .../docs/v3.0/en/guide/admin/deployment.md | 100 + .../v3.0/en/guide/admin/management-api.md | 11 + .../docs/v3.0/en/guide/admin/monitor-guide.md | 249 ++ .../en/guide/admin/nacos2-config-benchmark.md | 9 + .../en/guide/admin/nacos2-naming-benchmark.md | 9 + .../en/guide/admin/system-configurations.md | 107 + src/content/docs/v3.0/en/guide/user/auth.md | 226 ++ .../docs/v3.0/en/guide/user/failover.md | 147 + src/content/docs/v3.0/en/guide/user/faq.md | 185 ++ .../docs/v3.0/en/guide/user/open-api.md | 2807 +++++++++++++++++ .../docs/v3.0/en/guide/user/other-language.md | 18 + .../v3.0/en/guide/user/parameters-check.md | 10 + src/content/docs/v3.0/en/guide/user/sdk.md | 671 ++++ .../docs/v3.0/en/manual/admin/admin-api.md | 974 ++++++ .../docs/v3.0/en/manual/admin/auth.mdx | 167 + .../docs/v3.0/en/manual/admin/console.md | 177 ++ .../admin/deployment/deployment-cluster.md | 365 +++ .../admin/deployment/deployment-overview.md | 91 + .../deployment/deployment-standalone.mdx | 88 + .../docs/v3.0/en/manual/admin/monitor.md | 170 + .../en/manual/admin/system-configurations.md | 250 ++ .../docs/v3.0/en/manual/admin/upgrading.mdx | 135 + .../docs/v3.0/en/manual/test/nacos-e2e.md | 163 + .../v3.0/en/manual/test/nacos-unit-test.md | 139 + .../docs/v3.0/en/manual/user/addressing.mdx | 240 ++ src/content/docs/v3.0/en/manual/user/auth.mdx | 116 + .../docs/v3.0/en/manual/user/go-sdk/usage.md | 388 +++ .../v3.0/en/manual/user/java-sdk/failover.md | 143 + .../en/manual/user/java-sdk/properties.md | 223 ++ .../v3.0/en/manual/user/java-sdk/usage.md | 1068 +++++++ .../docs/v3.0/en/manual/user/open-api.md | 1919 +++++++++++ .../en/manual/user/overview/other-language.md | 22 + .../v3.0/en/manual/user/parameters-check.md | 235 ++ .../v3.0/en/manual/user/python-sdk/usage.md | 239 ++ src/content/docs/v3.0/en/overview.md | 138 + .../docs/v3.0/en/plugin/address-plugin.md | 96 + .../docs/v3.0/en/plugin/auth-plugin.md | 185 ++ .../v3.0/en/plugin/config-change-plugin.md | 141 + .../en/plugin/config-encryption-plugin.md | 82 + .../en/plugin/custom-environment-plugin.md | 58 + .../docs/v3.0/en/plugin/datasource-plugin.md | 9 + .../docs/v3.0/en/plugin/trace-plugin.md | 201 ++ .../v3.0/en/quickstart/quick-start-docker.mdx | 83 + .../en/quickstart/quick-start-kubernetes.mdx | 81 + .../v3.0/en/quickstart/quick-start-native.mdx | 69 + .../docs/v3.0/en/quickstart/quick-start.mdx | 117 + .../v3.0/en/upgrading/200-compatibility.md | 208 ++ .../docs/v3.0/en/upgrading/200-upgrading.md | 9 + src/content/docs/v3.0/en/what-is-nacos.md | 96 + src/content/docs/v3.0/zh-cn/architecture.md | 130 + .../docs/v3.0/zh-cn/archive/activity.md | 48 + .../docs/v3.0/zh-cn/archive/cli-guide.md | 7 + .../v3.0/zh-cn/archive/contributing-dev.md | 127 + .../docs/v3.0/zh-cn/archive/feature-list.md | 203 ++ .../docs/v3.0/zh-cn/archive/roadmap.md | 41 + .../docs/v3.0/zh-cn/archive/sdk-properties.md | 50 + .../zh-cn/archive/use-nacos-with-istio.md | 7 + .../archive/use-nacos-with-springcloud.md | 19 + .../docs/v3.0/zh-cn/community/community.md | 30 + .../docs/v3.0/zh-cn/community/nacos-dev.md | 100 + src/content/docs/v3.0/zh-cn/concepts.md | 78 + .../zh-cn/contribution/contributing-flow.md | 100 + .../v3.0/zh-cn/contribution/contributing.md | 101 + .../contribution/how-to-reporting-bugs.md | 30 + .../v3.0/zh-cn/contribution/pull-request.md | 32 + .../docs/v3.0/zh-cn/ecology/img/k8s-sync.jpg | Bin 0 -> 149811 bytes .../docs/v3.0/zh-cn/ecology/use-nacos-sync.md | 241 ++ .../zh-cn/ecology/use-nacos-with-coredns.md | 54 + .../zh-cn/ecology/use-nacos-with-dubbo.md | 481 +++ .../zh-cn/ecology/use-nacos-with-istio.md | 168 + .../zh-cn/ecology/use-nacos-with-k8s-sync.md | 38 + .../ecology/use-nacos-with-spring-boot.md | 173 + .../ecology/use-nacos-with-spring-cloud.md | 203 ++ .../zh-cn/ecology/use-nacos-with-spring.md | 456 +++ .../guide/admin/cluster-mode-quick-start.md | 176 ++ .../v3.0/zh-cn/guide/admin/console-guide.md | 189 ++ .../docs/v3.0/zh-cn/guide/admin/deployment.md | 99 + .../v3.0/zh-cn/guide/admin/management-api.md | 9 + .../v3.0/zh-cn/guide/admin/monitor-guide.md | 248 ++ .../guide/admin/nacos2-config-benchmark.md | 181 ++ .../guide/admin/nacos2-naming-benchmark.md | 131 + .../guide/admin/system-configurations.md | 107 + .../docs/v3.0/zh-cn/guide/user/auth.md | 236 ++ .../docs/v3.0/zh-cn/guide/user/failover.md | 144 + src/content/docs/v3.0/zh-cn/guide/user/faq.md | 308 ++ .../docs/v3.0/zh-cn/guide/user/open-api.md | 2531 +++++++++++++++ .../v3.0/zh-cn/guide/user/other-language.md | 23 + .../v3.0/zh-cn/guide/user/parameters-check.md | 244 ++ src/content/docs/v3.0/zh-cn/guide/user/sdk.md | 659 ++++ .../docs/v3.0/zh-cn/manual/admin/admin-api.md | 974 ++++++ .../docs/v3.0/zh-cn/manual/admin/auth.mdx | 167 + .../docs/v3.0/zh-cn/manual/admin/console.md | 177 ++ .../admin/deployment/deployment-cluster.md | 365 +++ .../admin/deployment/deployment-overview.md | 91 + .../deployment/deployment-standalone.mdx | 88 + .../docs/v3.0/zh-cn/manual/admin/monitor.md | 170 + .../manual/admin/system-configurations.md | 250 ++ .../v3.0/zh-cn/manual/admin/upgrading.mdx | 135 + .../docs/v3.0/zh-cn/manual/test/nacos-e2e.md | 163 + .../v3.0/zh-cn/manual/test/nacos-unit-test.md | 139 + .../v3.0/zh-cn/manual/user/addressing.mdx | 240 ++ .../docs/v3.0/zh-cn/manual/user/auth.mdx | 116 + .../v3.0/zh-cn/manual/user/go-sdk/usage.md | 388 +++ .../zh-cn/manual/user/java-sdk/failover.md | 143 + .../zh-cn/manual/user/java-sdk/properties.md | 223 ++ .../v3.0/zh-cn/manual/user/java-sdk/usage.md | 1068 +++++++ .../docs/v3.0/zh-cn/manual/user/open-api.md | 1919 +++++++++++ .../manual/user/overview/other-language.md | 21 + .../zh-cn/manual/user/parameters-check.md | 235 ++ .../zh-cn/manual/user/python-sdk/usage.md | 239 ++ src/content/docs/v3.0/zh-cn/overview.md | 138 + .../docs/v3.0/zh-cn/plugin/address-plugin.md | 97 + .../docs/v3.0/zh-cn/plugin/auth-plugin.md | 182 ++ .../v3.0/zh-cn/plugin/config-change-plugin.md | 138 + .../zh-cn/plugin/config-encryption-plugin.md | 78 + .../docs/v3.0/zh-cn/plugin/control-plugin.md | 222 ++ .../zh-cn/plugin/custom-environment-plugin.md | 58 + .../v3.0/zh-cn/plugin/datasource-plugin.md | 65 + .../docs/v3.0/zh-cn/plugin/trace-plugin.md | 199 ++ .../zh-cn/quickstart/quick-start-docker.mdx | 83 + .../quickstart/quick-start-kubernetes.mdx | 81 + .../zh-cn/quickstart/quick-start-native.mdx | 69 + .../v3.0/zh-cn/quickstart/quick-start.mdx | 117 + .../v3.0/zh-cn/upgrading/200-compatibility.md | 206 ++ .../v3.0/zh-cn/upgrading/200-upgrading.md | 550 ++++ src/content/docs/v3.0/zh-cn/what-is-nacos.md | 97 + 156 files changed, 34869 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/v3.0/_sidebar.json create mode 100644 src/content/docs/v3.0/en/architecture.md create mode 100644 src/content/docs/v3.0/en/archive/activity.md create mode 100644 src/content/docs/v3.0/en/archive/cli-guide.md create mode 100644 src/content/docs/v3.0/en/archive/contributing-dev.md create mode 100644 src/content/docs/v3.0/en/archive/feature-list.md create mode 100644 src/content/docs/v3.0/en/archive/roadmap.md create mode 100644 src/content/docs/v3.0/en/archive/sdk-properties.md create mode 100644 src/content/docs/v3.0/en/archive/use-nacos-with-istio.md create mode 100644 src/content/docs/v3.0/en/archive/use-nacos-with-springcloud.md create mode 100644 src/content/docs/v3.0/en/community/community.md create mode 100644 src/content/docs/v3.0/en/community/nacos-dev.md create mode 100644 src/content/docs/v3.0/en/concepts.md create mode 100644 src/content/docs/v3.0/en/contribution/contributing-flow.md create mode 100644 src/content/docs/v3.0/en/contribution/contributing.md create mode 100644 src/content/docs/v3.0/en/contribution/how-to-reporting-bugs.md create mode 100644 src/content/docs/v3.0/en/contribution/pull-request.md create mode 100644 src/content/docs/v3.0/en/ecology/img/k8s-sync.jpg create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-sync.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-coredns.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-dubbo.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-istio.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-k8s-sync.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-spring-boot.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-spring-cloud.md create mode 100644 src/content/docs/v3.0/en/ecology/use-nacos-with-spring.md create mode 100644 src/content/docs/v3.0/en/guide/admin/cluster-mode-quick-start.md create mode 100644 src/content/docs/v3.0/en/guide/admin/console-guide.md create mode 100644 src/content/docs/v3.0/en/guide/admin/deployment.md create mode 100644 src/content/docs/v3.0/en/guide/admin/management-api.md create mode 100644 src/content/docs/v3.0/en/guide/admin/monitor-guide.md create mode 100644 src/content/docs/v3.0/en/guide/admin/nacos2-config-benchmark.md create mode 100644 src/content/docs/v3.0/en/guide/admin/nacos2-naming-benchmark.md create mode 100644 src/content/docs/v3.0/en/guide/admin/system-configurations.md create mode 100644 src/content/docs/v3.0/en/guide/user/auth.md create mode 100644 src/content/docs/v3.0/en/guide/user/failover.md create mode 100644 src/content/docs/v3.0/en/guide/user/faq.md create mode 100644 src/content/docs/v3.0/en/guide/user/open-api.md create mode 100644 src/content/docs/v3.0/en/guide/user/other-language.md create mode 100644 src/content/docs/v3.0/en/guide/user/parameters-check.md create mode 100644 src/content/docs/v3.0/en/guide/user/sdk.md create mode 100644 src/content/docs/v3.0/en/manual/admin/admin-api.md create mode 100644 src/content/docs/v3.0/en/manual/admin/auth.mdx create mode 100644 src/content/docs/v3.0/en/manual/admin/console.md create mode 100644 src/content/docs/v3.0/en/manual/admin/deployment/deployment-cluster.md create mode 100644 src/content/docs/v3.0/en/manual/admin/deployment/deployment-overview.md create mode 100644 src/content/docs/v3.0/en/manual/admin/deployment/deployment-standalone.mdx create mode 100644 src/content/docs/v3.0/en/manual/admin/monitor.md create mode 100644 src/content/docs/v3.0/en/manual/admin/system-configurations.md create mode 100644 src/content/docs/v3.0/en/manual/admin/upgrading.mdx create mode 100644 src/content/docs/v3.0/en/manual/test/nacos-e2e.md create mode 100644 src/content/docs/v3.0/en/manual/test/nacos-unit-test.md create mode 100644 src/content/docs/v3.0/en/manual/user/addressing.mdx create mode 100644 src/content/docs/v3.0/en/manual/user/auth.mdx create mode 100644 src/content/docs/v3.0/en/manual/user/go-sdk/usage.md create mode 100644 src/content/docs/v3.0/en/manual/user/java-sdk/failover.md create mode 100644 src/content/docs/v3.0/en/manual/user/java-sdk/properties.md create mode 100644 src/content/docs/v3.0/en/manual/user/java-sdk/usage.md create mode 100644 src/content/docs/v3.0/en/manual/user/open-api.md create mode 100644 src/content/docs/v3.0/en/manual/user/overview/other-language.md create mode 100644 src/content/docs/v3.0/en/manual/user/parameters-check.md create mode 100644 src/content/docs/v3.0/en/manual/user/python-sdk/usage.md create mode 100644 src/content/docs/v3.0/en/overview.md create mode 100644 src/content/docs/v3.0/en/plugin/address-plugin.md create mode 100644 src/content/docs/v3.0/en/plugin/auth-plugin.md create mode 100644 src/content/docs/v3.0/en/plugin/config-change-plugin.md create mode 100644 src/content/docs/v3.0/en/plugin/config-encryption-plugin.md create mode 100644 src/content/docs/v3.0/en/plugin/custom-environment-plugin.md create mode 100644 src/content/docs/v3.0/en/plugin/datasource-plugin.md create mode 100644 src/content/docs/v3.0/en/plugin/trace-plugin.md create mode 100644 src/content/docs/v3.0/en/quickstart/quick-start-docker.mdx create mode 100644 src/content/docs/v3.0/en/quickstart/quick-start-kubernetes.mdx create mode 100644 src/content/docs/v3.0/en/quickstart/quick-start-native.mdx create mode 100644 src/content/docs/v3.0/en/quickstart/quick-start.mdx create mode 100644 src/content/docs/v3.0/en/upgrading/200-compatibility.md create mode 100644 src/content/docs/v3.0/en/upgrading/200-upgrading.md create mode 100644 src/content/docs/v3.0/en/what-is-nacos.md create mode 100644 src/content/docs/v3.0/zh-cn/architecture.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/activity.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/cli-guide.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/contributing-dev.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/feature-list.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/roadmap.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/sdk-properties.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/use-nacos-with-istio.md create mode 100644 src/content/docs/v3.0/zh-cn/archive/use-nacos-with-springcloud.md create mode 100644 src/content/docs/v3.0/zh-cn/community/community.md create mode 100644 src/content/docs/v3.0/zh-cn/community/nacos-dev.md create mode 100644 src/content/docs/v3.0/zh-cn/concepts.md create mode 100644 src/content/docs/v3.0/zh-cn/contribution/contributing-flow.md create mode 100644 src/content/docs/v3.0/zh-cn/contribution/contributing.md create mode 100644 src/content/docs/v3.0/zh-cn/contribution/how-to-reporting-bugs.md create mode 100644 src/content/docs/v3.0/zh-cn/contribution/pull-request.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/img/k8s-sync.jpg create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-sync.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-coredns.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-dubbo.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-istio.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-k8s-sync.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-boot.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-cloud.md create mode 100644 src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/cluster-mode-quick-start.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/console-guide.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/deployment.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/management-api.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/monitor-guide.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/nacos2-config-benchmark.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/nacos2-naming-benchmark.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/admin/system-configurations.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/auth.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/failover.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/faq.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/open-api.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/other-language.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/parameters-check.md create mode 100644 src/content/docs/v3.0/zh-cn/guide/user/sdk.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/admin-api.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/auth.mdx create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/console.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-cluster.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-overview.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-standalone.mdx create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/monitor.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/system-configurations.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/admin/upgrading.mdx create mode 100644 src/content/docs/v3.0/zh-cn/manual/test/nacos-e2e.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/test/nacos-unit-test.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/addressing.mdx create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/auth.mdx create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/go-sdk/usage.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/java-sdk/failover.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/java-sdk/properties.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/java-sdk/usage.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/open-api.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/overview/other-language.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/parameters-check.md create mode 100644 src/content/docs/v3.0/zh-cn/manual/user/python-sdk/usage.md create mode 100644 src/content/docs/v3.0/zh-cn/overview.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/address-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/auth-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/config-change-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/config-encryption-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/control-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/custom-environment-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/datasource-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/plugin/trace-plugin.md create mode 100644 src/content/docs/v3.0/zh-cn/quickstart/quick-start-docker.mdx create mode 100644 src/content/docs/v3.0/zh-cn/quickstart/quick-start-kubernetes.mdx create mode 100644 src/content/docs/v3.0/zh-cn/quickstart/quick-start-native.mdx create mode 100644 src/content/docs/v3.0/zh-cn/quickstart/quick-start.mdx create mode 100644 src/content/docs/v3.0/zh-cn/upgrading/200-compatibility.md create mode 100644 src/content/docs/v3.0/zh-cn/upgrading/200-upgrading.md create mode 100644 src/content/docs/v3.0/zh-cn/what-is-nacos.md diff --git a/src/components/common/Header/DocsMenu.astro b/src/components/common/Header/DocsMenu.astro index 0eb38f6c91a..03e77371f7e 100644 --- a/src/components/common/Header/DocsMenu.astro +++ b/src/components/common/Header/DocsMenu.astro @@ -27,6 +27,15 @@ export const docsItems = [ link: "/en/docs/next/overview/", } }, + { + label: "3.0", + target: "_self", + link: "/docs/v3.0/what-is-nacos/", + translations: { + en: "3.0", + link: "/en/docs/v3.0/what-is-nacos/", + } + }, { label: "2.3", target: "_self", diff --git a/src/components/starlight/Head.astro b/src/components/starlight/Head.astro index e84cc0363cd..ec52b2c4c7e 100644 --- a/src/components/starlight/Head.astro +++ b/src/components/starlight/Head.astro @@ -18,7 +18,7 @@ let canonical = Astro.site if (canonical && Astro.url.pathname.includes("/docs/")) { // 对于多版本文档来说,canonical需要改为latest的链接 - const versionList = ["v1", "v2", "next", "v2.3"]; + const versionList = ["v1", "v2", "next", "v2.3", "v3.0"]; const pathname = Astro.url.pathname; try { const version = pathname.split("/docs/")[1].split("/")[0]; diff --git a/src/content/docs/v3.0/_sidebar.json b/src/content/docs/v3.0/_sidebar.json new file mode 100644 index 00000000000..05be37067ef --- /dev/null +++ b/src/content/docs/v3.0/_sidebar.json @@ -0,0 +1,224 @@ +[ + { + "label": "概览", + "link": "docs/overview/", + "translations": { + "en": "Overview" + } + }, + { + "label": "快速开始", + "translations": { + "en": "Quick Start" + }, + "collapsed": false, + "items": [ + { + "label": "Nacos", + "link": "docs/quickstart/quick-start/", + "translations": { + "en": "Nacos" + } + }, + { + "label": "Nacos Docker", + "link": "docs/quickstart/quick-start-docker/", + "translations": { + "en": "Nacos Docker" + } + }, + { + "label": "Nacos Kubernetes", + "link": "docs/quickstart/quick-start-kubernetes/", + "translations": { + "en": "Nacos Kubernetes" + } + } + ] + }, + { + "label": "用户手册", + "translations": { + "en": "User Manual" + }, + "items": [ + { + "label": "概览", + "autogenerate": { "directory": "manual/user/overview" } + }, + { + "label": "JAVA SDK", + "autogenerate": { "directory": "manual/user/java-sdk" } + }, + { + "label": "Golang SDK", + "autogenerate": { "directory": "manual/user/go-sdk" } + }, + { + "label": "Python SDK", + "autogenerate": { "directory": "manual/user/python-sdk" } + }, + { + "label": "Open API 手册", + "translations": { + "en": "Open API Manual" + }, + "link": "docs/manual/user/open-api" + }, + { + "label": "配置鉴权", + "translations": { + "en": "Authorization" + }, + "link": "docs/manual/user/auth" + }, + { + "label": "参数校验", + "translations": { + "en": "Parameters Check" + }, + "link": "docs/manual/user/parameters-check" + }, + { + "label": "寻址说明", + "translations": { + "en": "Addressing" + }, + "link": "docs/manual/user/addressing" + } + ] + }, + { + "label": "运维手册", + "translations": { + "en": "Admin Manual" + }, + "items": [ + { + "label": "部署手册", + "translations": { + "en": "Deploy Manual" + }, + "autogenerate": { "directory": "manual/admin/deployment" } + }, + { + "label": "系统参数", + "translations": { + "en": "Nacos Configuration" + }, + "link": "docs/manual/admin/system-configurations" + }, + { + "label": "运维API", + "translations": { + "en": "Management API" + }, + "link": "docs/manual/admin/admin-api" + }, + { + "label": "鉴权手册", + "translations": { + "en": "Authorization Manual" + }, + "link": "docs/manual/admin/auth" + }, + { + "label": "升级手册", + "translations": { + "en": "Upgrading Manual" + }, + "link": "docs/manual/admin/upgrading" + }, + { + "label": "监控手册", + "translations": { + "en": "Monitor Manual" + }, + "link": "docs/manual/admin/monitor" + } + , + { + "label": "控制台手册", + "translations": { + "en": "Console Manual" + }, + "link": "docs/manual/admin/console" + } + ] + }, + { + "label": "插件", + "translations": { + "en": "Plugin" + }, + "autogenerate": { "directory": "plugin" } + }, + { + "label": "测试手册", + "translations": { + "en": "Test Manual" + }, + "autogenerate": { "directory": "manual/test" } + }, + { + "label": "生态融合", + "translations": { + "en": "Ecology" + }, + "autogenerate": { "directory": "ecology" } + }, + { + "label": "开源共建", + "translations": { + "en": "Contributor Guide" + }, + "autogenerate": { "directory": "contribution" } + }, + { + "label": "社区", + "translations": { + "en": "Community" + }, + "autogenerate": { "directory": "community" } + }, + { + "label": "将归档", + "translations": { + "en": "Archiving" + }, + "collapsed": true, + "items": [ + { + "label": "什么是 Nacos", + "translations": { + "en": "What is Nacos" + }, + "link": "docs/what-is-nacos" + }, + { + "label": "升级", + "translations": { + "en": "Upgrading" + }, + "collapsed": true, + "autogenerate": { "directory": "upgrading" } + }, + { + "label": "用户指南", + "translations": { + "en": "User Guide" + }, + "collapsed": true, + "autogenerate": { "directory": "guide/user" } + }, + { + "label": "运维指南", + "translations": { + "en": "Admin Guide" + }, + "collapsed": true, + "autogenerate": { "directory": "guide/admin" } + } + ] + } +] \ No newline at end of file diff --git a/src/content/docs/v3.0/en/architecture.md b/src/content/docs/v3.0/en/architecture.md new file mode 100644 index 00000000000..94e59de122a --- /dev/null +++ b/src/content/docs/v3.0/en/architecture.md @@ -0,0 +1,135 @@ +--- +title: Nacos architecture +keywords: [Nacos,architecture] +description: Nacos architecture +--- + +# Nacos architecture + +> Document optimizing... + +## Basic Architecture and Concepts + +![nacos_arch.jpg](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217892717-1418fb9b-7faa-4324-87b9-f1740329f564.jpeg) + +### Service + +A software function or a set of software functions (such as the retrieval of specified information or the execution of a set of operations) with the purpose that different clients can be reused for different purposes (for example, through a cross-process network call). Nacos supports almost all types of services: +[Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) + +[gRPC](https://grpc.io/docs/guides/concepts.html#service-definition) +[ | Dubbo RPC Service](https://dubbo.apache.org/#/?lang=en-us) + +[Spring Cloud RESTful Service](https://spring.io/projects/spring-cloud) + +### Service Registry + +The database of services, instances and metadata. Service instances are registered with the service registry on startup and deregistered on shutdown. Clients of the service and/or routers query the service registry to find the available instances of a service. A service registry might invoke a service instances health check API to verify that it is able to handle requests. + +### Service Metadata + +Data describing services such as service endpoints, service labels, service version, service instance weights, routing rules, security policies. + +### Service Provider + +A process or application which provides reusable and callable services. + +### Service Consumer + +A process or application which initiates a call to a service. + +### Configuration + +During system development, developers usually extract some parameters or variables that need to be changed from the code and manage them in a separate configuration file. This enables the static system artifacts or deliverables (such as WAR and JAR packages) to fit with the physical operating environment in a better way. Configuration management is generally a part of system deployment, which is executed by the administrator or operation and maintenance personnel. Configuration modification is an effective method to adjust the behavior of a running system. + +### Configuration Management + +In the data center, all configuration-related activities such as editing, storage, distribution, change management, history version management, and change audit are collectively referred to as configuration management. + +### Naming Service + +Mapping the "names" of all the objects and entities in the distributed system to the associated metadata, for example, ```ServiceName``` -> ```Endpoints\Version etc...```, ```Distributed Lock Name``` -> ```Lock Owner/Status Info```, ```DNS Domain Name``` -> ```IP List```. Service discovery and DNS are the two major scenarios of naming service. + +### Configuration Service + +Providing dynamic configuration, service metadata and configuration management for other services or application. + +### [More concepts...](./concepts.md) + +## Introduction to logical architecture and its components + +![nacos-logic.jpg](https://cdn.nlark.com/yuque/0/2019/png/338441/1561217775318-6e408805-18bb-4242-b4e9-83c5b929b469.png) + +- Service Management: Implement services CRUD, domain name CRUD, service health check, service weight management, etc. +- Configuration Management: Implement configuration CRUD, version management, grayscale management, monitoring management, push trajectory, aggregate data, etc. +- Metadata Management: Provides metadata CURD and marking capabilities +- Plug-in mechanism: implements three modules to share the ability to implement the extended point SPI mechanism +- Event mechanism: implement asynchronous event notification, sdk data change asynchronous notification and other logic +- Log module: Manage log classification, log level, log portability (especially to avoid conflicts), log format, exception code + help documentation +- Callback mechanism: sdk informs the data and calls back user processing through a unified mode. Interface and data structures need to be scalable +- Addressing mode: solve various addressing modes such as ip, domain name, nameserver, broadcast, etc., need to be expandable +- Push channel: solve the push performance problem between server and storage, server, server and sdk +- Capacity management: manage each tenant, the capacity under the group, prevent the storage from being blasted, affecting service availability +- Traffic management: control the request frequency, the number of long links, the size of the message, and request flow control according to multiple dimensions such as tenant and group. +- Caching mechanism: disaster recovery directory, local cache, server cache mechanism. Disaster recovery catalogue requires tools +- Startup mode: Start different programs + UI according to stand-alone mode, configuration mode, service mode, dns mode, or all mode +- Consistency Protocol: Resolve different data, different consistency requirements, different consistency mechanisms +- Storage module: solve data persistence, non-persistent storage, solve data fragmentation problem +- Nameserver: Resolve the routing problem from namespace to clusterid, solve the mapping problem between user environment and nacos physical environment +- CMDB: Solve the metadata storage, docking problems with the three-party cmdb system, solving applications, people, resource relationships +- Metrics: Exposes standard metrics data for easy access to three-way monitoring systems +- Trace: Exposure standard trace, easy to open with SLA system, log whitening, push trajectory, etc., and can be connected with metering and billing system +- Access management: equivalent to Ali cloud service, assign identity, capacity, authority process +- User Management: Resolve issues such as user management, login, sso, etc. +- Rights Management: Resolve issues such as identity, access control, role management, etc. +- Audit system: extended interface facilitates access to different company audit systems +- Notification system: Core data changes, or operations, facilitated through the SMS system, notify the corresponding person data changes +- OpenAPI: exposes the standard Rest style HTTP interface, easy to use, and easy for multi-language integration +- Console: easy to use console, do service management, configuration management, etc. +- SDK: Multilingual sdk +- Agent: dns-f similar mode, or integration with mesh and other programs +- CLI: Lightweight management of the product on the command line, as easy as git + +## Domain Model + +### Data Model + +The Nacos data model Key is uniquely determined by the triplet. The Namespace defaults to an empty string, the public namespace (public), and the group defaults to DEFAULT_GROUP. + +![nacos_data_model](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217857314-95ab332c-acfb-40b2-957a-aae26c2b5d71.jpeg) + +### Service Entity Relationship Model + +![nacos_naming_data_model](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217924697-ba504a35-129f-4fc6-b0df-1130b995375a.jpeg) + +### Config Entity Relationship Model + +Around the configuration, there are mainly two associated entities, one is the configuration change history, and the other is the service tag (used for marking classification, convenient for indexing), which is associated by ID. + +![nacos_config_er](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217958896-4465757f-f588-4797-9c90-a76e604fabb4.jpeg) + + +## Class view + +### Nacos-SDK Class view + +// TODO Service part to be continued + +![nacos_sdk_class_relation](https://cdn.nlark.com/yuque/0/2022/png/25574784/1650771676187-d95a9e45-8656-4d1a-8b5b-ed63a23a816b.png) + + +## Artifacts, Deployment, and Start Mode + +![undefined](https://cdn.yuque.com/lark/0/2018/png/15914/1531730742844-e8325932-258b-49b2-9473-8d1199efe20d.png) + +### Two Artifacts + +Nacos supports both standard Docker images (v0.2.0) and nacos-.zip(tar.gz). You can choose the appropriate build to deploy the Nacos service according to your needs. + +### Two Start Modes + +Nacos supports two start modes. you can merging the Service Registry and the Config Center in one process or deploying them in separately cluster. + +### Free Public Cloud Service on Alibaba Cloud + +In addition to deploying and launching Nacos services by users themselves, Nacos also supports public cloud. Nacos public cloud service will be free in Alibaba Cloud's commercial service (such as [MSE](https://cn.aliyun.com/product/aliware/mse?spm=nacos-website.topbar.0.0.0), [EDAS](https://www.aliyun.com/product/edas)). We also welcome other public cloud providers to offer Nacos public cloud services. diff --git a/src/content/docs/v3.0/en/archive/activity.md b/src/content/docs/v3.0/en/archive/activity.md new file mode 100644 index 00000000000..bb002239641 --- /dev/null +++ b/src/content/docs/v3.0/en/archive/activity.md @@ -0,0 +1,50 @@ +--- +title: Nacos activity +keywords: [Nacos,activity] +description: Nacos award activity +--- + +# Nacos activity + +## I. Activity task list + +* Read the official website copy, find the official website bug, and make constructive suggestions for the official website of Chinese and English. +* Read Chinese and English documents, find document bugs, and give suggestions for improving Chinese and English documents (especially pay attention to places where English translation is not good, because English is our own programmers) +* Try the code -> compile and package -> start Nacos server -> stop Nacos server process, propose improvements +* Try to configure and start multi-node Nacos cluster mode tasks, suggestions for improvement +* Try to use the Nacos Java SDK to give suggestions for improvements to the Java SDK +* Try to improve the OpenAPI with the Nacos Open API +* Try to contribute to the contributor process by trying to contribute to the contribution process based on How to Contribute to Nacos Documentation +* Ask Nacos for needs, development plans, ideas and requirements, etc. + +## II. Activity participation method + +* Scan "超哥" WeChat 2 microcode, let "超哥"" help join "Nacos community exchange group" + + + +![WeChat QR code | left](https://cdn.yuque.com/lark/0/2018/png/11189/1532004866850-5e03b901-6d76-4380-b7bf-66e227808bdc.png "") + + +* Select one or more experience tasks in (I) +* After discovering the problem or bug, follow the "Report Report Method" in (III), send a corresponding github issue, and assign it to @github account [xuechaos] ([https://github.com/xuechaos](https://github.com/xuechaos)) + +## III. Problem Report Mode + +* Submit questions in the nacos repository at github according to the issues template. +* Code warehouse problem, submit pull request repair, after review, merge into the trunk, it becomes a Contributor. +* [https://github.com/alibaba/nacos](https://github.com/alibaba/nacos) +* [https://github.com/nacos-group/nacos-group.github.io](https://github.com/nacos-group/nacos-group.github.io) + +## IV. Mission Rewards + +* We are customizing some small gifts with Nacos Logo for the small partners who have participated and made outstanding contributions, and will consider the courier to the small partners who have made outstanding contributions in the process. +* The gift is light, but I hope I can express my gratitude for your help. +* Due to limited funds, we can only provide 40 gifts at present, so we limit the number of participants to 40 people, first come first served. +* If you wish, the partner who wishes to receive the gift can send a photo with the gift (can hide the face) for us as a photo material for our community activities. + +## V. Other instructions + +* We are not sure that every proposal will be adopted in the end, but we will try to explain what kind of consideration we are based on, and we did not recommend your proposal. +* Try to report problems by mailing list or report issue instead of reporting on WeChat group to document and facilitate our communication process. + diff --git a/src/content/docs/v3.0/en/archive/cli-guide.md b/src/content/docs/v3.0/en/archive/cli-guide.md new file mode 100644 index 00000000000..b9609816ef5 --- /dev/null +++ b/src/content/docs/v3.0/en/archive/cli-guide.md @@ -0,0 +1,7 @@ +--- +title: CLI Guide +keywords: [CLI,Guide] +description: In plan with Nacos 1.x.x +--- + +**IN PLAN** with Nacos 1.x.x diff --git a/src/content/docs/v3.0/en/archive/contributing-dev.md b/src/content/docs/v3.0/en/archive/contributing-dev.md new file mode 100644 index 00000000000..57c031c34c6 --- /dev/null +++ b/src/content/docs/v3.0/en/archive/contributing-dev.md @@ -0,0 +1,130 @@ +--- +title: Contributing to Nacos +keywords: [Nacos,contribute] +description: Welcome to Nacos! This document is a guideline about how to contribute to Nacos. +--- + +# Contributing to Nacos + +Welcome to Nacos! This document is a guideline about how to contribute to Nacos. + +If you find something incorrect or missing, please leave comments / suggestions. + +## Before you get started + +### Code of Conduct + +Please make sure to read and observe our [Code of Conduct](https://github.com/alibaba/nacos/blob/master/CODE_OF_CONDUCT.md). + +## Contributing + +Nacos welcome new participants of any role, including user, contributor, committer and PMC. + +![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/contributor_definition.png) + + +We encourage new comers actively join in Nacos projects and involving from user role to committer role, and even PMC role. In order to accomplish this, new comers needs to actively contribute in Nacos project. The following paragraph introduce how to contribute in Nacos way. + +#### Open / pickup an issue for preparation + +If you find a typo in document, find a bug in code, or want new features, or want to give suggestions, you can [open an issue on GitHub](https://github.com/alibaba/Nacos/issues/new) to report it. + +If you just want to contribute directly you can choose the issue below. + +- [Contribution Welcome](https://github.com/alibaba/nacos/labels/contribution%20welcome): Heavily needed issue, but currently short of hand. + +- [good first issue](https://github.com/alibaba/nacos/labels/good%20first%20issue): Good for newcomers, new comer can pickup one for warm-up. + + +We strongly value documentation and integration with other projects such as Spring Cloud, Kubernetes, Dubbo, etc. We are very glad to work on any issue for these aspects. + +Please note that any PR must be associated with a valid issue. Otherwise the PR will be rejected. + +#### Begin your contribute + +Now if you want to contribute, please create a new pull request. + +We use the `develop` branch as the development branch, which indicates that this is a unstable branch. + +Further more, our branching model complies to [https://nvie.com/posts/a-successful-git-branching-model/](https://nvie.com/posts/a-successful-git-branching-model/). We strongly suggest new comers walk through the above article before creating PR. + +Now, if you are ready to create PR, here is the workflow for contributors: + +1. Fork to your own + +2. Clone fork to local repository + +3. Create a new branch and work on it + +4. Keep your branch in sync + +5. Commit your changes (make sure your commit message concise) + +6. Push your commits to your forked repository + +7. Create a pull request to **develop** branch. + + +When creating pull request: + +1. Please follow [the pull request template](https://github.com/alibaba/nacos/blob/master/.github/PULL_REQUEST_TEMPLATE.md). + +2. Please create the request to **develop** branch. + +3. Please make sure the PR has a corresponding issue. + +4. If your PR contains large changes, e.g. component refactor or new components, please write detailed documents about its design and usage. + +5. Note that a single PR should not be too large. If heavy changes are required, it's better to separate the changes to a few individual PRs. + +6. After creating a PR, one or more reviewers will be assigned to the pull request. + +7. Before merging a PR, squash any fix review feedback, typo, merged, and rebased sorts of commits. The final commit message should be clear and concise. + + +If your PR contains large changes, e.g. component refactor or new components, please write detailed documents about its design and usage. + +### Code review guidance + +Committers will rotate reviewing the code to make sure all the PR will be reviewed timely and by at least one committer before merge. If we aren't doing our job (sometimes we drop things). And as always, we welcome volunteers for code review. + +Some principles: + +- Readability - Important code should be well-documented. API should have Javadoc. Code style should be complied with the existing one. + +- Elegance: New functions, classes or components should be well designed. + +- Testability - 80% of the new code should be covered by unit test cases. + +- Maintainability - Comply with our [PMD spec](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md), and 3-month-frequency update should be maintained at least. + + +### Now how about try become a committer? + +Generally speaking, contribute 8 non-trivial patches and get at least three different people to review them (you'll need three people to support you). Then ask someone to nominate you. You're demonstrating your: + +- at least 8 PR and the associated issues to the project, + +- ability to collaborate with the team, + +- understanding of the projects' code base and coding style, + +- ability to write good code (last but certainly not least) + + +A current committer nominates you by slacking the team on the Nacos issue with label "nomination" + +- your first and last name + +- a link to your Git profile + +- an explanation of why you should be a committer, + +- Elaborate the top 3 PR and the associated issues the nominator has worked with you that can demonstrate your ability. + + +Two other committer need to second your nomination. If no one objects in 5 working days (China), you're a committer. If anyone objects or wants more information, the committers discuss and usually come to a consensus (within the 5 working days). If issues cannot be resolved, there's a vote among current committers. + +![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/nomination_process.png) + +In the worst case, this can drag out for two weeks. Keep contributing! Even in the rare cases where a nomination fails, the objection is usually something easy to address like "more patches" or "not enough people are familiar with this person's work." diff --git a/src/content/docs/v3.0/en/archive/feature-list.md b/src/content/docs/v3.0/en/archive/feature-list.md new file mode 100644 index 00000000000..da01c218d2b --- /dev/null +++ b/src/content/docs/v3.0/en/archive/feature-list.md @@ -0,0 +1,203 @@ +--- +title: Nacos功能和需求列表 +keywords: [Nacos,功能] +description: Nacos功能和需求列表 +--- + +# Nacos功能和需求列表 + +本文列举了目前Nacos支持的主要功能和一些还未支持的需求排期,方便读者了解目前Nacos已经支持和计划支持的能力,同时所有计划支持的能力都开放给开发者进行认领,本文末有详细的认领教程。 + +在下面的表格中,每个需求都有一个状态的标志,包含若干种取值,各种取值的含义如下: + +1. 状态的取值: + 1. 不支持:该功能还不支持,且没有在现在的时间表里有任何排期; + 1. 排期中:该功能还不支持,但是已经放到了时间表里,有希望在后面的某个版本支持; + 1. 设计中:表示该功能正在方案设计中,方案的草稿和终稿都会开放访问,供大家讨论; + 1. 开发中:表示该功能设计方案已经确定,正在有对应的开发者进行开发,会在接下来的某个版本正式发布; + 1. beta:该功能已经发布,但是未经过大规模的用户验证,还不能确保稳定性; + 1. 稳定:表示已经迭代至少4个版本,目前没有反馈重大缺陷; + + + +# 服务发现 +代码地址:[https://github.com/alibaba/nacos/tree/develop/naming](https://github.com/alibaba/nacos/tree/develop/naming) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 服务注册与发现 | nkorange | 稳定 | 0.1.0 | +| 健康检查(服务端探测、客户端心跳) | xuanyin | 稳定 | 0.1.0 | +| 路由策略(权重、保护阈值、就近访问) | wangjianwei | 稳定 | 0.1.0 | +| | | | | + + + +# 配置管理 +代码地址:[https://github.com/alibaba/nacos/tree/develop/config](https://github.com/alibaba/nacos/tree/develop/config) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 配置管理(发布、修改、查询、监听配置) | yanlinly | 稳定 | 0.1.0 | +| 灰度配置 | yanlinly | 稳定 | 1.1.0 | +| 加密配置 | | 不支持 | | + + + +# 元数据管理 +代码地址:[https://github.com/alibaba/nacos/tree/develop/cmdb](https://github.com/alibaba/nacos/tree/develop/cmdb) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 对接第三方CMDB | nkorange | beta | 0.7.0 | + + + +# 地址服务器 +代码地址:[https://github.com/alibaba/nacos/tree/develop/address](https://github.com/alibaba/nacos/tree/develop/address) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 支持Nacos寻址 | pbting | beta | 1.1.0 | + + + +# Nacos内核 +代码地址:[https://github.com/alibaba/nacos/tree/develop/core](https://github.com/alibaba/nacos/tree/develop/core) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 去除MySQL依赖 | chuntaojun | 设计中 | | +| Raft协议替换成JRaft | chuntaojun | 稳定 | 1.4.1 | +| 异步通知机制统一 | wfnuser | 设计中 | | +| 线程模块统一 | | 排期中 | | +| 传输通道统一 | nkorange | 设计中 | | +| 推送模块统一 | satjd | 设计中 | | +| 启动模块统一 | | 排期中 | | + + + +# 安全与稳定性 +代码地址:[https://github.com/alibaba/nacos](https://github.com/alibaba/nacos) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 命名空间模块下沉为公共模块 | | 排期中 | | +| 权限控制,包括认证与鉴权 | nkorange | 开发中 | 1.2.0 | +| 操作审计与记录 | | 排期中 | | +| 支持加密传输 | | 排期中 | | +| OpenTracing对接 | | 排期中 | | +| metrics收集 | TsingLiang | 稳定 | 0.8.0 | +| 缓存容灾机制统一 | | 排期中 | | +| 支持命令行运维 | | 排期中 | | +| 数据自动备份 | | 排期中 | | +| 限流模块 | | 排期中 | | +| 容量管理 | | 排期中 | | + + + +# 代码质量 +代码地址:[https://github.com/alibaba/nacos](https://github.com/alibaba/nacos) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 工具类模块统一 | | 排期中 | | +| 常量定义统一 | | 排期中 | | +| 异常处理模块统一 | | 排期中 | | +| 日志模块统一 | | 排期中 | | +| 系统参数模块统一 | | 排期中 | | +| 依赖统一 | | 排期中 | | +| 状态码模块统一 | KeRan213539 | 设计中 | | + + + +# 云原生 +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 对接Istio | nkorange | beta | 1.1.4 | +| 对接ConfigMap | | 排期中 | | +| [对接CoreDNS](https://github.com/nacos-group/nacos-coredns-plugin) | JianweiWang | beta | 0.1.0 | +| 对接SPIFFE | | 排期中 | | + + + +# 客户端 +客户端支持包含了目前已知的Nacos多语言客户端及Spring生态的相关客户端,除了Java客户端和Go客户端,其他均为社区热心贡献者开发,如果您有新的语言的客户端,或者有目前已经支持的语言的客户端的另外一个实现,欢迎在github上留言进行登记。 + +| 描述 | 主要开发者 | 状态 | +| :---: | --- | --- | +| [Java客户端](https://github.com/alibaba/nacos/tree/develop/client) | Nacos | 稳定 | +| [Go客户端](https://github.com/nacos-group/nacos-sdk-go) | atlanssia, lzp0412 | 稳定 | +| [Node.js客户端](https://github.com/nacos-group/nacos-sdk-nodejs) | czy88840616, gxcsoccer | 稳定 | +| [Python客户端](https://github.com/nacos-group/nacos-sdk-python) | sanwei | beta | +| [C#客户端](https://github.com/catcherwong/nacos-sdk-csharp) | catcherwong | 推荐 | +| C++客户端 | | | +| PHP客户端 | | | +| [Spring客户端](https://github.com/nacos-group/nacos-spring-project) | chuntaojun | 稳定 | +| [SpringBoot客户端](https://github.com/nacos-group/nacos-spring-boot-project) | chuntaojun | 稳定 | + + + +# Nacos-Docker +代码地址:[https://github.com/nacos-group/nacos-docker](https://github.com/nacos-group/nacos-docker) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| Docker部署Nacos Server | paderlol | 稳定 | 0.1.0 | + + + +# Nacos-K8s +代码地址:[https://github.com/nacos-group/nacos-k8s](https://github.com/nacos-group/nacos-k8s) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| K8s部署Nacos Server | paderlol | 稳定 | 0.1.0 | + + + +# Nacos-Sync +代码地址:[https://github.com/nacos-group/nacos-sync](https://github.com/nacos-group/nacos-sync) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| Nacos与Nacos服务双向同步 | paderlol | 稳定 | 0.1.0 | +| Nacos与Zookeeper服务双向同步 | paderlol | 稳定 | 0.3.0 | +| Nacos与Eureka服务双向同步 | paderlol | 稳定 | 0.3.0 | +| Nacos与Consul服务双向同步 | paderlol | 稳定 | 0.3.0 | + + + +# Nacos官网 +代码地址:[https://github.com/nacos-group/nacos-group.github.io](https://github.com/nacos-group/nacos-group.github.io) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 支持页面内锚链接 | | 不支持 | | + + + + +# 参与共建 + +### 参与共建能得到什么? +参与Nacos共建,你将有机会让你的代码被全中国甚至全世界的用户阅读并使用,同时在成为Nacos Committer后(如何成为Nacos Committer可以参考[手册](https://github.com/alibaba/nacos/blob/develop/CONTRIBUTING.md)),还可以有以下福利: + +- 在Nacos官网[团队页](https://nacos.io/docs/latest/community/nacos-dev/)留名; +- 收到我们带Nacos logo的小礼物,有T恤、水杯、帽衫等等; +- 代表Nacos参加各种线上线下活动,与更多小伙伴交流; +- 更多我们还在筹划中的福利; + + +### 如何共建 +除了在上面列举的功能和需求,其他的在github仓库上打上了[contribution welcome](https://github.com/alibaba/nacos/issues?q=is%3Aopen+is%3Aissue+label%3A%22contribution+welcome%22)或者[help wanted](https://github.com/alibaba/nacos/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)标签的issue,也非常欢迎大家提交代码贡献。加入Nacos 社区核心贡献小组钉钉群23335652,联系群管理员认领需求。 + +大家提PR的时候有几点需要注意下: + +1. 比较重大的特性需要有方案文档:[https://github.com/alibaba/nacos/issues/858](https://github.com/alibaba/nacos/issues/858) +1. 已经阅读并遵守共建规范: [https://github.com/alibaba/nacos/blob/master/CONTRIBUTING.md](https://github.com/alibaba/nacos/blob/master/CONTRIBUTING.md) +1. 使用github账户提交代码,这样大家才会在contributor列表看到自己的名字; +1. commit信息要带上issue id,这样才能在issue里看到PR的进度; +1. 代码中不要出现中文注释,提交前要格式化,并添加必要的集成测试用例和单元测试用例; +1. 提PR前运行mvn -Prelease-nacos clean install -U和mvn clean install -Pit-test成功; + +任务认领原则:每人一次最多领取两个任务,任务PR合并后,即可开始认领下个任务。 diff --git a/src/content/docs/v3.0/en/archive/roadmap.md b/src/content/docs/v3.0/en/archive/roadmap.md new file mode 100644 index 00000000000..708ef8dedea --- /dev/null +++ b/src/content/docs/v3.0/en/archive/roadmap.md @@ -0,0 +1,42 @@ +--- +title: Nacos roadmap +keywords: [Nacos,roadmap] +description: Nacos roadmap +--- + +# Nacos roadmap + +We plan to make Nacos available for production from Nacos 0.8.0. Prior to this release, we recommend that you use it only in development and test environments. +Our current plan is to strive to make Nacos production ready in the 6-8 months. The plan might be adjusted due to various factors, including the priority adjustment according to the voice of the community, but the overall plan is that it should not take longer than one year. + +Below are the main roadmaps and plans for the next year. + +## Nacos 1.0 + +Main goals: + +* Build a simple and easy to use, service related set of tools, including the service discovery, configuration management, service metadata storage, push, consistency and metadata management etc; + +* Seamless integration with and support for open-source ecosystems including [Spring Cloud](https://github.com/alibaba/spring-cloud-alibaba)、[Kubernetes](https://github.com/kubernetes/kubernetes)、[Dubbo](https://github.com/apache/dubbo) etc., while at the same time developing a variety of excellent features for large-scale production. + +The following is a rough version plan: + +* 0.1 Basic Nacos server and simple OpenAPI and Java SDK; +* 0.2 - 0.3 Seamless support for Kubernetes, Service Mesh and Spring Cloud service discovery and configuration management; +* 0.4 - 0.5 Build an easy-to-use Web UI/User Console; +* 0.6 - 0.7 High availability, ease of use, monitoring and alert etc; +* 0.8 Production ready; +* 0.9 Large scale performance tuning and benchmark; +* 1.0 GA for large scale production. + +## Nacos 2.0 + +Mainly focus on the unified service management, service sharing and service management system of the open service platform construction, mainly includes two aspects: + +* Dubbo 4.0 + Nacos 2.0: An Open-Service Platform + +![Screen Shot 2018-07-11 at 22.32.17.png | left](https://cdn.yuque.com/lark/0/2018/png/15914/1531319724777-d19b0304-535c-4af9-bee1-f358b6e55d91.png "") + +* Kubernetes + Spring Cloud: Unified Service Management + +![Screen Shot 2018-07-11 at 22.35.30.png | left](https://cdn.yuque.com/lark/0/2018/png/15914/1531319755930-0040e67e-ca05-47b9-9cd0-07ffd7452eae.png "") \ No newline at end of file diff --git a/src/content/docs/v3.0/en/archive/sdk-properties.md b/src/content/docs/v3.0/en/archive/sdk-properties.md new file mode 100644 index 00000000000..dd03fe72f90 --- /dev/null +++ b/src/content/docs/v3.0/en/archive/sdk-properties.md @@ -0,0 +1,50 @@ +--- +title: Nacos 客户端初始化说明 +keywords: [Nacos,客户端,初始化] +description: Nacos 客户端初始化说明 +--- + +Nacos 客户端初始化说明 + +``` + public final static String ENDPOINT = "endpoint"; + public final static String NAMESPACE = "namespace"; + public final static String ACCESS_KEY = "accessKey"; + public final static String SECRET_KEY = "secretKey"; + public final static String SERVER_ADDR = "serverAddr"; + public final static String CONTEXT_PATH = "contextPath"; + public final static String CLUSTER_NAME = "clusterName"; + public final static String ENCODE = "encode"; + +``` +一、客户端可以通过两种方式初始化(二选一,必传) + +1. 通过直接传入Nacos server端信息(ip:port,或者域名)方式 + + `` + SERVER_ADDR server地址,格式为“ip1:port,ip2.port” + `` +2. 通过接入点进行接入获取环境信息 + + ``` + ENDPOINT 接入点 + CLUSTER_NAME 集群名字 + ``` + +二、链接的server的路径(非必传) + +``` +CONTEXT_PATH server根路径 (默认值 nacos) +``` +三、区域隔离(非必传) + +``` +NAMESPACE 名称区域 +``` + +四、鉴权参数(非必传) + +``` +ACCESS_KEY 公钥 +SECRET_KEY 私钥 +``` diff --git a/src/content/docs/v3.0/en/archive/use-nacos-with-istio.md b/src/content/docs/v3.0/en/archive/use-nacos-with-istio.md new file mode 100644 index 00000000000..36a05c3dd3a --- /dev/null +++ b/src/content/docs/v3.0/en/archive/use-nacos-with-istio.md @@ -0,0 +1,7 @@ +--- +title: Nacos Istio +keywords: [Nacos,Istio] +description: In plan with Nacos 1.x.x +--- + +**IN PLAN** with Nacos 1.x.x \ No newline at end of file diff --git a/src/content/docs/v3.0/en/archive/use-nacos-with-springcloud.md b/src/content/docs/v3.0/en/archive/use-nacos-with-springcloud.md new file mode 100644 index 00000000000..30b9fcc317d --- /dev/null +++ b/src/content/docs/v3.0/en/archive/use-nacos-with-springcloud.md @@ -0,0 +1,19 @@ +--- +title: Nacos Spring Cloud +keywords: [nacos,Spring Cloud] +description: Nacos Spring Cloud +--- + +# Nacos Spring Cloud + +## Examples + +* [Nacos Config Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md) +* [Nacos Discovery Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md) + +## Related Projects + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) \ No newline at end of file diff --git a/src/content/docs/v3.0/en/community/community.md b/src/content/docs/v3.0/en/community/community.md new file mode 100644 index 00000000000..3dfb43b612e --- /dev/null +++ b/src/content/docs/v3.0/en/community/community.md @@ -0,0 +1,30 @@ +--- +title: Community +keywords: [community,contact] +description: Community +sidebar: + order: 1 +--- + +# Community + +## Contact us + +### Nacos Gitter-[https://gitter.im/alibaba/nacos](https://gitter.im/alibaba/nacos) +### Nacos weibo-[https://weibo.com/u/6574374908](https://weibo.com/u/6574374908) +### Nacos segmentfault-[https://segmentfault.com/t/nacos](https://segmentfault.com/t/nacos) + +### Mailing list + +Mailing list is recommended for discussing almost anything related to Nacos. Please refer to this?[guide](https://github.com/apache/incubator-dubbo/wiki/Mailing-list-subscription-guide)?for detailed documentation on how to subscribe to our mailing lists. + +* [dev-nacos@googlegroups.com](mailto:dev-nacos%2Bsubscribe@googlegroups.com): The develop mailing list. You can ask questions here if you encounter any problem when using or developing Nacos. +* [commits-nacos@googlegroups.com](mailto:commits-nacos%2Bsubscribe@googlegroups.com): All commits will be sent to this mailing list. You can subscribe to it if you are interested in Nacos' development. +* [users-nacos@googlegroups.com](mailto:users-nacos%2Bsubscribe@googlegroups.com): All Github?[issue](https://github.com/alibaba/nacos/issues)?updates and?[pull request](https://github.com/alibaba/nacos/pulls)?updates will be sent to this mailing list. +* [nacos_dev@linux.alibaba.com](mailto:nacos_dev@linux.alibaba.com). + +### Nacos DingDing group + +Welcome to join Nacos community nail group + +![nacos_dingding.jpg](/img/nacos_dingding.jpg) \ No newline at end of file diff --git a/src/content/docs/v3.0/en/community/nacos-dev.md b/src/content/docs/v3.0/en/community/nacos-dev.md new file mode 100644 index 00000000000..56446187538 --- /dev/null +++ b/src/content/docs/v3.0/en/community/nacos-dev.md @@ -0,0 +1,101 @@ +--- +title: Developers +keywords: [nacos,developers,committer,pmc,nacos team] +description: Nacos developers page +sidebar: + order: 2 +--- + +# Developers + +## Nacos Developer Roles + +Nacos developers include three roles: Maintainer, Committer, and Contributor. The standard definitions for each role are as follows. + +### Maintainer + +Maintainer is an individual who has made a significant contribution to the evolution and development of the Nacos project, including projects under the nacos-group. Specifically includes the following criteria: + +* Completing the design and development of multiple key modules or projects, is an core developer of the project +* Continuous investment and passion, can actively participate in the maintenance of related matters such as community, official website, issue, PR, etc. +* Has a visible influence in the community and is able to represent Nacos in important community meetings and events +* Have the consciousness and ability to cultivate Committer and Contributor + +### Committer + +Committer is an individual with write access to the Nacos repository and includes the following criteria: + +* An individual who can contribute to the issue and PR continuously for a long time; +* Participate in the maintenance of the issue list and discussion of important features; +* Participate in code review; + +### Contributor + +Contributor is an individual who contributes to the Nacos project. The standard is: + +* Submitted a PR that is merged; + + +### Nacos Developer Rights and Obligations + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/333810/1560152742873-65f7dbcb-38cf-4840-aa9c-5c6cfa926cec.png#align=left&display=inline&height=502&name=image.png&originHeight=751&originWidth=1113&size=235532&status=done&width=744) + +## Nacos Team + +This page shows Nacos developers and continues to expand. The list is not prioritized. + +### Committers + +| Github ID | Name | Organization | Roles | Mail | +|------------------------------------------------ |---------------|------------------------------------| -----------| ------------------------------------------------------------ | +| [xuechaos](https://github.com/xuechaos) | Xuechao Xing | Alibaba | Maintainer | xingxuechao@alibaba-inc.com | +| [yanlinly](https://github.com/yanlinly) | Yanlin Li | Alibaba | Maintainer | yan.lin2009@163.com | +| [loadchange](https://github.com/loadchange) | Yanmin Wang | ele | Maintainer | wym177771@alibaba-inc.com | +| [nkorange](https://github.com/nkorange) | Pengfei Zhu | Shopee | Maintainer | zpf.073@gmail.com | +| [chuntaojun](https://github.com/chuntaojun) | Chuntao Liao | Tencent | Maintainer | liaochuntao@live.com | +| [paderlol](https://github.com/paderlol) | Pader Zhang | Shareworks | Maintainer | huangbbbaihao@gmail.com | +| [KomachiSion](https://github.com/KomachiSion) | Yi Yang | Alibaba | Maintainer | yangyi@apache.org | +| [sanxun0325](https://github.com/sanxun0325) | Binbin Zhang | DiDi | Maintainer | bbz17640380550@163.com | +| [zhangjimmy](https://github.com/zhangjimmy) | Bo Zhang | Huya | Committer | zhangjimmy@foxmail.com | +| [nanamikon](https://github.com/nanamikon) | Jian Zhou | Huya | Committer | nanamikon@gmail.com | +| [lzp0412](https://github.com/lzp0412) | Zhipeng Li | Ant Financial | Committer | 641785844@qq.com | +| [pguo65535](https://github.com/pguo65535) | Ping Guo | Alibaba | Committer | guoping.gp@alibaba-inc.com | +| [hxy1991](https://github.com/hxy1991) | Xiaoyu Huang | Alibaba | Committer | huangxiaoyu1018@gmail.com | +| [mingyixu](https://github.com/mingyixu) | Xiaochun Xu | Alibaba | Committer | xiaochun.xxc@alibaba-inc.com | +| [JianweiWang](https://github.com/JianweiWang) | Jianwei Wang | Alibaba | Committer | wangjianwei.nwpu@gmail.com | +| [jameslcj](https://github.com/jameslcj) | Chen li | Alibaba | Committer | zhichen.lc@alibaba-inc.com | +| [mercyblitz](https://github.com/mercyblitz) | Mercy Ma | Alibaba | Committer | taogu.mxx@alibaba-inc.com | +| [fangjian0423](https://github.com/fangjian0423) | Jian Fang | Alibaba | Committer | fangjian.fj@alibaba-inc.com | +| [wyp12](https://github.com/wyp12) | Yuanpiao Wu | Alibaba | Committer | caogu.wyp@antfin.com | +| [TsingLiang](https://github.com/TsingLiang) | Liang qing | Alibaba | Committer | qingliang.ql@alibaba-inc.com | +| [caojiele](https://github.com/caojiele) | Jiele Cao | Sinocare | Committer | caojiele1225@126.com | +| [KeRan213539](https://github.com/KeRan213539) | Ying Xie | ShengDa | Committer | 213539@qq.com | +| [zongtanghu](https://github.com/zongtanghu) | Zongtang Hu | China Mobile Cloud Centre | Committer | zongtanghu@hotmail.com | +| [Maijh97](https://github.com/Maijh97) | Jianhui Mai | Yunliuchain | Committer | xiaomai_h@163.com | +| [wangweizZZ](https://github.com/wangweizZZ) | Wei Wang | China Mobile Cloud Centre | Committer | wwfortunate@gmail.com | +| [horizonzy](https://github.com/horizonzy) | Yan Zhao | Dmall(Cheng Du) | Committer | 1060026287@qq.com | +| [catcherwong](https://github.com/catcherwong) | Wenqing Huang | Lianou Health Technology | Committer| catcher_hwq@outlook.com | +| [TTTTTAAAAAKKKKEEEENNNN](https://github.com/TTTTTAAAAAKKKKEEEENNNN) | Hanyu Liu | Tencent | Committer | tensai0lhy@gmail.com | +| [haoyann](https://github.com/haoyann) | Hao Yan | Shusheng Chengdu Technology | Committer | 1064645534@qq.com | +| [shiyiyue1102](https://github.com/shiyiyue1102) | Zunfei Liu | Alibaba | Committer | liuzunfei@gmail.com | +| [JackSun-Developer](https://github.com/JackSun-Developer) | Li Sun | Alibaba | Committer | yongyue.sl@alibaba-inc.com | +| [brotherlu-xcq](https://github.com/brotherlu-xcq) | Lu Cheng | KuJiaLe | Committer | 1285823170@qq.com | +| [li-xiao-shuang](https://github.com/li-xiao-shuang) | XiaoShuang Li | DiDi | Committer | 644968328@qq.com | +| [MajorHe1](https://github.com/MajorHe1) | Fan He | WeBank | Committer | 601023364@qq.com | +| [hujun-w-2](https://github.com/hujun-w-2) | Jun Hu | Xiaomi | Committer | 510830970@qq.com | +| [onewe](https://github.com/onewe) | Wenchao Mao | Independent developer | Committer | 2583021406@qq.com | +| [chenhao26-nineteen](https://github.com/chenhao26-nineteen)| Hao Chen | Xiaomi | Committer | hashmap2018@163.com | +| [shenkonghui](https://github.com/shenkonghui) | Konghui Shen | Harmony Cloud | Committer | shenkh1992@gmail.com | +| [CherishCai](https://github.com/CherishCai) | Hongwen Cai | Ant Financial | Committer | 785427346@qq.com | +| [The-Gamer-01](https://github.com/The-Gamer-01) | Yixuan Huang | Jishou University | Committer | 19974361760@163.com | +| [Karsonto](https://github.com/karsonto) | Karson | ICBC (Asia) | Committer | karsontao@hotmail.com | +| [Daydreamer-ia](https://github.com/Daydreamer-ia) | Yiqin Chen | Guangdong University of Technology | Committer | 2296032269@qq.com | +| [shalk](https://github.com/shalk) | Kun Xiao | NASDAQ:TIGR | Committer | xshalk@163.com | +| [heqingpan](https://github.com/heqingpan) | Qingpan He | Aicheng Technology | Committer | heqingpan@126.com | +| [godhth](https://github.com/godhth) | Tianhui Huang | CE Cloud | Committer | 1165559068@qq.com | + +### Contributors + +| Github ID | Name | Organization | Roles | Mail | +|-------------------------------------------|--------------------|------------------------------|-------------|---------------------------| +| [wfnuser](https://github.com/wfnuser) | Qinghao Huang | ByteDance | Contributor | wfnuser@hotmail.com | diff --git a/src/content/docs/v3.0/en/concepts.md b/src/content/docs/v3.0/en/concepts.md new file mode 100644 index 00000000000..8f980b59078 --- /dev/null +++ b/src/content/docs/v3.0/en/concepts.md @@ -0,0 +1,101 @@ +--- +title: Nacos Concepts +keywords: [Nacos,Concepts] +description: Nacos Concepts +--- + +# Nacos Concepts + +> NOTE: Nacos introduces some basic concepts and systematic understanding of these concepts can help you better understand and correct use Nacos products. + +## Region + +Physical data centers, unalterable after resources are created. + +## Available Zone + +Physical areas with independent power grids and networks in one region. The network latency for instances in the same zone is lower. + +## Endpoint + +The entry domain name of a service in each region. + +## Namespace + +For configuration isolation by tenants. Different namespaces may have configurations with the same Group or Data ID. One of the common scenarios for namespace is to differentiate and isolate the configurations in different environments, as in development and test environment and production environment. + +## Configuration + +During system development, developers usually extract some parameters or variables that need to be changed from the code and manage them in a separate configuration file. This enables the static system artifacts or deliverables (such as WAR and JAR packages) to fit with the physical operating environment in a better way. Configuration management is usually a part of system deployment, which is executed by the administrator or operation and maintenance personnel. Configuration modification is an effective way to adjust the behavior of a running system. + +## Configuration Management + +Configuration-related activities including editing, storage, distribution, modification management, release version management, and modification audit. + +## Configuration Item + +A specific configurable parameter with its value range, generally in the form of param-key=param-value. For example, the log output level (logLevel=INFO|WARN|ERROR) of a system is regarded as a configuration item. + +## Configuration Set + +A collection of related or unrelated configuration items.In a system, a configuration file is generally a configuration set which contains all the configurations of the system. For example, a configuration set may contain configuration items such as data sources, thread pools, and log levels. + +## Data ID + +The ID of a configuration set in Nacos. It is one of the dimensions according to which configurations are organized. Data ID is generally used to organize the system configuration sets. A system or application can contain multiple configuration sets, each of which can be identified by a meaningful name. The Data ID usually uses the naming rule similar to Java packages (for example, com.taobao.tc.refund.log.level) to ensure global uniqueness. This naming rule is not mandatory. + +## Group + +The group of configuration sets in Nacos. It is one of the dimensions according to which configurations are organized. The configuration sets are always grouped by a meaningful string such as Buy or Trade to differentiate the configuration sets with the same Data ID. When you create a configuration on Nacos, the group name is replaced by DEFAULT\_GROUP by default if not specified. A typical scenario of Group is when the same configuration type is used for different applications or components, such as database\_url configuration and MQ\_topic configuration. + +## Configuration Snapshot + +The Nacos client SDK can generate snapshots of configurations on local machines. Snapshots can be used to indicate the overall disaster recovery capabilities of the system when the client cannot connect to the Nacos server. Configuration snapshot is similar to local commit in Git, or cache, which is updated at the appropriate time, but does not have the notion of expiration as in cache. + +## Service + +Software functions which are provided to the client via the network through a predefined interface. + +## Service Name + +Identifier provided by the service, by which the service it refers to can be uniquely determined. + +## Service Registry + +Database which stores the instances of services and the load balancing policies for services. + +## Service Discovery + +On a computer network, the address and metadata of an instance under the service are probed (usually using a service name) and provided to the client for querying with a predefined interface. + +## Metadata + +Custom configuration information, such as a disaster recovery policy, a load balancing policy, an authentication configuration, and various tags. From the scope of action, it is divided into meta-information of service level, meta-information of virtual cluster, and meta-information of instance. + +## Application + +Property of service which can be used to identify the service provider. + +## Service Group + +Different services can be categorized into the same service group. + +## Virtual Cluster + +Service instances under the same service can be further classified. One possible unit of this classification is Virtual Cluster. + +## Instance + +A process with an accessible network address (IP:Port) that provides one or more services. + +## Weight + +Instance-level configuration. Weight is a floating-point number. The greater the weight, the greater the traffic that the instance expects to be allocated. + +## Health Check + +Health check of the instances under a service in a specified manner to ensure that the instances can work properly. Instances are judged to be healthy or unhealthy according to the inspection results. Unhealthy instances are not returned to the client when initiating a resolution request to the service. + +## Protect Threshold + +To prevent traffic from flowing to healthy instances because of some unhealthy instances, which causes traffic pressure, healthy instance collapse, and finally an avalanche, the health protection threshold should be defined as a floating point number between 0 and 1. When the proportion of the domain name healthy instance to the total instance is smaller than this value, the instance is returned to the client regardless of the health of the instance. Although this can result in a loss of some of the traffic, we ensure that the remaining healthy instances can work normally. diff --git a/src/content/docs/v3.0/en/contribution/contributing-flow.md b/src/content/docs/v3.0/en/contribution/contributing-flow.md new file mode 100644 index 00000000000..88ce07fb0f7 --- /dev/null +++ b/src/content/docs/v3.0/en/contribution/contributing-flow.md @@ -0,0 +1,107 @@ +--- +title: Contributing Flow +keywords: [Contributing,Source Code] +description: This contribution flow is applicable to all Nacos community content, including but not limited to Nacos, Nacos wiki/doc, Nacos SDK. +sidebar: + order: 2 +--- + +# Nacos Contributing Flow + +This contribution flow is applicable to all Nacos community content, including but not limited to `Nacos`, `Nacos wiki/doc`, `Nacos SDK`. + +The following use contributing `Nacos` as an example to explain the contribution flow in detail. + +## 1. Fork Alibaba/Nacos repository to your Github. + +## 2. Clone your fork Nacos repository to local. + +``` +git clone ${your fork nacos repo address} + +cd nacos +``` + +## 3. Add Alibaba/Nacos repository as upstream repo. + +``` +git remote add upstream https://github.com/alibaba/nacos.git + +git remote -v + + origin ${your fork nacos repo address} (fetch) + origin ${your fork nacos repo address} (push) + upstream https://github.com/alibaba/nacos.git (fetch) + upstream https://github.com/alibaba/nacos.git (push) + +git fetch origin +git fetch upstream +``` + +## 4. Choose a basic branch of development usually upstream/develop,and create a new branch based on it. + +``` +(checkout branch from remote repo to local) +git checkout -b upstream-develop upstream/develop + +(Create a development branch from the local branch, usually using the issue number as the development branch name) +git checkout -b develop-issue#${issue-number} + +``` + +## 5. Do your change in your local develop branch. + +First please make sure you read and set the `Nacos code style` correctly, please read the related content [Code of Conduct](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md). + +When making changes, please ensure that the changes on this branch are **only relevant to the issue**, and try to be as small as possible, so that **only one thing is modified in one branch, and only one thing is modified in one PR**. + +At the same time, please use your English description as much as possible for your commits. It is mainly described by **predicate + object**, such as: `Fix xxx problem/bug`. + +Some simple commits can be described using `For xxx`, such as: `For codestyle`. + +If the commits is related to an ISSUE, you can add the ISSUE number as a prefix, such as: `For #10000, Fix xxx problem/bug`. + +## 6. Rebase develop branch + +When you make changes, other people's changes may have commited and merged. At this time, there may be conflicts. Please use the rebase command to merge and resolve. There are two main benefits: + +1. Your submission record will be very clean, without the words `Merge xxxx branch`. +2. After rebase, the commit log of your branch is also a single chain, it is easier to check back. + +``` +git fetch upstream + +git rebase -i upstream/develop + +``` + +OR + +``` +git checkout upstream-develop +git pull +git checkout develop-issue#${issue-number} +git rebase -i upstream-develop +``` + +**If you are using Intellij IDEA**, it is recommended to use the IDE version control, which has a more convenient visual panel to resolve conflicts and squash operations. + +## 7. Push your develop branch to your fork repository. + +``` +git push origin develop-issue#${issue-number} +``` + +## 8. Create Pull Request according to the pull request template + +[pull request template](./pull-request.md) + +The Nacos community will review your Pull Request and may propose comments. + +You can return to step 5 to modify code according to the comments and use step 6 to resubmit. + +**If you are prompted that there are conflicts when you push to fork repo again, Force push to your fork branch will be ok.** The reason of conflicts is that the commit ID has changed after you rebase with others changes. + + +## 9. If no more problem, Nacos community will merge your PR. Congratulations for you becoming a official contributor of Nacos. + diff --git a/src/content/docs/v3.0/en/contribution/contributing.md b/src/content/docs/v3.0/en/contribution/contributing.md new file mode 100644 index 00000000000..227b5311ecf --- /dev/null +++ b/src/content/docs/v3.0/en/contribution/contributing.md @@ -0,0 +1,102 @@ +--- +title: How to Contribute +keywords: [contribute,code] +description: We are always very happy to have contributions, whether for trivial cleanups or big new features. +sidebar: + order: 1 +--- + +# How to Contribute + +Nacos is released under the non-restrictive Apache 2.0 license, and follows a very standard Github development process, using Github tracker for issues and merging pull requests into master. If you want to contribute even something trivial, please do not hesitate, but follow the guidelines below. + +We are always very happy to have contributions, whether for trivial cleanups or big new features. +We want to have high quality, well documented codes for each programming language. + +Nor is code the only way to contribute to the project. We strongly value documentation, integration with other project, and gladly accept improvements for these aspects. + +## Contact us + +##### Nacos Gitter- [https://gitter.im/alibaba/nacos](https://gitter.im/alibaba/nacos) +##### Nacos weibo- [https://weibo.com/u/6574374908](https://weibo.com/u/6574374908) +##### Nacos segmentfault- [https://segmentfault.com/t/nacos](https://segmentfault.com/t/nacos) + +#### Mailing list + +Mailing list is recommended for discussing almost anything related to Nacos. Please refer to this?[guide](https://github.com/apache/incubator-dubbo/wiki/Mailing-list-subscription-guide)?for detailed documentation on how to subscribe to our mailing lists. + +* [dev-nacos@googlegroups.com](mailto:dev-nacos%2Bsubscribe@googlegroups.com): The develop mailing list. You can ask questions here if you encounter any problem when using or developing Nacos. +* [commits-nacos@googlegroups.com](mailto:commits-nacos%2Bsubscribe@googlegroups.com): All commits will be sent to this mailing list. You can subscribe to it if you are interested in Nacos' development. +* [users-nacos@googlegroups.com](mailto:users-nacos%2Bsubscribe@googlegroups.com): All Github?[issue](https://github.com/alibaba/nacos/issues)?updates and?[pull request](https://github.com/alibaba/nacos/pulls)?updates will be sent to this mailing list. +* [nacos_dev@linux.alibaba.com](mailto:nacos_dev@linux.alibaba.com). + +## Contributing Code + +### Notice + +To submit a change for inclusion, please do the following: + +#### Read Nacos [Code of Conduct](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md), and make sure your IDE has set code style and install plugin. + +#### If the change is non-trivial, please include unit tests that cover the new functionality. + +#### If you are introducing a completely new feature or API, it is a good idea to start a wiki and get consensus on the basic design first. + +### Contribution flow + +This is a rough outline of what a contributor's workflow looks like: + +* Fork the current repository. +* Create a topic branch from where to base the contribution. This is usually the master branch. +* Make commits of logical units. +* Make sure commit messages are in the proper format (see below). +* Push changes in a topic branch to your forked repository. +* Follow the checklist in the [pull request template](./pull-request.md). +* Before you send the pull request, please sync your forked repository with remote repository. This will make your pull request simple and clear. See guide below: +``` +git remote add upstream git@github.com:alibaba/nacos.git +git fetch upstream +git rebase upstream/master +git checkout -b your_awesome_patch +... add some work +git push origin your_awesome_patch +``` +* Submit a pull request to alibaba/nacos and wait for reply. + +* Detail contribution flow see [Contribution Flow](./contributing-flow.md) + +Thanks for contributing! + +## Contributing Document + +### Notice + +When contributing documents, please confirm and check the following: + +#### Has been confirmed that the document is indeed wrong or missing. + +#### Familiar with [Markdown](https://www.markdownguide.org/getting-started) + +#### Familiar with [docsite](https://github.com/txd-team/docsite), at least be able to complete local debugging according to the [document README.md](https://github.com/nacos-group/nacos-group.github.io). + +## Becoming a Committer + +We are always interested in adding new contributors. What we look for are series of contributions, good taste and ongoing interest in the project. If you are interested in becoming a committer, please let one of the existing committers know and they can help you walk through the process. + +Nowadays, we have several important contribution points: + +#### Wiki & JavaDoc +#### Nacos Console +#### Nacos SDK(C++\.Net\PHP\Python\Go\Node.js) + +#### Prerequisites + +If you want to contribute to the above listing points, you must abide by the prerequisites listed below: + +##### Readability - APIs as well as important methods must have Javadoc. + +##### Testability - Ensure over 80% unit test coverage for main processes. + +##### Maintainability - Comply with our [Code of Conduct](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md), with an update frequency at least once every 3 months. + +##### Deployability - We encourage you to deploy into [maven repository](http://search.maven.org/). diff --git a/src/content/docs/v3.0/en/contribution/how-to-reporting-bugs.md b/src/content/docs/v3.0/en/contribution/how-to-reporting-bugs.md new file mode 100644 index 00000000000..bfe00fd895f --- /dev/null +++ b/src/content/docs/v3.0/en/contribution/how-to-reporting-bugs.md @@ -0,0 +1,32 @@ +--- +title: How to report bugs +keywords: [nacos,submit,reporting bugs] +description: How to report bugs +sidebar: + order: 4 +--- + +# Reporting bugs + +If any part of the Nacos project has bugs or documentation mistakes, please let us know by [opening an issue](https://github.com/alibaba/nacos/issues/new). We treat bugs and mistakes very seriously and believe no issue is too small, anyOne is implement. Before creating a bug report, please check that an issue reporting the same problem does not already exist. + +To make the bug report accurate and easy to understand, please try to create bug reports that are: + +- Specific. Include as much details as possible: which version, what environment, what configuration, etc. If the bug is related to running the Nacos server, please attach the Nacos log (the starting log with Nacos configuration is especially important). + +- Reproducible. Include the steps to reproduce the problem. We understand some issues might be hard to reproduce, please includes the steps that might lead to the problem. If possible, please attach the affected Nacos data dir and stack strace to the bug report. + +- Unique. Do not duplicate existing bug report. + + +It may be worthwhile to read [Elika Etemad’s article on filing good bug reports](http://fantasai.inkedblade.net/style/talks/filing-good-bugs/) before creating a bug report. + +We might ask for further information to locate a bug. A duplicated bug report will be closed. + +[nacos-issue](https://github.com/alibaba/nacos/issues/new) + +[filing-good-bugs](http://fantasai.inkedblade.net/style/talks/filing-good-bugs/) + +# Reporting security bugs + +If you find any security problem in the Nacos project, please let us know through [ASRC (Alibaba Security Response Center)](https://security.alibaba.com). diff --git a/src/content/docs/v3.0/en/contribution/pull-request.md b/src/content/docs/v3.0/en/contribution/pull-request.md new file mode 100644 index 00000000000..b00b6ff00d2 --- /dev/null +++ b/src/content/docs/v3.0/en/contribution/pull-request.md @@ -0,0 +1,32 @@ +--- +title: Pull request template +keywords: [pull request,template] +description: Pull request template +sidebar: + order: 3 +--- + +# Pull request template + +Please do not create a Pull Request without creating an issue first. + +## What is the purpose of the change + +XXXXX + +## Brief changelog + +XX + +## Verifying this change + +XXXX + +Follow this checklist to help us incorporate your contribution quickly and easily: + +* [ ] Make sure there is a Github issue filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. +* [ ] Format the pull request title like `[ISSUE #123] Fix UnknownException when host config not exist`. Each commit in the pull request should have a meaningful subject line and body. +* [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. +* [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in [test module](https://github.com/apache/rocketmq/tree/master/test). +* [ ] Run `mvn -B clean apache-rat:check findbugs:findbugs` to make sure basic checks pass. Run `mvn clean install -DskipITs` to make sure unit-test pass. Run `mvn clean test-compile failsafe:integration-test` to make sure integration-test pass. +* [ ] If this contribution is large, please file an [Apache Individual Contributor License Agreement](http://www.apache.org/licenses/#clas). \ No newline at end of file diff --git a/src/content/docs/v3.0/en/ecology/img/k8s-sync.jpg b/src/content/docs/v3.0/en/ecology/img/k8s-sync.jpg new file mode 100644 index 0000000000000000000000000000000000000000..78205dd947923f6970e7698c1e5b83132f0e7c48 GIT binary patch literal 149811 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 literal 0 HcmV?d00001 diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-sync.md b/src/content/docs/v3.0/en/ecology/use-nacos-sync.md new file mode 100644 index 00000000000..baea80da928 --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-sync.md @@ -0,0 +1,241 @@ +--- +title: NacosSync user guide +keywords: [NacosSync,migration,user guide] +description: NacosSync migration user manual +sidebar: + order: 5 +--- + +# NacosSync migration user guide + +# Guide purposes +* Understand NacosSync service +* Start NacosSync service +* By a simple example, demonstrates how to register to the Zookeeper Dubbo client migrated to Nacos. + +# Introduce +* NacosSync is a support for a variety of registry of synchronous components,based on the Spring boot development framework,Data layer uses the Spring Data JPA,follow the standard JPA access codes,support for multiple data storage,Default to Hibernate implementation, support table created automatically update more conveniently. +* Using efficient event driven asynchronous model, support a variety of custom events, make the synchronization task processing time delay control in 3s, 8C16G stand-alone can support 6 k synchronization tasks. +* NacosSync in addition to the standalone deployment, but also provides high availability cluster deployment patterns, NacosSync is stateless design, such as task status data migration to the database, the cluster expansion is very convenient. +* Abstraction is out of Sync core component interface, through annotations to distinguish synchronous type, allowing developers to easily according to their own needs, different registry to expand, has now supports synchronous type: + * Nacos to Nacos data synchronization + * Zookeeper to Nacos data synchronization + * Nacos to the Zookeeper data synchronization + * Eureka to Nacos data synchronization + * Consul to Nacos data synchronization + +## System module architecture: +![image.png](https://img.alicdn.com/tfs/TB12VPaJVzqK1RjSZSgXXcpAVXa-886-752.png)
The console
Provides concise Web console operation, support for internationalization.
+ +### Synchronization task management page +![](https://img.alicdn.com/tfs/TB1eSYyJ5LaK1RjSZFxXXamPFXa-2866-1064.png) + +### Registry management page + +## ![image.png](https://img.alicdn.com/tfs/TB1e_rdJ7voK1RjSZFNXXcxMVXa-2876-1124.png) + +## Usage scenarios: +* Multiple network communication between the Region of Shared services, break the service call restriction of the Region. + +![image.png](https://img.alicdn.com/tfs/TB1Mo6yJ4jaK1RjSZKzXXXVwXXa-1136-798.png) + +* Two-way synchronization function, support Dubbo + Zookeeper service smooth migration to Dubbo + Naocs, enjoy Nacos more high quality service. + +![image.png](https://img.alicdn.com/tfs/TB1Dza8J9zqK1RjSZPxXXc4tVXa-1728-838.png) + +# Usage + +## Preparatory work +Before you start the service, you need to install the following services: +* 64bit OS: Linux/Unix/Mac/Windows supported, Linux/Unix/Mac recommended. +* 64bit JDK 1.8+: [downloads](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html), [JAVA_HOME settings](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/). +* Maven 3.2.x+: [downloads](https://maven.apache.org/download.cgi), [settings](https://maven.apache.org/settings.html). +* MySql 5.6.+ + +## Get the installation package +There are two ways to obtain NacosSync installation package: +* Direct download NacosSync binary installation package:[nacosSync.${version}.zip](https://github.com/nacos-group/nacos-sync/releases) +* Download NacosSync source to build from the Github + +Package: +```basic +cd nacosSync/ +mvn clean package -U +``` + +The path of the target file: +```basic +nacos-sync/nacossync-distribution/target/nacosSync.${version}.zip +``` + +Unpack the installation package, project file directory structure: +```basic +nacosSync +├── LICENSE +├── NOTICE +├── bin +│   ├── nacosSync.sql +│   ├── shutdown.sh +│   └── startup.sh +├── conf +│   ├── application.properties +│   └── logback-spring.xml +├── logs +└── nacosSync-server.${version}.jar +``` + +## Initialize the database + +The system default configuration database is Mysql, can also support other relational database. +1.The database construction, the default database name for "nacos_Sync". +2.The database table don't need to create separately, using the hibernate automatically by default build table function. +3.If you do not support automatic table, you can use the system's own build table SQL script, the script in the bin directory. + +## Database configuration + +Database configuration file on `conf/application.properties`: +```basic +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/nacos_sync?characterEncoding=utf8 +spring.datasource.username=root +spring.datasource.password=root +``` + +## Start server + +```bash +$ nacosSync/bin: +sh startup.sh restart +``` + +## Check system status + +1.Check system log + +The path of the log in `nacosSync/logs/nacosSync.log`, check whether there are abnormal information. + +2.Check system port + +Port is the default system `8081`, you can define your own `application.properties`. + +```bash +$netstat -ano|grep 8081 +tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN off (0.00/0/0) +``` + +## Console + +Access path: +``` +http://127.0.0.1:8081/#/serviceSync +``` +![image.png](https://img.alicdn.com/tfs/TB1EKbkJ3HqK1RjSZFEXXcGMXXa-2866-606.png) + +If there is no problem, check NacosSync has begun, normal NacosSync deployment structure:![image.png](https://img.alicdn.com/tfs/TB107nfJ9zqK1RjSZFjXXblCFXa-1412-342.png) + +## Starting migration + +### Migration information + +Dubbo service deployment information:![image.png](https://img.alicdn.com/tfs/TB1Ci_eJ4TpK1RjSZR0XXbEwXXa-938-700.png) + +The migration of services: + +| Service Name | Version | Group Name | +| --- | --- | --- | +| com.alibaba.nacos.api.DemoService | 1.0.0 | zk | + +### Add the registry cluster information + +1.Click on the "cluster configuration" button in the left navigation bar, a new cluster, first add a Zookeeper cluster, select the cluster type for ZK. +![image.png](https://img.alicdn.com/tfs/TB1oJDnJ7voK1RjSZFwXXciCFXa-2870-1130.png) + +> Note: the cluster name can customize, but once confirmed, cannot be modified, otherwise increase task based on the cluster, after NacosSync restart, success will not resume. + +2.The same steps, increase Nacos cluster. +![image.png](https://img.alicdn.com/tfs/TB1HQPhJVzqK1RjSZFCXXbbxVXa-2846-1042.png) + +3.After the completion of the add, can inquire on the list: +![image.png](https://img.alicdn.com/tfs/TB1AX6fJVYqK1RjSZLeXXbXppXa-2864-824.png) + +### Add the synchronization task + +1. Add a sync task, from Zookeeper cluster synchronization to Nacos cluster, synchronous granularity is service, it is called a Zookeeper cluster source cluster, Nacos cluster called target cluster. +![imagesd.png](https://img.alicdn.com/tfs/TB1tF_fJVYqK1RjSZLeXXbXppXa-2838-1138.png) + +Add finished, can be in service sync list, view has add synchronization task: +![image.png](https://img.alicdn.com/tfs/TB1l6uJJ9zqK1RjSZPcXXbTepXa-2824-570.png) + +2. The synchronization is completed, check whether the data synchronization to success Nacos cluster, can query through the Nacos console. +![image.png](https://img.alicdn.com/tfs/TB1tPneJ4TpK1RjSZR0XXbEwXXa-2872-828.png) + +3. At the moment, the data has been successfully from Zookeeper cluster synchronization to Nacos cluster, the deployment structure is as follows: +![image.png](https://img.alicdn.com/tfs/TB14kriJ6TpK1RjSZKPXXa3UpXa-1724-772.png) + +### Dubbo clients to connect to Nacos registry + +#### Dubbo Consumer client migration + +Dubbo has supported Nacos registry, support version 2.5 +, need to add a Nacos registry of Dubbo extensions depends on: +```basic + + com.alibaba + dubbo-registry-nacos + 0.0.2 + +``` + +Increase Nacos client depends on: +```basic + + com.alibaba.nacos + nacos-client + 0.6.2 + +``` + +Configuration Dubbo Consumer Dubbo profile `Consumer. The yaml`, let the client can find Nacos cluster. +```basic +spring: + application: +name: dubbo-consumer +demo: + service: + version: 1.0.0 + group: zk +dubbo: + registry: + address: nacos://127.0.0.1:8848 +``` + +Don't need to modify the code, configuration after the update, you can restart your application into law. + +Consumer release is completed, the deployment of the structure is as follows: +![image.png](https://img.alicdn.com/tfs/TB181fkJ3HqK1RjSZFEXXcGMXXa-1734-878.png) + +#### Dubbo Provider migration + +Before you upgrade the Provider, you need to ensure that the Provider of services, are already configured in NacosSync, synchronous way from Nacos synchronization to Zookeeper, because the Provider connected to Nacos upgrade, you need to make sure that the old Dubbo Consumer client can subscribe to the Provider's address in the Zookeeper, now, we add a sync task: +![image.png](https://img.alicdn.com/tfs/TB1pdDnJ7voK1RjSZFwXXciCFXa-2872-1060.png) + +![image.png](https://img.alicdn.com/tfs/TB19Ey_J6DpK1RjSZFrXXa78VXa-2842-660.png) + +> Note: Nacos synchronization to the Zookeeper service, do not need to fill in the version number, you in choosing the source cluster, the version number of the input box automatically hidden. + +Sync task is completed, you can upgrade the Provider, upgrade the Provider method, reference to upgrade the Consumer steps. + +### New deployment structure +* In the process of upgrading, there will be new and old versions of the client at the same time, the deployment structure is as follows: + +![image.png](https://img.alicdn.com/tfs/TB14Y_iJ3HqK1RjSZFPXXcwapXa-1728-838.png) + +* All client migration is completed, the deployment structure is as follows: + +![image.png](https://img.alicdn.com/tfs/TB1Cg2dJYvpK1RjSZPiXXbmwXXa-1466-864.png) + +Now, the Zookeeper cluster, NacosSync cluster can get offline. + +### Attention +* Synchronization task after adding, you need to ensure that the service is successful sync to the target cluster, can through the console the target cluster. +* NacosSync support high availability cluster pattern deployment, you only need to put the database can be configured to the same. +* Comb if not clear subscription and published service, suggested can do services are two-way synchronous. +* Dubbo client currently does not support Nacos weighting function, if you are using the weight function, need to reconsider the plan. diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-coredns.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-coredns.md new file mode 100644 index 00000000000..717ea29fb91 --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-coredns.md @@ -0,0 +1,53 @@ +--- +title: Nacos push domain with CoreDNS +keywords: [CoreDNS, DNS-F] +description: Nacos push domain with CoreDNS +sidebar: + order: 6 +--- + +# Nacos DNS user guide +This plugin provides a DNS-F client based on CoreDNS, which can help export those registed services on Nacos as DNS domain. DNS-F client is a dedicated agent process(side car) beside the application's process to foward the service discovery DNS domain query request to Nacos. +## Quick Start +To build and run nacos coredns plugin, the OS must be Linux or Mac. And also, make sure your nacos version is 2.2 or higher and golang version is 1.17 or higher. And golang environments(GOPATH,GOROOT,GOHOME) must be configured correctly. Because it needs to support the gRPC connection feature of the nacos2.x version and the go mod function. +## Build +``` +git clone https://github.com/nacos-group/nacos-coredns-plugin.git +cp nacos-coredns-plugin/bin/build.sh ~/ +cd ~/ +sh build.sh +``` + +## Configuration +To run nacos coredns plugin, you need a configuration file. A possible file may be as bellow: +``` +. { + log + nacos { + nacos_namespaceId public + nacos_server_host 127.0.0.1:8848 + } + forward . /etc/resolv.conf +} +``` +- forward: domain names those not registered in nacos will be forwarded to upstream. +- nacos_namespaceId: nacos namespaceId, defalut is public. +- nacos_server_host: Ip and Port of nacos server, seperated by comma if there are two or more nacos servers + +## Run +1. Firstly, you need to deploy nacos server. [Here](https://github.com/alibaba/nacos) +2. Secondly, register service on nacos. +3. Finally, configure the file **($path_to_corefile)** and the port **($dns_port)** to run the plugin. +``` +$GOPATH/src/coredns/coredns -conf $path_to_corefile -dns.port $dns_port +``` +![](https://cdn.nlark.com/yuque/0/2022/png/29425667/1663504581023-95437fee-0e3d-4b6a-851c-44a352dedd81.png) + +## Test + +Input the service name **($nacos_service_name)** , and the plugin's IP address **($dns_ip)** and port **($dns_port)** . We can get the DNS answer from the plugin. + +``` +dig $nacos_service_name @$dns_ip -p $dns_port +``` +![](https://cdn.nlark.com/yuque/0/2022/png/29425667/1663504588231-341b38fe-da55-41eb-a24b-e3752b86faa4.png) diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-dubbo.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-dubbo.md new file mode 100644 index 00000000000..3a3c8fe8299 --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-dubbo.md @@ -0,0 +1,473 @@ +--- +title: Nacos with Dubbo fusion become registry +keywords: [Dubbo,Nacos,registration center] +description: Nacos with Dubbo fusion become registry +sidebar: + order: 1 +--- + +# Nacos with Dubbo fusion become registry + +Nacos as Dubbo ecosystems important registry implementation, including [`dubbo-registry-nacos`](https://github.com/dubbo/dubbo-registry-nacos) is Dubbo fusion Nacos registry implementation. + +## Preparatory work + +When you put [`dubbo-registry-nacos`](https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos) integrated into your dubbo project before, please make sure the background nacos service has started. If you are still not familiar with the basic use of Nacos, reference [Quick Start for Nacos](../quickstart/quick-start.mdx). + +## Quick Start + +Nacos Dubbo fusion become registry procedure is very simple, general steps can be divided into "increasing Maven dependency" and "the registry". + +### Increasing Maven dependency + +First, you need to `dubbo-registry-nacos`Maven dependent on added to your project `pom.xml` file, and strongly recommend that you use the dubbo `2.6.5`: + +```xml + + + ... + + + + com.alibaba + dubbo + 3.0.5 + + + + + com.alibaba + dubbo-registry-nacos + 3.0.5 + + + + + com.alibaba.spring + spring-context-support + 1.0.11 + + + ... + + +``` + +When a project to add`dubbo-registry-nacos`, you don't need to explicitly logic programming to realize service discovery and registration, actual implementation is provided by the third party and then configure Naocs registry. + +### Configuration registry + +Suppose you Dubbo application using the Spring Framework assembly, there will be two kinds of optional configuration method, respectively: [Dubbo Spring externalized configuration](https://mercyblitz.github.io/2018/01/18/Dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/), and the Spring XML configuration files, and, I strongly recommend the former. + +### Dubbo Spring externalized configuration + +Dubbo Spring externalized configuration consists of Dubbo `2.5.8` introduced new features, through the Spring `Environment` attribute automatically generate and bind the Dubbo configuration Bean, implement configuration to simplify, and lower the threshold of service development. + +Suppose you Dubbo applications using Nacos as registry, and the server IP address is:`10.20.153.10` at the same time, the registered address as Dubbo externalized configuration properties are stored in `dubbo-config.properties` file, as shown below: + +```properties +## application +dubbo.application.name = your-dubbo-application + +## Nacos registry address +dubbo.registry.address = nacos://10.20.153.10:8848 +##If you want to use your own namespace, you can use the following two methods: +#dubbo.registry.address = nacos://10.20.153.10:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +... +``` + +Then, restart your application Dubbo, Dubbo information services and consumption in Nacos console can show: + +![image-20181213103845976-4668726.png | left | 747x284](https://img.alicdn.com/tfs/TB1n6m7zMTqK1RjSZPhXXXfOFXa-2784-1058.png "") + +As shown, the service name prefix for `providers:` metainfo for the service provider's information, `consumers:` represents the service consumer metainfo. Click"**details**" can check the service status details: + +![image-20181213104145998-4668906.png | left | 747x437](https://img.alicdn.com/tfs/TB1vZzfzQzoK1RjSZFlXXai4VXa-2714-1588.png "") + +If you are using a Spring XML configuration file assembly Dubbo registry, please refer to the next section. + +### Spring XML configuration files + +Similarly, suppose you Dubbo applications using Nacos as registry, and the server IP address is:`10.20.153.10`, and assembling Spring Bean in the XML file, as shown below: + +```xml + + + + + + + + + + + ... + +``` + +After restart the Dubbo application, you can also find the provider and consumer registered metainfo is presented in Nacos console: + +![image-20181213113049185-4671849.png | left | 747x274](https://img.alicdn.com/tfs/TB1zl2dzQPoK1RjSZKbXXX1IXXa-2784-1022.png "") + +Whether you absolute configuration or switch Nacos registry super Easy? If you are still wanting more or less understand, may refer to the following the complete example. + +## Complete sample + +Above pictures of metadata from Dubbo Spring annotations driver sample and Dubbo Spring XML configuration driven example, the following will introduce both, you can choose your preference programming model.Before the formal discussion, first to introduce the preparation work, because they are dependent on the Java service interface and implementation.At the same time, **please make sure that the local (`127.0.0.1`) environment has launched Nacos service**. + +### Sample interface and implementation + +First define the sample interface, as shown below: + +```java +package com.alibaba.nacos.example.dubbo.service; + +public interface DemoService { + String sayName(String name); +} +``` + +Provide the above interface implementation class: + +```java + +package com.alibaba.nacos.example.dubbo.service; + +import com.alibaba.dubbo.config.annotation.Service; +import com.alibaba.dubbo.rpc.RpcContext; +import org.springframework.beans.factory.annotation.Value; + +/** + * Default {@link DemoService} + * https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/ + * @since 2.6.5 + */ +@Service(version = "${demo.service.version}") +public class DefaultService implements DemoService { + + @Value("${demo.service.name}") + private String serviceName; + + public String sayName(String name) { + RpcContext rpcContext = RpcContext.getContext(); + return String.format("Service [name :%s , port : %d] %s(\"%s\") : Hello,%s", + serviceName, + rpcContext.getLocalPort(), + rpcContext.getMethodName(), + name, + name); + } +} +``` + +Interface and implementation after ready, the following will be driven by annotations and XML configuration driven their implementation. + +### Spring annotations driver sample + +https://github.com/nacos-group/nacos-examples/tree/master/nacos-dubbo-example + +Dubbo `2.5.7` reconstructed the Spring annotations driver programming model. + +#### Provider annotation driven implementation + +- Definition of Dubbo provider externalized configuration properties - `provider-config.properties` + +```properties +## application +dubbo.application.name = dubbo-provider-demo + +## Nacos registry address +dubbo.registry.address = nacos://127.0.0.1:8848 +##If you want to use your own namespace, you can use the following two methods: +#dubbo.registry.address = nacos://127.0.0.1:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 + +## Dubbo Protocol +dubbo.protocol.name = dubbo +dubbo.protocol.port = -1 + +# Provider @Service version +demo.service.version=1.0.0 +demo.service.name = demoService + +dubbo.application.qosEnable=false + +``` + + + +- Implement the provider to guide class - `DemoServiceProviderBootstrap` + +```java +package com.alibaba.nacos.example.dubbo.provider; + +import com.alibaba.nacos.example.dubbo.service.DemoService; +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.PropertySource; +import java.io.IOException; + +/** + * {@link DemoService} provider demo + * https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/ + */ +@EnableDubbo(scanBasePackages = "com.alibaba.nacos.example.dubbo.service") +@PropertySource(value = "classpath:/provider-config.properties") +public class DemoServiceProviderBootstrap { + + public static void main(String[] args) throws IOException { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(DemoServiceProviderBootstrap.class); + context.refresh(); + System.out.println("DemoService provider is starting..."); + System.in.read(); + } +} + +``` + +The annotation `@EnableDubbo` activation Dubbo annotation driven and externalized configuration, its `scanBasePackages` properties scanning to specify the Java package, all marked `@Service` Service interface implementation class exposure for Spring Bean, then be exported Dubbo Service. + +`@PropertySource` is Spring Framework 3.1 introduced the standard import properties annotation configuration resources, it will provide Dubbo externalized configuration. + +#### Service consumer comments drive implementation + +- Definition of Dubbo consumer externalized configuration properties - `consumer-config.properties` + +```properties +## Dubbo Application info +dubbo.application.name = dubbo-consumer-demo + +## Nacos registry address +dubbo.registry.address = nacos://127.0.0.1:8848 +##If you want to use your own namespace, you can use the following two methods: +#dubbo.registry.address = nacos://127.0.0.1:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 + +# @Reference version +demo.service.version= 1.0.0 + +dubbo.application.qosEnable=false + +``` + +Similarly, `dubbo.registry.address` attribute points to Nacos registry, other dubbo service relevant meta information through Nacos registry access. + +- Implementation services consumer guide - `DemoServiceConsumerBootstrap` + +```java +package com.alibaba.nacos.example.dubbo.consumer; + + +import com.alibaba.nacos.example.dubbo.service.DemoService; +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.PropertySource; +import javax.annotation.PostConstruct; +import java.io.IOException; + +/** + * {@link DemoService} consumer demo + * https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/ + */ +@EnableDubbo +@PropertySource(value = "classpath:/consumer-config.properties") +public class DemoServiceConsumerBootstrap { + + @DubboReference(version = "${demo.service.version}") + private DemoService demoService; + + @PostConstruct + public void init() { + for (int i = 0; i < 10; i++) { + System.out.println(demoService.sayName("Nacos")); + } + } + + public static void main(String[] args) throws IOException { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(DemoServiceConsumerBootstrap.class); + context.refresh(); + context.close(); + } +} + +``` + +Similarly, `@EnableDubbo` annotations to activate Dubbo annotation driven and externalized configuration, but the current belong to the Service consumers, without having to specify the Java package name scan label `@Service` Service implementation. + +`@Reference` is Dubbo remote service dependency injection annotations, need service providers and consumers agreed interface (interface), version (version) and group (group) information.Example, in the current service consumption `DemoService` service version from the configuration properties file `consumer-config.properties`. + +`@PostConstruct` code shows when `DemoServiceConsumerBootstrap` Bean initialization, execution Dubbo ten times remote method invocation. + +#### Run annotation driver sample + +Twice in the local boot `DemoServiceProviderBootstrap`, the registry will appear two health services: + +![image-20181213123909636-4675949.png | left | 747x38](https://img.alicdn.com/tfs/TB1s9fbzMHqK1RjSZFgXXa7JXXa-2390-122.png "") + +Run again `DemoServiceConsumerBootstrap`, run results as follows: + +``` +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +``` + +Operate, and service consumer using the load balancing strategy, RPC calls ten times the average contribution to two Dubbo provider instance. + +### Spring XML configuration driver sample + +The Spring XML configuration driven programming model is a traditional Spring assembly components. + +#### Provider XML configuration + +- Define the service provider XML context configuration file - `/META-INF/spring/dubbo-provider-context.xml` + +```xml + + + + + + + + + + + + + + + + + + + + +``` + +- Implement the provider to guide class - `DemoServiceProviderXmlBootstrap` + +```xml +package com.alibaba.nacos.example.dubbo.provider; + +import com.alibaba.dubbo.demo.service.DemoService; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.io.IOException; + +/** + * {@link DemoService} provider demo XML bootstrap + */ +public class DemoServiceProviderXmlBootstrap { + + public static void main(String[] args) throws IOException { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); + context.setConfigLocation("/META-INF/spring/dubbo-provider-context.xml"); + context.refresh(); + System.out.println("DemoService provider (XML) is starting..."); + System.in.read(); + } +} +``` +#### Service consumer driven XML configuration + +- Define the service consumer context XML configuration files - `/META-INF/spring/dubbo-consumer-context.xml` + +```xml + + + + + + + + + + + + + + + +``` +- Implementation services consumer guide - `DemoServiceConsumerXmlBootstrap` + +```java +package com.alibaba.nacos.example.dubbo.consumer; + +import com.alibaba.dubbo.demo.service.DemoService; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.io.IOException; + +/** + * {@link DemoService} consumer demo XML bootstrap + */ +public class DemoServiceConsumerXmlBootstrap { + + public static void main(String[] args) throws IOException { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); + context.setConfigLocation("/META-INF/spring/dubbo-consumer-context.xml"); + context.refresh(); + System.out.println("DemoService consumer (XML) is starting..."); + DemoService demoService = context.getBean("demoService", DemoService.class); + for (int i = 0; i < 10; i++) { + System.out.println(demoService.sayName("Nacos")); + } + context.close(); + } +} +``` + +#### Run the XML configuration driven example + +Similarly, to start the first two `DemoServiceProviderXmlBootstrap` bootstrap class, observe Nacos registry service provider changes: + +![image-20181213125527201-4676927.png | left | 747x33](https://img.alicdn.com/tfs/TB1HCfbzMHqK1RjSZFgXXa7JXXa-2388-106.png "") + +XML configuration driven service version for `2.0.0`, therefore the registration service and correct. + +Again run service consumers leading class `DemoServiceConsumerXmlBootstrap`, watch the console output: + +``` +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +``` + +Results also runs and load balancing is normal, but because the current sample has yet to add attributes `demo.service.name` of therefore, "name" part of the information output `null`. + +If your attention or love Dubbo and Nacos open source project, as well as for their points of "star", related links: + +- Apache Dubbo:https://github.com/apache/dubbo +- Dubbo Nacos Registry:https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos +- Alibaba Nacos:https://github.com/alibaba/nacos diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-istio.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-istio.md new file mode 100644 index 00000000000..cd33fd67bab --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-istio.md @@ -0,0 +1,176 @@ +--- +title: Nacos push xDS with Istio +keywords: [Istio,xDs,Envoy] +description: Nacos push xDS with Istio +--- +# Istio Guide + +It supports CDS and EDS in xDS protocol, and realizes incremental push for EDS and MCP. Users can use Envoy or other XDS protocol-enabled clients to dock with Nacos for service discovery. + +## Configuration + +### Server + +For distribution packages: + +modify `nacos.istio.mcp.server.enabled` in `nacos/conf/application.properties` to true. + +For source code: + +modify `nacos.istio.mcp.server.enabled` in `nacos/distribution/conf/application.properties` to true. + +For incremental MCP: + +modify `nacos.istio.server.full ` in `nacos/istio/misc/IstioConfig` to false in addition to the above configuration. + +### Client + +In the following example, using Envoy, you can download the Envoy directly or create a mirror and mount the following configuration file. + +**Config** : the port number used can be changed on demand. + +```yaml +node: + cluster: test-cluster + id: test-idn + +admin: + address: + socket_address: { address: 0.0.0.0, port_value: 15000 } + +dynamic_resources: + ads_config: + api_type: GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: nacos_xds + cds_config: + ads: {} + lds_config: + path: /etc/envoy/lds.yaml + # ads: {} + +static_resources: + clusters: + - type: STATIC + connect_timeout: 1s + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: {} + name: nacos_xds + load_assignment: + cluster_name: nacos_xds + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 18848 +``` + +**lds** : when Envoy acquires a listening service, it automatically acquires EDS from the server. The listening service can change as needed. + +```yaml +resources: +- "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: listener_0 + address: + socket_address: { address: 0.0.0.0, port_value: 80 } + # listener_filters: + # - name: "envoy.filters.listener.tls_inspector" + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog + codec_type: AUTO + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: { prefix: "/" } + name: test + route: + cluster: outbound|8071||service-provider.DEFAULT-GROUP.e77d7925-1c90-4fa9-93cb-83153a099636.nacos + http_filters: + - name: envoy.filters.http.router +``` + +## RUN + +Note: each instance of the same service should use the same protocol, EDS default to use incremental push. + +1. Deploy Nacos, [ deployment reference ](https://nacos.io/en/docs/latest/quickstart/quick-start/); + +2. Modify the configuration in accordance with the above requirements; + +3. Start the server, the detailed start command can be seen in the above deployment reference; + + ```bash + bash startup.sh -m standalone -p embedded + ``` + +4. Start the client. + + ```bash + docker start envoy + ``` + +## CDS Example + +Note: The logs are viewed in nacos/logs/istio-main.log + +The service configuration registered in the example is as follows, [ example reference ](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example ). + +```properties +server.port=8071 +spring.application.name=service-provider +spring.cloud.nacos.discovery.namespace=e77d7925-1c90-4fa9-93cb-83153a099636 +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +![CDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341241-4e9b2dde-55c7-43ae-af1e-dc081565ab72.png) + +## EDS Example + +The service is configured as above. + +![EDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341176-fe312687-6488-41c2-bdd1-346d7a344bd2.png) + +## Full CDS Example + +The services are registered with the following configurations: + +```properties +#service-provider +server.port=8071 +spring.application.name=service-provider +spring.cloud.nacos.discovery.namespace=e77d7925-1c90-4fa9-93cb-83153a099636 +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 + +#service-consumer +server.port=8080 +spring.application.name=service-consumer +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +In the console, modify only the service-consumer service configuration, push as follows: + +![Full CDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341233-bc35de56-5653-4d5f-a510-819180dfe7f0.png) + +## Incremental EDS Example + +In the console, modify only the service-consumer instance configuration, push as follows: + +![Incremental EDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341234-aa195810-c76d-4ff5-977a-55626775e697.png) diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-k8s-sync.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-k8s-sync.md new file mode 100644 index 00000000000..e09006b216a --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-k8s-sync.md @@ -0,0 +1,37 @@ +--- +title: Nacos supports synchronizing metadata from K8S service discovery +keywords: [Nacos,k8s,kubernetes] +description: Nacos supports synchronizing metadata from K8S service discovery +--- + +# Nacos supports synchronizing metadata from K8S service discovery + +## Data synchronization +Nacos monitors the changes of services and instances in K8S, obtains its service metadata, and synchronizes the change information to Nacos' service discovery. Supports K8S version 1.22 (corresponding to K8S-Java-API version 14.0.0).The diagram is as follow: +![](img/k8s-sync.jpg) + +Mapping scheme of K8S resource synchronization to Nacos resource (single-direction, Nacos resource synchronization to K8S resource to be supplemented) : + +K8S Data to be synchronized|Field in K8S|Field mapped to Nacos +---|---|--- +service name|service.metadata.name|service.name +service targetPort(pod port)(multiple)|service.ports.targetPort|instance.port +service name|service.metadata.name|instance.cluster +service port(multiple)|service.ports.port|instance.extendData +pod ip|pod.status.hostIP / service.ipFamilies|instance.ip + +## Configuration file +Deploy the Nacos cluster according to [the deploy document](../guide/admin/deployment.md) + +Configure the application.properties file to enable K8S synchronization: +``` +nacos.k8s.sync.enabled=true +``` + +If you are using the Java API from an application outside the K8S cluster, you need to specify kubeConfig: +``` +nacos.k8s.sync.outsideCluster=true +nacos.k8s.sync.kubeConfig=/.kube/config +``` + +After configuration, services and instance changes in K8S are automatically synchronized to Nacos. diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-spring-boot.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-spring-boot.md new file mode 100644 index 00000000000..ac4b67104d8 --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-spring-boot.md @@ -0,0 +1,178 @@ +--- +title: Nacos with Spring Boot Projects +keywords: [Nacos,Spring Boot] +description: Quick Start for Nacos Spring Boot Projects +sidebar: + order: 3 +--- + +# Nacos with Spring Boot Projects + +This quick start introduces how to enable Nacos configuration management and service discovery features for your Spring Boot project. + +For more details about Nacos Spring Boot: [nacos-spring-boot-project](https://github.com/nacos-group/nacos-spring-boot-project/wiki/spring-boot-0.2.2-%E4%BB%A5%E5%8F%8A-0.1.2%E7%89%88%E6%9C%AC%E6%96%B0%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C). + +The quick start includes two samples: +* How to enable dynamic configuration updates with Nacos server and nacos-config-spring-boot-starter; +* How to enable service registration and discovery with Nacos Server and nacos-discovery-spring-boot-starter. + +## Prerequisite + +Follow instructions in [Nacos Quick Start](../quickstart/quick-start.mdx) to download Nacos and start the Nacos server. + +## Enable Configuration Service + +Once you start the Nacos server, you can follow the steps below to enable the Nacos configuration management service for your Spring Boot project. + +Sample project: [nacos-spring-boot-config-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-boot-example/nacos-spring-boot-config-example) + +1. Add the Nacos Spring Boot dependency. + +``` + + com.alibaba.boot + nacos-config-spring-boot-starter + ${latest.version} + +``` + +**Note**: Version [0.2.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-config-spring-boot-starter) is compatible with the Spring Boot 2.0.x line. Version [0.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-config-spring-boot-starter) is compatible with the Spring Boot 1.x line. + +2. Configure the Nacos server address in `application.properties` : + +``` +nacos.config.server-addr=127.0.0.1:8848 +``` + +3. Use `@NacosPropertySource` to load the configuration source whose `dataId` is `example` , and enable autorefresh of configuration updates: + +```plain +@SpringBootApplication +@NacosPropertySource(dataId = "example", autoRefreshed = true) +public class NacosConfigApplication { + + public static void main(String[] args) { + SpringApplication.run(NacosConfigApplication.class, args); + } +} +``` + +4. Specify the property value of the `@NacosValue` annotation of Nacos. + +``` +@Controller +@RequestMapping("config") +public class ConfigController { + + @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true) + private boolean useLocalCache; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public boolean get() { + return useLocalCache; + } +} +``` + +5. Start `NacosConfigApplication`and call `curl http://localhost:8080/config/get`. You will get a return message of `false`, as no configuration has been published so far. + +6. Call [Nacos Open API](../guide/user/open-api.md) to publish a configuration to the Nacos server. Assume the dataId is `example`, and the content is `useLocalCache=true`. + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example&group=DEFAULT_GROUP&content=useLocalCache=true" +``` + +7. Access `http://localhost:8080/config/get`again, and the returned value will be`true`,indicating that the value of `useLocalCache`in your application has been updated. + +## Enable Service Discovery + +Now you would also like to enable the service discovery feature of Nacos in your Spring Boot project. + +Sample project: [nacos-spring-boot-discovery-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example) + +1. Add the Nacos Spring Boot dependency. + +``` + + com.alibaba.boot + nacos-discovery-spring-boot-starter + ${latest.version} + +``` + +**Note**: Version [0.2.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-discovery-spring-boot-starter) is compatible with the Spring Boot 2.0.x line. Version [0.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-discovery-spring-boot-starter) is compatible with the Spring Boot 1.x line. + +2. Configure the Nacos server address in `application.properties` : + +``` +nacos.discovery.server-addr=127.0.0.1:8848 +``` + +3. Use `@NacosInjected` to inject a Nacos `NamingService` instance: + +``` +@Controller +@RequestMapping("discovery") +public class DiscoveryController { + + @NacosInjected + private NamingService namingService; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public List get(@RequestParam String serviceName) throws NacosException { + return namingService.getAllInstances(serviceName); + } +} + +@SpringBootApplication +public class NacosDiscoveryApplication { + + public static void main(String[] args) { + SpringApplication.run(NacosDiscoveryApplication.class, args); + } +} +``` + + +4. Start `NacosDiscoveryApplication`and call `curl http://localhost:8080/discovery/get?serviceName=example`,you will get a return value of an empty JSON array `[]`. + +5. Call [Nacos Open API](../guide/user/open-api.md) to register a service called `example` to the Nacos server. + +``` +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=example&ip=127.0.0.1&port=8080' +``` + +6. Access `curl http://localhost:8080/discovery/get?serviceName=example`again and you will get the following return: + +``` +[ + { + "instanceId": "127.0.0.1-8080-DEFAULT-example", + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "cluster": { + "serviceName": null, + "name": "", + "healthChecker": { + "type": "TCP" + }, + "defaultPort": 80, + "defaultCheckPort": 80, + "useIPPort4Check": true, + "metadata": {} + }, + "service": null, + "metadata": {} + } +] +``` + +## Related Projects +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-spring-cloud.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-spring-cloud.md new file mode 100644 index 00000000000..77df2cbe27c --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-spring-cloud.md @@ -0,0 +1,201 @@ +--- +title: Nacos with Spring Cloud Projects +keywords: [Nacos,Spring Cloud] +description: Quick Start for Nacos Spring Cloud Projects +sidebar: + order: 4 +--- + +# Nacos with Spring Cloud Projects + +This quick start introduces how to enable Nacos configuration management and service discovery features for your Spring Cloud project. + +For more details about Nacos Spring Cloud: [Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc) and [Nacos Discovery](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc). + +The quick start includes two samples: + +* How to enable dynamic configuration updates with Nacos server and spring-cloud-starter-alibaba-nacos-config; +* How to enable service registration and discovery with Nacos server and spring-cloud-starter-alibaba-nacos-discovery. + +## Prerequisite + +Follow instructions in [Nacos Quick Start](../quickstart/quick-start.mdx) to download Nacos and start the Nacos server. + +## Enable Configuration Service + +Once you start the Nacos server, you can follow the steps below to enable the Nacos configuration management service for your Spring Cloud project. + +Sample project: [nacos-spring-cloud-config-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-config-example) + +1. Add the Nacos Spring Cloud dependency. + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + ${latest.version} + +``` + +**Note**: Version [2.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config) is compatible with the Spring Boot 2.1.x line. Version [2.0.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config) is compatible with the Spring Boot 2.0.x line. Version [1.5.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config) is compatible with the Spring Boot 1.5.x line. + +2. Configure the Nacos Server address and Specify the application name in `bootstrap.properties` : + +``` +spring.cloud.nacos.config.server-addr=127.0.0.1:8848 + +spring.application.name=example +``` + +**Note**: The value of `spring.application.name` will be used to construct part of the dataId in Nacos configuration management. + +In Nacos Spring Cloud, the format of `dataId` is as follows: + +```plain +${prefix}-${spring.profiles.active}.${file-extension} +``` + +* The value of `prefix` is the value of `spring.application.name` by default. You can also configure this value in `spring.cloud.nacos.config.prefix`. +* `spring.profiles.active` is the profile of the current environment. For more details, refer to [Spring Boot Document](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles). + **Note: When the value of `spring.profiles.active` is empty, the corresponding hyphen `-` will be deleted, and the format of dataId becomes: `${prefix}.${file-extension}`** +* `file-exetension` is the data format of the configuration content, and can be configured in `spring.cloud.nacos.config.file-extension` . Currently only the `properties` and `yaml` type is supported. + +4. Add the native `@RefreshScope` annotation of Spring Cloud to enable autorefresh of configuration updates: + +``` +@RestController +@RequestMapping("/config") +@RefreshScope +public class ConfigController { + + @Value("${useLocalCache:false}") + private boolean useLocalCache; + + @RequestMapping("/get") + public boolean get() { + return useLocalCache; + } +} +``` + + +5. Call [Nacos Open API](../guide/user/open-api.md) to publish a configuration to the Nacos server. Assume the dataId is `example.properties`,and the content is `useLocalCache=true`. + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=true" +``` + +6. Run `NacosConfigApplication`and call `curl http://localhost:8080/config/get`,You will get a returned value of `true`. + +7. Call [Nacos Open API](../guide/user/open-api.md) again to publish an updated configuration to the Nacos server. Assume the dataId is`example.properties`,and the content is `useLocalCache=false`. + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=false" +``` + +8. Access `http://localhost:8080/config/get`again and the returned value became `false`,indicating that the value of `useLocalCache`in your application has been updated. + +## Enable Service Discovery + +Now you would also like to enable the service discovery feature of Nacos in your Spring Cloud project. + +Sample project: [nacos-spring-cloud-discovery-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example) + +1. Add the Nacos Spring Cloud dependency. + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${latest.version} + +``` + +**Note**: Version [2.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery) is compatible with the Spring Boot 2.1.x line. Version [2.0.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery) is compatible with the Spring Boot 2.0.x line. Version [1.5.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery) is compatible with the Spring Boot 1.5.x line. + +2. Configure the service provider, so that it can register its services to the Nacos server. + + i. Add the Nacos server address in `application.properties` : + +``` +server.port=8070 +spring.application.name=service-provider + +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +ii. Enable service discovery by adding the Spring Cloud native annotation of `@EnableDiscoveryClient`: + +``` +@SpringBootApplication +@EnableDiscoveryClient +public class NacosProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(NacosProviderApplication.class, args); + } + + @RestController + class EchoController { + @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET) + public String echo(@PathVariable String string) { + return "Hello Nacos Discovery " + string; + } + } +} +``` + + +3. Configure the service consumer so that it can discover the services that it would like to call on the Nacos server. + +i. Configure the Nacos server address in `application.properties` : + +``` +server.port=8080 +spring.application.name=service-consumer + +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +ii. Add the Spring Cloud native annotation of `@EnableDiscoveryClient` to enable service discovery. Add the `@LoadBalanced` annotation for the [RestTemplate](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-resttemplate.html) instance,  and enable the integration of `@LoadBalanced` and [Ribbon](https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html): + +``` +@SpringBootApplication +@EnableDiscoveryClient +public class NacosConsumerApplication { + + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + public static void main(String[] args) { + SpringApplication.run(NacosConsumerApplication.class, args); + } + + @RestController + public class TestController { + + private final RestTemplate restTemplate; + + @Autowired + public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;} + + @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET) + public String echo(@PathVariable String str) { + return restTemplate.getForObject("http://service-provider/echo/" + str, String.class); + } + } +} +``` + + +4. Start `ProviderApplication` and `ConsumerApplication` , and call `http://localhost:8080/echo/2018`. You will get a returned message of `Hello Nacos Discovery 2018`. + +## Related Projects + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) diff --git a/src/content/docs/v3.0/en/ecology/use-nacos-with-spring.md b/src/content/docs/v3.0/en/ecology/use-nacos-with-spring.md new file mode 100644 index 00000000000..1e6be1acc0d --- /dev/null +++ b/src/content/docs/v3.0/en/ecology/use-nacos-with-spring.md @@ -0,0 +1,455 @@ +--- +title: Nacos with Spring Projects +keywords: [Nacos,Spring,quick start] +description: This quick start introduces how to enable Nacos configuration management and service discovery features for your Spring project. +sidebar: + order: 2 +--- + +# Nacos with Spring Projects + +This quick start introduces how to enable Nacos configuration management and service discovery features for your Spring project. + +For more details about Nacos Spring Boot: [nacos-spring-project](https://github.com/nacos-group/nacos-spring-project/wiki/Nacos-Spring-Project-0.3.1-%E6%96%B0%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C). + +The quick start includes two samples: +* How to enable Nacos server and Nacos Spring configuration modules to implement dynamic configuration management; +* How to enable Nacos server and Nacos Spring service discovery modules to implement service registration and discovery. + +## Prerequisite + +Follow instructions in [Nacos Quick Start](../quickstart/quick-start.mdx) to download Nacos and start the Nacos server. + +## Enable Configuration Service + +Once you start the Nacos server, you can follow the steps below to enable the Nacos configuration management service for your Spring project. + +Sample project: [nacos-spring-config-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-config-example) + +1. Add the Nacos Spring dependency. + +``` + + com.alibaba.nacos + nacos-spring-context + ${latest.version} + +``` + +The the latest version can be available in maven repositories such as "[mvnrepository.com](https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-spring-context)". + +2. Add the `@EnableNacosConfig` annotation to enable the configuration service. In the code below, `@NacosPropertySource` is used to load the configuration source whose `dataId` is `example` , and autorefresh is also enabled: + +``` +@Configuration +@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) +@NacosPropertySource(dataId = "example", autoRefreshed = true) +public class NacosConfiguration { + +} +``` + +3. Specify the property value for the `@NacosValue` annotation of Nacos. + +``` +@Controller +@RequestMapping("config") +public class ConfigController { + + @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true) + private boolean useLocalCache; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public boolean get() { + return useLocalCache; + } +} +``` + +4. Start Tomcat and call `curl http://localhost:8080/config/get` to get configuration information. Because no configuration has been published, a `false`message is returned. + +5. Now you can call [Nacos Open API](../guide/user/open-api.md) to publish a configruation to the Nacos server. Assume the dataId is `example`, and content is `useLocalCache=true`. + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example&group=DEFAULT_GROUP&content=useLocalCache=true" +``` + +6. Access `http://localhost:8080/config/get`again, and you get a return message of `true`, indicating that the value of `useLocalCache`in your application has been updated. + +## Enable Service Discovery + +Now you would like to enable the service discovery function of Nacos in your Spring project. + +Sampe project: [nacos-spring-discovery-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-discovery-example) + +1. Add the Nacos Spring dependency. + +``` + + com.alibaba.nacos + nacos-spring-context + ${latest.version} + +``` + +The the latest version can be available in maven repositories such as "[mvnrepository.com](https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-spring-context)". + +2. Add the `@EnableNacosDiscovery` annotation to enable the service discovery function of Nacos: + +``` +@Configuration +@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) +public class NacosConfiguration { + +} +``` + +3. Use `@NacosInjected` to inject a Nacos `NamingService` instance: + +``` +@Controller +@RequestMapping("discovery") +public class DiscoveryController { + + @NacosInjected + private NamingService namingService; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public List get(@RequestParam String serviceName) throws NacosException { + return namingService.getAllInstances(serviceName); + } +} +``` + +4. Start Tomcat and call `curl http://localhost:8080/discovery/get?serviceName=example`, and the return value is an empty JSON array `[]`. + +5. Call [Nacos Open API](../guide/user/open-api.md) to register a service called `example`to the Nacos Server. + +``` +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=example&ip=127.0.0.1&port=8080' +``` + +6. Access `curl http://localhost:8080/discovery/get?serviceName=example`again, and you will get the following return: + +``` +[ + { + "instanceId": "127.0.0.1#8080#DEFAULT#example", + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "cluster": { + "serviceName": null, + "name": "", + "healthChecker": { + "type": "TCP" + }, + "defaultPort": 80, + "defaultCheckPort": 80, + "useIPPort4Check": true, + "metadata": {} + }, + "service": null, + "metadata": {} + } +] +``` + +# Nacos Spring Key Features + +This section provides a detailed description of the key features of [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project): + +- Annotation-Driven +- Dependency Injection +- Externalized Configuration +- Event-Driven + +## 1. Annotation-Driven + +### 1.1. Enable Nacos + +`@EnableNacos` is a modular-driven annotation that enables all features of Nacos Spring, including **Service Discovery** and **Distributed Configuration**. It equals to `@EnableNacosDiscovery` and +`@EnableNacosConfig`, which can be configured separately and used in different scenarios. + +### 1.2. Configure Change Listener method + +Suppose there was a config in Nacos Server whose `dataId` is "testDataId" and `groupId` is default group("DEFAULT_GROUP"). Now you would like to change its content by using the `ConfigService#publishConfig` method: + +```java +@NacosInjected +private ConfigService configService; + +@Test +public void testPublishConfig() throws NacosException { + configService.publishConfig(DATA_ID, DEFAULT_GROUP, "9527"); +} +``` + +Then you would like to add a listener, which will be listening for the config changes. You can do this by adding a config change listener method into your Spring Beans: + +```java +@NacosConfigListener(dataId = DATA_ID) +public void onMessage(String config) { + assertEquals("mercyblitz", config); // asserts true +} +``` + +The code below has the same effect: + +```java +configService.addListener(DATA_ID, DEFAULT_GROUP, new AbstractListener() { + @Override + public void receiveConfigInfo(String config) { + assertEquals("9527", config); // asserts true + } +}); +``` + +**Note:** `@NacosConfigListener` supports richer type conversions. + +- See [Simple Sample of `@NacosConfigListener`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/listener/SimpleNacosConfigListener.java) + +#### 1.2.1. Type Conversion + +The type conversion of `@NacosConfigListener` includes both build-in and customized implementations. By default, build-in type conversion is based on Spring `DefaultFormattingConversionService`, which means it covers most of the general cases as well as the rich features of the higher Spring framework. + +For example, the content "9527" in the preceding example can also be listened by a method with integer or the `Integer` argument: + +```java +@NacosConfigListener(dataId = DATA_ID) +public void onInteger(Integer value) { + assertEquals(Integer.valueOf(9527), value); // asserts true +} + +@NacosConfigListener(dataId = DATA_ID) +public void onInt(int value) { + assertEquals(9527, value); // asserts true +} +``` + +Of course, [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) provides elastic extension for developers. If you define a named `nacosConfigConversionService` Spring Bean whose type is `ConversionService` , the `DefaultFormattingConversionService` will be ignored. In addition, you can customize the implementation of the `NacosConfigConverter` interface to specify a listener method for type conversion: + +```java +public class UserNacosConfigConverter implements NacosConfigConverter { + + @Override + public boolean canConvert(Class targetType) { + return true; + } + + @Override + public User convert(String source) { + return JSON.parseObject(source, User.class); + } +} +``` + +The `UserNacosConfigConverter` class binds the `@NacosConfigListener.converter()` attribute: + +```java +@NacosInjected +private ConfigService configService; + +@Test +public void testPublishUser() throws NacosException { + configService.publishConfig("user", DEFAULT_GROUP, "{\"id\":1,\"name\":\"mercyblitz\"}"); +} + +@NacosConfigListener(dataId = "user", converter = UserNacosConfigConverter.class) +public void onUser(User user) { + assertEquals(Long.valueOf(1L), user.getId()); + assertEquals("mercyblitz", user.getName()); +} +``` + +- See [Type Conversion Sample of `@NacosConfigListener`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/listener/PojoNacosConfigListener.java) + +#### 1.2.2. Timeout of Execution + +As it might cost some time to run customized `NacosConfigConverter`, you can set max execution time in the `@NacosConfigListener.timeout()` attribute to prevent it from blocking other listeners: + +```java +@Configuration +public class Listeners { + + private Integer integerValue; + + private Double doubleValue; + + @NacosConfigListener(dataId = DATA_ID, timeout = 50) + public void onInteger(Integer value) throws Exception { + Thread.sleep(100); // timeout of execution + this.integerValue = value; + } + + @NacosConfigListener(dataId = DATA_ID, timeout = 200) + public void onDouble(Double value) throws Exception { + Thread.sleep(100); // normal execution + this.doubleValue = value; + } + + public Integer getIntegerValue() { + return integerValue; + } + + public Double getDoubleValue() { + return doubleValue; + } +} +``` + +The `integerValue` of `Listeners` Bean is always `null` and will not be changed. Therefore, those asserts will be `true`: + +```java +@Autowired +private Listeners listeners; + +@Test +public void testPublishConfig() throws NacosException { + configService.publishConfig(DATA_ID, DEFAULT_GROUP, "9527"); + assertNull(listeners.getIntegerValue()); // asserts true + assertEquals(Double.valueOf(9527), listeners.getDoubleValue()); // asserts true +} +``` + +- See [Timeout Sample of `@NacosConfigListener`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/listener/TimeoutNacosConfigListener.java) + +### 1.3. Global and Special Nacos Properties + +The `globalProperties` is a required attribute in any `@EnableNacos`, `@EnableNacosDiscovery` or `@EnableNacosConfig`, and its type is `@NacosProperties`. `globalProperties` initializes "**Global Nacos Properties**" that will be used by other annotations +and components, e,g `@NacosInjected`. In other words, **Global Nacos Properties**" defines the global and default properties. It is set with the lowest priority and can be overridden if needed. The precedence of overiding rules is shown in the following table: + +| Precedence Order | Nacos Annotation | Required | +| ---------------- | ------------------------------------------------------------ | -------- | +| 1 | `*.properties()` | N | +| 2 | `@EnableNacosConfig.globalProperties()` or `@EnableNacosDiscovery.globalProperties()` | Y | +| 3 | `@EnableNacos.globalProperties()` | Y | + + +`*.properties()` defines special Nacos properties which come from one of the following: + +- `@NacosInjected.properties()` +- `@NacosConfigListener.properties()` +- `@NacosPropertySource.properties()` +- `@NacosConfigurationProperties.properties()` + +Special Nacos properties are also configured by `@NacosProperties`. However, they are optional and are used to override Global Nacos Properties in special scenarios. If not defined, the Nacos Properties will +try to retrieve properities from `@EnableNacosConfig.globalProperties()` or `@EnableNacosDiscovery.globalProperties()`, or +`@EnableNacos.globalProperties()`. + +### 1.4. `@NacosProperties` + +`@NacosProperties` is a uniform annotation for global and special Nacos properties. It serves as a mediator between Java `Properties` and `NacosFactory` class. `NacosFactory` is responsible for creating `ConfigService` or `NamingService` instances. + +The attributes of `@NacosProperties` completely support placeholders whose source is all kinds of `PropertySource` in Spring `Environment` abstraction, typically Java System `Properties` and OS environment variables. The prefix of all placeholders are `nacos.`. The mapping between the attributes of `@NacosProperties` and Nacos properties are shown below: + +| Attribute | Property | Placeholder | Description | Required | +| --------------- | -------------- | ------------------------ | ----------- | --------- | +| `endpoint()` | `endpoint` | `${nacos.endpoint:}` | | N | +| `namespace()` | `namespace` | `${nacos.namespace:}` | | N | +| `accessKey()` | `access-key` | `${nacos.access-key:}` | | N | +| `secretKey()` | `secret-key` | `${nacos.secret-key:}` | | N | +| `serverAddr()` | `server-addr` | `${nacos.server-addr:}` | | Y | +| `contextPath()` | `context-path` | `${nacos.context-path:}` | | N | +| `clusterName()` | `cluster-name` | `${nacos.cluster-name:}` | | N | +| `encode()` | `encode` | `${nacos.encode:UTF-8}` | | N | + + +Note that there are some differences in the placeholders of `globalProperties()` between `@EnableNacosDiscovery` and `@EnableNacosConfig`: + + +| Attribute | `@EnableNacosDiscovery`'s Placeholder |`@EnableNacosConfig`'s Placeholder | +| --------------- | -------------------------------------------------------- | ------------------------------------------------- | +| `endpoint()` | `${nacos.discovery.endpoint:${nacos.endpoint:}}` |`${nacos.config.endpoint:${nacos.endpoint:}}` | +| `namespace()` | `${nacos.discovery.namespace:${nacos.namespace:}}` |`${nacos.config.namespace:${nacos.namespace:}}` | +| `accessKey()` | `${nacos.discovery.access-key:${nacos.access-key:}}` |`${nacos.config.access-key:${nacos.access-key:}}` | +| `secretKey()` | `${nacos.discovery.secret-key:${nacos.secret-key:}}` |`${nacos.config.secret-key:${nacos.secret-key:}}` | +| `serverAddr()` | `${nacos.discovery.server-addr:${nacos.server-addr:}}` | `${nacos.config.server-addr:${nacos.server-addr:}}` | +| `contextPath()` | `${nacos.discovery.context-path:${nacos.context-path:}}` | `${nacos.config.context-path:${nacos.context-path:}}` | +| `clusterName()` | `${nacos.discovery.cluster-name:${nacos.cluster-name:}}` |`${nacos.config.cluster-name:${nacos.cluster-name:}}` | +| `encode()` | `${nacos.discovery.encode:${nacos.encode:UTF-8}}` |`${nacos.config.encode:${nacos.encode:UTF-8}}` | + +These placeholders of `@EnableNacosDiscovery` and `@EnableNacosConfig` are designed to isolate different Nacos servers, and are unnecessary in most scenarios. By default, general placeholders will be reused. + +## 2. Dependency Injection + +`@NacosInjected` is a core annotation which is used to inject `ConfigService` or `NamingService` instance in your Spring Beans and make these instances **cacheable**. This means the instances will be the same if their `@NacosProperties` are equal, regargless of whether the properties come from global or special Nacos properties: + +```java +@NacosInjected +private ConfigService configService; + +@NacosInjected(properties = @NacosProperties(encode = "UTF-8")) +private ConfigService configService2; + +@NacosInjected(properties = @NacosProperties(encode = "GBK")) +private ConfigService configService3; + +@NacosInjected +private NamingService namingService; + +@NacosInjected(properties = @NacosProperties(encode = "UTF-8")) +private NamingService namingService2; + +@NacosInjected(properties = @NacosProperties(encode = "GBK")) +private NamingService namingService3; + +@Test +public void testInjection() { + + Assert.assertEquals(configService, configService2); + Assert.assertNotEquals(configService2, configService3); + + Assert.assertEquals(namingService, namingService2); + Assert.assertNotEquals(namingService2, namingService3); +} +``` + +The property `configService` uses `@EnableNacos#globalProperties()` or `@EnableNacosConfig#globalProperties()`, and because the default value of the `encode` attribute is "UTF-8", therefore the `configService` instance and the `configService2` instance which is annotated by `@NacosProperties(encode = "UTF-8")` are the same. The same is true for `namingService` and `namingService2`. + +More importantly, unlike the `ConfigService` instances created by the `NacosFactory.createConfigService()` method, the `ConfigService` instances created by the `@NacosInjected` annotation support Nacos Spring events. For instance, there will be an `NacosConfigPublishedEvent` after an enhanced `ConfigService` invokes the `publishConfig()` method. Refer to the [Event/Listener Driven](#eventlistener-driven) section for more details. + +- See [Dependency Injection Sample](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/NacosConfiguration.java) + +## 3. Externalized Configuration + +Externalized configuration is a concept introduced by Spring Boot, which allows applications to receive external property sources to control runtime behavior. Nacos Server runs an isolation process outside the application to maintain the application configurations. [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) provides properties features including object binding, dynamic configuration(auto-refreshed) and so on, and dependence on Spring Boot or Spring Cloud framework is required. + +Here is a simple comparison between [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) and Spring stack: + +| Spring Stack | Nacos Spring | Highlight | +| -------------------------- | ------------------------------- | ---------------------------------------------- | +| `@Value` | `@NacosValue` | auto-refreshed | +| `@ConfigurationProperties` | `@NacosConfigurationProperties` | auto-refreshed,`@NacosProperty`,`@NacosIgnore` | +| `@PropertySource` | `@NacosPropertySource` | auto-refreshed, precedence order control | +| `@PropertySources` | `@NacosPropertySources` | | + +- See [Auto-Refreshed Sample of `@NacosConfigurationProperties`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/properties/NacosConfigurationPropertiesConfiguration.java) + +- See [Sample of `@NacosPropertySources` and `@NacosPropertySource`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/env/NacosPropertySourceConfiguration.java) + +## 4. Event/Listener Driven + +Nacos Event/Listener Driven is based on the standard Spring Event/Listener mechanism. The `ApplicationEvent` of Spring is an abstract super class for all Nacos Spring events: + +| Nacos Spring Event | Trigger | +| -------------------------------------------- | ------------------------------------------------------------ | +| `NacosConfigPublishedEvent` | After `ConfigService.publishConfig()` | +| `NacosConfigReceivedEvent` | After`Listener.receiveConfigInfo()` | +| `NacosConfigRemovedEvent` | After `configService.removeConfig()` | +| `NacosConfigTimeoutEvent` | `ConfigService.getConfig()` on timeout | +| `NacosConfigListenerRegisteredEvent` | After `ConfigService.addListner()` or `ConfigService.removeListener()` | +| `NacosConfigurationPropertiesBeanBoundEvent` | After `@NacosConfigurationProperties` binding | +| `NacosConfigMetadataEvent` | After Nacos Config operations | + +- See [Event/Listener Sample](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/event/NacosEventListenerConfiguration.java) + +# Related Projects + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) diff --git a/src/content/docs/v3.0/en/guide/admin/cluster-mode-quick-start.md b/src/content/docs/v3.0/en/guide/admin/cluster-mode-quick-start.md new file mode 100644 index 00000000000..dd50f34e954 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/cluster-mode-quick-start.md @@ -0,0 +1,165 @@ +--- +title: Cluster deployment instructions +keywords: [Cluster,deployment] +description: Cluster deployment instructions +sidebar: + order: 2 +--- + +# Cluster deployment instructions + +> Document optimizing... + +## Cluster Mode Deployment + +This Quick Start Manual is to help you quickly download, install and use Nacos on your computer to deploy the cluster mode for production use. + +### Cluster Deployment Architecture + +Therefore, when it is open source, it is recommended that users put all server lists under a vip and then hang under a domain name. + +Http://ip1:port/openAPI Directly connected to ip mode, the machine needs to be modified to use ip. + +Http://SLB:port/openAPI Mount the SLB mode(Intranet, do not expose internet to avoid security risks), directly connect to SLB, the following server ip real ip, readability is not good. + +Http://nacos.com:port/openAPI Domain name + SLB mode(Intranet, do not expose internet to avoid security risks), good readability, and easy to change ip, recommended mode + +![deployDnsVipMode.jpg](/img/deployDnsVipMode.jpg) + +## 1. Preparing for the Environment + +Make sure that it is installed and used in the environment: + +1. 64 bit OS Linux/Unix/Mac, recommended Linux system. +2. 64 bit JDK 1.8+; [Download](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). [Configuration](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javome_t/). +3. Maven 3.2.x+; [Download](https://maven.apache.org/download.cgi). [Configuration](https://maven.apache.org/settings.html). +4. 3 or more Nacos Nodes; + +## 2. Download source code or installation package + +You can get Nacos in two ways. + +### Download source code from Github + +```bash +unzip nacos-source.zip +cd nacos/ +mvn -Prelease-nacos clean install -U +cd nacos/distribution/target/nacos-server-1.3.0/nacos/bin +``` + +### Download Compressed Packet after Compilation + +Download address + +Select the latest stable version from [releases](https://github.com/alibaba/nacos/releases) and download `nacos-server-$version.zip` or `nacos-server-$version.tar.gz` + +```bash + unzip nacos-server-$version.zip OR tar -xvf nacos-server-$version.tar.gz + cd nacos/bin +``` + +## 3. Configuration Cluster Profile + +In the Nacos decompression directory Nacos / conf directory, there is a configuration file cluster. conf, please configure each line as ip: port. + +```plain +# ip:port +200.8.9.16:8848 +200.8.9.17:8848 +200.8.9.18:8848 +``` + +### 3.1 Open Default auth plugin (Optional) + +Then Setting configuration file `application.properties` under `conf`. + +Setting + +```properties +nacos.core.auth.enabled=true +nacos.core.auth.system.type=nacos +nacos.core.auth.plugin.nacos.token.secret.key=${custom, make sure same in all nodes} +nacos.core.auth.server.identity.key=${custom, make sure same in all nodes} +nacos.core.auth.server.identity.value=${custom, make sure same in all nodes} +``` +Detail see [Authentication](../../plugin/auth-plugin.md). + +> Attention,Default value in Document `SecretKey012345678901234567890123456789012345678901234567890123456789` and `VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=` is a public default, **only** should use in test temporary. Please **make sure** to replace it with another valid value when you actually deploy. + +## 4. Determine The DataSource + +### Using built-in data sources + +No configuration is required + +### Use an external data source + + +production and use recommendations at least backup mode, or high availability database. + +#### Initializes the MySQL database + +[sql statement source file](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +### application. properties configuration + +[application.properties configuration file](https://github.com/alibaba/nacos/blob/master/distribution/conf/application.properties) + +## 5. start server + +### Linux/Unix/Mac + +#### Standalone mode + +```bash +sh startup.sh -m standalone +``` + +#### Cluster mode + +> Using built-in data sources + +```bash +sh startup.sh -p embedded +``` + +> Use an external data source + +```bash +sh startup.sh +``` + +## 6. Service Registration & Discovery and Configuration Management + +### Service registration + +`curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'` + +> Attention: If open default auth plugin, please call with username and password in header. + +### Service discovery + +`curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'` + +> Attention: If open default auth plugin, please call with username and password in header. + +### Publish configuration + +`curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld"` + +> Attention: If open default auth plugin, please call with username and password in header. + +### get configuration + +`curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"` + +> Attention: If open default auth plugin, please call with username and password in header. + +## 7. shut down server + +### Linux/Unix/Mac + +```bash +sh shutdown.sh +``` diff --git a/src/content/docs/v3.0/en/guide/admin/console-guide.md b/src/content/docs/v3.0/en/guide/admin/console-guide.md new file mode 100644 index 00000000000..45a31d4a109 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/console-guide.md @@ -0,0 +1,181 @@ +--- +title: Console Guide +keywords: [console,guide] +description: Nacos console aims to enhance the console for service list, health management, service management, a distributed configuration management control ability. +sidebar: + order: 5 +--- + +# Console Guide + +[Nacos console](http://console.nacos.io/nacos/index.html) aims to enhance the console for service list, health management, service management, a distributed configuration management control ability, in order to help users reduce the cost of micro management service application architecture, will provide basic functions include the following: + +* Service management + * Service list and health status display + * Service metadata storage and editing + * Service flow weight adjustment + * Service elegant line up and down +* Configuration management + * More configuration format editing + * Edit DIFF + * Sample code + * Push status query + * Configure version and rolled back +* Namespace +* Login management + +## Features + +### Service management + +Developer or operations staff often require after service registry, through friendly interface to view the service registration situation, the current system, including the registration of all of the details of the services and each service.And in a case, with access control service of some of the configuration editor.Nacos in this version of open service found that part of the console, main is to provide users a basic operations page, to view, edit, the current registration services. + +#### Service list management + +Service list to help users with a unified view management of all its service and health status.The overall layout is the upper left corner services and search box to search button, the page is the central service list.Service main display service name list, the cluster number, number of instances, health instance number and details button five columns. + +![image.png | left | 747x281](https://cdn.nlark.com/lark/0/2018/png/15356/1540536911804-3660f0e9-855f-4439-ac23-e76f6f644360.png "") + +In the service list page click details, you can see details of the service.Can look at the service, the basic information of the cluster and examples. + +#### Service flow weighted support and protection + +Nacos flow provides the user with the ability of weight control, open the threshold of service flow protection at the same time, in order to help users better protection service cluster service providers are not accidentally break.The diagram below so, click the edit button instance, modify instance weights.If you want to increase the flow of instance, to turn up the weight, if you don't want to flow method receives the instance, the weight can be set to 0. + +![image.png | left | 747x266](https://cdn.nlark.com/lark/0/2018/png/15356/1540537029452-dffbb078-4ae5-4397-9f70-083e0ebbb5be.png "") + +#### Service metadata management + +Nacos provide multiple dimensions of service metadata exposed, help users to store the information of the custom.This information is based on data storage structure, K - V on the console, as to the k1 = v1, k2 = v2 show such format.Similarly, edit the metadata can be performed by the same format.Such as service metadata editing, first click on the service details in the top right corner of the page "edit service" button, and then in the metadata input: input box version = 1.0, env = prod. + +![image.png | left | 747x271](https://cdn.nlark.com/lark/0/2018/png/15356/1540537359751-217d7500-c19c-4bad-8508-27f347f48a2f.png "") + +Click on the confirmation, you can in the service details page, see the service metadata has been updated. + +![image.png | left | 747x145](https://cdn.nlark.com/lark/0/2018/png/15356/1540537452673-01dc6c92-329a-4b6f-a616-36dc546c3355.png "") + +#### Service elegant line up and down + +Nacos also offers the service instance line operation, up and down in the service details page, you can click on the instance of "on-line" or "off" button, the offline instance, cases of health will not be included in the list. + +![image.png | left | 747x142](https://cdn.nlark.com/lark/0/2018/png/15356/1540537640435-b28cb279-75af-4965-8a9a-54cee213f1a5.png "") + +### Configuration management + +Nacos support Group configuration based on the Namespace and Group management, so that users more flexible according to their own needs in accordance with the environment or application, module, such as grouping management services as well as the configuration of Spring, in the configuration management major provides configuration version history, rollback, subscriber query such as the core management abilities. + +![image.png | left | 747x297](https://cdn.nlark.com/lark/0/2018/png/9687/1540458893745-219a46a8-ebd9-405b-9e8f-226f3f0c7e76.png "") + +#### More configuration format editor + +Nacos support YAML, Properties, TEXT, JSON, XML, HTML and other common configuration format online editing, syntax highlighting, format check, help users efficiently edit at the same time greatly reduced the risks of format error. + +Nacos support configuration tag ability, help users better and more flexible to the configuration of the classification and management based on the tag.Description of configuration and its change is support users at the same time, people or cross team collaboration management configuration. + +![image.png | left | 747x426](https://cdn.nlark.com/lark/0/2018/png/9687/1540458995051-b3e67fd4-c905-4552-9e52-f54b6ef59941.png "") + +#### Edit DIFF + +Nacos supports editing a DIFF ability, help the user to check the changes, and reduce the risks of correction. + +![image.png | left | 747x338](https://cdn.nlark.com/lark/0/2018/png/9687/1540457990344-a60e1db3-ca1a-47ed-a03e-f92e37745247.png "") + +#### Sample code + +Nacos provide sample code ability, can let a novice quickly using client-side programming consumption this configuration, novice slash barriers. + +![image.png | left | 747x223](https://cdn.nlark.com/lark/0/2018/png/9687/1540456991412-01acc11c-8b48-48d8-9032-589ebb9388d9.png "") + +![image.png | left | 747x380](https://cdn.nlark.com/lark/0/2018/png/9687/1540532899571-ccea6b6f-a1e1-44d1-a130-f9afaba01c51.png "") + +#### Listener query + +Nacos provide configuration subscriber is the listener query ability, at the same time provide Client MD5 checksum value of the current configuration, in order to help users better check configuration changes pushed to the Client side. + +![image.png | left | 747x185](https://cdn.nlark.com/lark/0/2018/png/9687/1540459212236-0abdc558-68b9-4585-b11e-c9a1924ce7ef.png "") + +#### Configure version and rolled back + +Nacos by providing a key roll back configuration version management and its ability, help users can configure to quick recovery, reduce the micro service system in configuration management will meet the availability of the risk. + +![image.png | left | 747x242](https://cdn.nlark.com/lark/0/2018/png/9687/1540459226967-a258b9a7-f95f-41b0-874f-2a0a5da2fc5c.png "") + +![image.png | left | 747x493](https://cdn.nlark.com/lark/0/2018/png/9687/1540459237821-d4c06d16-b356-4953-a6e7-da949b1f3aec.png "") + +## Namespace management + +Nacos based in Namespace helps users logic isolation based multiple namespaces, this can help users better management testing, service and configure the pretest, production environment, so that the same configuration environment (such as database data sources) can define different values. + +![image.png | left | 747x298](https://cdn.nlark.com/lark/0/2018/png/9687/1540519411777-74908cc2-29bc-4270-be58-aed62605228f.png "") + +![image.png | left | 747x206](https://cdn.nlark.com/lark/0/2018/png/9687/1540519427066-effd5153-02c9-4e21-ae9f-1a2e9ae7713e.png "") + +## Login management + +Nacos 0.8 version supports simple login function, the default username/password for: `nacos/nacos`. + +![login](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561262748106-4fc05174-bf70-4806-bcbd-90296c5bcbaa.jpeg) + +### Change the default username/password method + +1. Generate encrypted password in `com.alibaba.nacos.console.utils.PasswordEncoderUtil.main` function, change nacos to you want to change the password, running with encryption algorithm.Note that salt is random, so the generated password every time may be different, please don't worry about it. + +``` +public class PasswordEncoderUtil { + + public static void main(String[] args) { + System.out.println(new BCryptPasswordEncoder().encode("nacos")); + } +} +``` + +2. Create a user name or password, use specify a user name password. +``` +INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); +INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN'); +``` + +### Close the login function + +As part of its own development console, do not want to be nacos security filter interceptor.Therefore nacos support custom close the login functionFind the configuration file `${nacoshome}/conf/application.properties`. The properties, replace the following content. + +``` +## spring security config +### turn off security +spring.security.enabled=false +management.security=false +security.basic.enabled=false +nacos.security.ignore.urls=/** + +#nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/** + +``` + +### Session time + +The default session to keep time for 30 minutes.After 30 minutes need to login authentication.Temporarily does not support to modify the default time. + +## Community participation in the front end of the building + +In Nacos front style, the layout of the discussion, the community vote, finally choose the style of the classic black and white and blue skin, and through our UED Yao Cheng design, layout, make interaction is very natural. + +In the development of the console, we recruited through community many front students to participate in the development of the front-end code, in this especially thank Chen Li, Qing Wang, Yanmin Wang Nacos front-end development process in the strong support! + +## Adhere to the community development, welcome to join and contribute to the community + +> DISS is cheap, show me your hand! + +To join Nacos WeChat community discussion Nacos the evolution of the product, you can sweep through **xuechaos** WeChat QRcode, let "xuechaos" help you pull in "Nacos community communication group". + +![Screen Shot 2018-06-27 at 13.39.09.png | left](https://cdn.yuque.com/lark/0/2018/png/15914/1530077965587-8f4e3100-bdd4-469a-9ea0-7af7061bc9ef.png "") + +More Nacos related open source project information: + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring Project](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) +* [Dubbo](https://github.com/apache/dubbo) +* [Sentinel](https://github.com/alibaba/Sentinel) +* [Spring Cloud](https://projects.spring.io/spring-cloud/) +* [Nepxion Discovery](https://github.com/Nepxion/Discovery) diff --git a/src/content/docs/v3.0/en/guide/admin/deployment.md b/src/content/docs/v3.0/en/guide/admin/deployment.md new file mode 100644 index 00000000000..89283227912 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/deployment.md @@ -0,0 +1,100 @@ +--- +title: Nacos supports three types of deployment modes +keywords: [Nacos,deployment modes] +description: Nacos supports three types of deployment modes +sidebar: + order: 1 +--- + +> Document optimizing... + +# Nacos deployment environment + +Nacos is defined as an IDC internal application component, not a product for the public network environment. It is not recommended expose it to the public network environment directly. + +All network related concepts such as VIP and network interface mentioned in the following documents are in the **internal network environment**. + +# Nacos supports three types of deployment modes + +* Standalone Mode - used in DEV or TEST environment. +* Cluster Mode - used in production environment to ensure high-availability. +* Multi-Cluster Mode - in complicated production mode, you may want to deploy multi-cluster mode to support different business units. + +# Environment preparation +- JDK installed, 1.8 and above are required +- Recommendation: 2 core CPU / 4G RAM and above +- Recommendation: Production environment with 3 nodes and above + +## Running Nacos in Standalone Mode + +### Linux/Unix/Mac + +* Standalone means it is non-cluster Mode. * +sh startup.sh -m standalone + +### Windows + +* Standalone means it is non-cluster Mode. * +cmd startup.cmd -m standalone + +### Running Nacos with mysql in Standalone Mode + +#### Initialize MySQL database + +[sql statement source file](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +#### application.properties configuration + +[application.properties configuration file](https://github.com/alibaba/nacos/blob/master/distribution/conf/application.properties) + +add mysql datasource and configure url, user and password + +``` +spring.datasource.platform=mysql + +db.num=1 +db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true +db.user=nacos_devtest +db.password=youdontknow +``` + +## Running Nacos in Multi-Node Cluster Mode + +[Nacos in Multi-Node Cluster Mode](./cluster-mode-quick-start.md) + + +## Deploy Nacos in Multi-Cluster Mode + +Nacos support a NameServer route request mode, by which you can design a useful mapping rule to control the request forward to the corresponding cluster, in the mapping rule you can sharding the request by namespace or by tenant etc... + +to setup a NameServer: + +## IP Selection of Multiple Network Cards + +When the local environment is complex, the Nacos service needs to choose IP or network card to use at runtime when it starts up. Nacos Gets IP Reference Spring Cloud Design from Multiple Network Cards. With the nacos.inetutils parameter, you can specify the network card and IP address used by Nacos. The configuration parameters currently supported are: + +- ip-address parameter can set Nacos IP directly + +``` +nacos.inetutils.ip-address=10.11.105.155 +``` + +- use-only-site-local-interfaces parameter allows Nacos to use LAN ip, which is useful when Nacos deploys a machine with multiple network cards + +``` +nacos.inetutils.use-only-site-local-interfaces=true +``` + +- ignored-interfaces parameter support network card arrays, allowing Nacos to ignore multiple network cards + +``` +nacos.inetutils.ignored-interfaces[0]=eth0 +nacos.inetutils.ignored-interfaces[1]=eth1 +``` + +- preferred-networks parameter allow Nacos to select the matching IP preferentially and support regular matching and prefix matching + +``` +nacos.inetutils.preferred-networks[0]=30.5.124. +nacos.inetutils.preferred-networks[0]=30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))),30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))) +``` diff --git a/src/content/docs/v3.0/en/guide/admin/management-api.md b/src/content/docs/v3.0/en/guide/admin/management-api.md new file mode 100644 index 00000000000..7672c8dc1fb --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/management-api.md @@ -0,0 +1,11 @@ +--- +title: Management API +keywords: [Management,API] +description: In plan with Nacos 1.x.x +sidebar: + order: 4 +--- + +> Document optimizing... + +TODO diff --git a/src/content/docs/v3.0/en/guide/admin/monitor-guide.md b/src/content/docs/v3.0/en/guide/admin/monitor-guide.md new file mode 100644 index 00000000000..433cb9bfda0 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/monitor-guide.md @@ -0,0 +1,249 @@ +--- +title: Nacos monitor guide +keywords: [Nacos,monitor guide] +description: Nacos monitor guide +sidebar: + order: 6 +--- + +# Nacos monitor guide + +> Document optimizing... + +Nacos 0.8.0 improves the monitoring system, supporting Nacos operation status monitoring through exposing metrics data access to third-party monitoring system. Currently, prometheus, elastic search and influxdb are supported. The docs introduce how prometheus and grafana monitor Nacos. +You can find out for yourself how to use elastic search and influxdb. + +## Deploy Nacos cluster to expose metrics data + +Deploy the Nacos cluster according to [the deploy document](./deployment.md) + +Configure the application. properties file to expose metrics data +``` +management.endpoints.web.exposure.include=* +``` + +Access {ip}:8848/nacos/actuator/prometheus to see if metrics data can be accessed + +## Deploy prometheus to collect Nacos metrics data +Download the Prometheus version you want to install at the address of [download prometheus](https://prometheus.io/download/) + +### linux & mac +Decompress prometheus compression package +``` +tar xvfz prometheus-*.tar.gz +cd prometheus-* +``` + +Modify configuration file prometheus.yml to collect Nacos metrics data +``` + metrics_path: '/nacos/actuator/prometheus' + static_configs: + - targets: ['{ip1}:8848','{ip2}:8848','{ip3}:8848'] +``` + +Start prometheus service +``` +./prometheus --config.file="prometheus.yml" +``` + +### windows + +Download the corresponding version of Windows and decompress it + +Modify configuration file prometheus.yml to collect Nacos metrics data +``` + metrics_path: '/nacos/actuator/prometheus' + static_configs: + - targets:['{ip1}:8848','{ip2}:8848','{ip3}:8848'] +``` + +Start prometheus service + +``` +prometheus.exe --config.file=prometheus.yml +``` + +By accessing http://{ip}:9090/graph, we can see the data collected by prometheus. By searching nacos_monitor in the search bar, we can find Nacos data to show the success of the data collection. +![IMAGE](https://img.alicdn.com/tfs/TB1LThVCQvoK1RjSZFwXXciCFXa-2832-1576.png) + +## Deploy grafana to graphically display metrics data +Install grafana on the same machine as prometheus, and use yum to install grafana + +### mac + +``` +brew install grafana +brew services start grafana +``` + +### linux +``` +sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.2.4-1.x86_64.rpm +sudo service grafana-server start +``` + +### windows +Reference document:http://docs.grafana.org/installation/windows/ + + +Access grafana: http://{ip}:3000 + + +Configuring prometheus data source +![IMAGE](https://img.alicdn.com/tfs/TB1bTafCOLaK1RjSZFxXXamPFXa-2832-1568.png) + +Import Nacos grafana monitoring [template](https://github.com/nacos-group/nacos-template/blob/master/nacos-grafana.json) +![IMAGE](https://img.alicdn.com/tfs/TB1JadVCPDpK1RjSZFrXXa78VXa-2742-1338.png) + +Nacos monitoring is divided into three modules: +- nacos monitor shows core monitoring items +![IMAGE](https://img.alicdn.com/tfs/TB1PMpUCQvoK1RjSZFDXXXY3pXa-2832-1584.png) +- nacos detail shows the change curve of index +![IMAGE](https://img.alicdn.com/tfs/TB1ZBF4CNjaK1RjSZFAXXbdLFXa-2742-1480.png) +- nacos alert is alerts about nacos +![IMAGE](https://img.alicdn.com/tfs/TB1ALlUCFzqK1RjSZFCXXbbxVXa-2742-1476.png) + +## configure grafana alert + +When Nacos runs out of order, Grafana can alert the person in charge. Grafana supports a variety of police alert. Mail, DingTalk and webhook are commonly used. + +### DingTalk alert +Configure DingTalk robots +![IMAGE](https://img.alicdn.com/tfs/TB1eJ0RCSzqK1RjSZFjXXblCFXa-2742-1482.png) + +Configure DingTalk robots url +![IMAGE](https://img.alicdn.com/tfs/TB1ERtQCSzqK1RjSZFjXXblCFXa-2832-1578.png) + +Test alert +![IMAGE](https://img.alicdn.com/tfs/TB1KvXPCHPpK1RjSZFFXXa5PpXa-996-504.png) + +### mail alert + +Modify defaults.ini configuration file to add mail alerts + +``` +#################################### SMTP / Emailing ########################## +[smtp] +enabled = true +host = smtp.126.com:25 +user = xxxxxx +password = xxxxx +;cert_file = +;key_file = +skip_verify = true +from_address = xxxxxx@126.com + +[emails] +;welcome_email_on_sign_up = false +``` + +Configuration notification mailbox +![IMAGE](https://img.alicdn.com/tfs/TB12qyhCNnaK1RjSZFtXXbC2VXa-2832-1576.png) + + +## meaning of Nacos metrics + +### jvm metrics + +item|meaning +---|--- +system_cpu_usage|cpu usage +system_load_average_1m|load +jvm_memory_used_bytes|jvm memory used(bytes) +jvm_memory_max_bytes|jvm memory max(bytes) +jvm_gc_pause_seconds_count|gc count +jvm_gc_pause_seconds_sum|gc time +jvm_threads_daemon|jvm threads count + +### Nacos metrics + +item|meaning +---|--- +http_server_requests_seconds_count|http requests count +http_server_requests_seconds_sum|http requests time +nacos_timer_seconds_sum|Nacos config notify time +nacos_timer_seconds_count|Nacos config notify count +grpc_server_requests_seconds_max|Nacos grpc request handle record +grpc_server_executor{name='maximumPoolSize'}|Nacos grpc server executor maximum pool size +grpc_server_executor{name='corePoolSize'}|Nacos grpc server executor core pool size +grpc_server_executor{name='taskCount'}|Nacos grpc server executor task count +grpc_server_executor{name='poolSize'}|Nacos grpc server executor current pool size +grpc_server_executor{name='activeCount'}|Nacos grpc server executor current active thread count +grpc_server_executor{name='completedTaskCount'}|Nacos grpc server executor completed task count +grpc_server_executor{name='inQueueTaskCount'}|Nacos grpc server executor current in queue task count +nacos_monitor{name='longPolling'}|Nacos config connection count +nacos_monitor{name='configCount'}|Nacos configuration file count +nacos_monitor{name='dumpTask'}|Nacos config dump task count +nacos_monitor{name='notifyTask'}|Nacos config notify task count +nacos_monitor{name='getConfig'}|Nacos config read configuration count +nacos_monitor{name='publish'}|Nacos config update configuration count +nacos_monitor{name='ipCount'}|Nacos naming ip count +nacos_monitor{name='domCount'}|Nacos naming domain count(1.x version) +nacos_monitor{name='serviceCount'}|Nacos naming domain count(2.x version) +nacos_monitor{name='failedPush'}|Nacos naming push fail count +nacos_monitor{name='avgPushCost'}|Nacos naming push cost time(average)(ms) +nacos_monitor{name='leaderStatus'}|Nacos naming if node is leader +nacos_monitor{name='maxPushCost'}|Nacos naming push cost time(max)(ms) +nacos_monitor{name='mysqlhealthCheck'}|Nacos naming mysql health check count +nacos_monitor{name='httpHealthCheck'}|Nacos naming http health check count +nacos_monitor{name='tcpHealthCheck'}|Nacos naming tcp health check count +nacos_monitor{name='longConnection'}|Nacos core connection count group by module + +### nacos exception +item|meaning +---|--- +nacos_exception_total{name='db'}|database exception +nacos_exception_total{name='configNotify'}|Nacos config notify exception +nacos_exception_total{name='unhealth'}|Nacos config server health check exception +nacos_exception_total{name='disk'}|Nacos naming write disk exception +nacos_exception_total{name='leaderSendBeatFailed'}|Nacos naming leader send heart beat fail count +nacos_exception_total{name='illegalArgument'}|request argument illegal count +nacos_exception_total{name='nacos'}|Nacos inner exception + +### client metrics +item|meaning +---|--- +nacos_monitor{name='subServiceCount'}|subscribed services count +nacos_monitor{name='pubServiceCount'}|published services count +nacos_monitor{name='configListenSize'}|listened configuration file count +nacos_client_request_seconds_count|request count +nacos_client_request_seconds_sum|request time + +## Nacos-Sync monitor + +With the release of Nacos 0.9, Nacos-Sync 0.3 supports metrics monitoring. It can observe the running status of Nacos-Sync service through metrics data, and improve the monitoring capability of Nacos-Sync in production environment. +Reference for the Construction of the Overall Monitoring System [Nacos Monitoring Manual](./monitor-guide.md) + +## grafana monitor Nacos-Sync +The same as Nacos monitoring, Nacos-Sync also provides monitoring templates to import monitoring [Nacos-Sync templates](https://github.com/nacos-group/nacos-template/blob/master/nacos-sync-grafana) + +Nacos-Sync monitoring is also divided into three modules: +- nacos-sync monitor shows core monitoring items +![monitor](https://img.alicdn.com/tfs/TB1GeNWKmzqK1RjSZFHXXb3CpXa-2834-1588.png) +- nacos-sync detail and alert shows monitoring curves and alarms. +![detail](https://img.alicdn.com/tfs/TB1kP8UKbvpK1RjSZPiXXbmwXXa-2834-1570.png) + +## Nacos-Sync metrics meaning +Nacos-Sync metrics is divided into JVM layer and application layer +### jvm metrics + +item|meaning +---|--- +system_cpu_usage|cpu usage +system_load_average_1m|load +jvm_memory_used_bytes|jvm memory used(bytes) +jvm_memory_max_bytes|jvm memory max(bytes) +jvm_gc_pause_seconds_count|gc count +jvm_gc_pause_seconds_sum|gc time +jvm_threads_daemon|jvm threads count + +## application metrics + +item|meaning +---|--- +nacosSync_task_size|sync task count +nacosSync_cluster_size|cluster count +nacosSync_add_task_rt|add task time +nacosSync_delete_task_rt|delete task time +nacosSync_dispatcher_task|dispatcher task time +nacosSync_sync_task_error|sync task error count diff --git a/src/content/docs/v3.0/en/guide/admin/nacos2-config-benchmark.md b/src/content/docs/v3.0/en/guide/admin/nacos2-config-benchmark.md new file mode 100644 index 00000000000..334dc83ef5c --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/nacos2-config-benchmark.md @@ -0,0 +1,9 @@ +--- +title: Nacos2.0Service configuration performance test report +keywords: [Nacos,service,configuration,performance] +description: Nacos2.0Service configuration performance test report +sidebar: + order: 7 +--- + +To be determined \ No newline at end of file diff --git a/src/content/docs/v3.0/en/guide/admin/nacos2-naming-benchmark.md b/src/content/docs/v3.0/en/guide/admin/nacos2-naming-benchmark.md new file mode 100644 index 00000000000..76b04960d1d --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/nacos2-naming-benchmark.md @@ -0,0 +1,9 @@ +--- +title: Nacos2.0Service configuration performance test report2 +keywords: [Nacos,service,configuration,performance] +description: Nacos2.0Service configuration performance test report2 +sidebar: + order: 8 +--- + +To be determined \ No newline at end of file diff --git a/src/content/docs/v3.0/en/guide/admin/system-configurations.md b/src/content/docs/v3.0/en/guide/admin/system-configurations.md new file mode 100644 index 00000000000..8c7c0a2de59 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/admin/system-configurations.md @@ -0,0 +1,107 @@ +--- +title: Nacos system parameters introduce +keywords: [Nacos,System parameters] +description: Nacos system parameters introduce +sidebar: + order: 3 +--- + +# Nacos system parameters introduce + +> Document optimizing... + +## Nacos Server + +For Server side, usually set in `{nacos.home}/conf/application.properties`, if the parameter name after mark (-D), says is the JVM parameter, need in `{nacos.home}/bin/startup.sh` accordingly set up. Such as setting nacos. The value of the home, can be in `{nacos.home}/bin/startup.sh` the following Settings: + +``` +JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}" +``` + +### Global parameters + +|Parameter names |Meaning | Optional value | Default value | Support version | +|----------------|-----------|-------------------|-----------------|-------| +|nacos.home(-D)| Nacos root directory | Directory path| Nacos installation directory | >= 0.1.0 | +|nacos.standalone(-D)| Whether in stand-alone mode | true/false | false | >= 0.1.0 | +|nacos.functionMode(-D)| Boot mode, support only start one module, do not set all modules will start | config/naming/null | null | >= 0.9.0 | +|nacos.inetutils.prefer-hostname-over-ip| if you should fill in `hostname` in `cluster.conf` | true/false| false | >= 0.3.0 | +|nacos.inetutils.ip-address | Native IP, set this parameter, will use this IP to`cluster.conf`matching, please make sure that the IP value exists in the `cluster.conf` | Native IP| null | >= 0.3.0 | + + +### Naming module + +|Parameter names |Meaning | Optional value | Default value| Support version | +|------|------|-----------|-----------------|-------| +|nacos.naming.data.warmup| Whether the Server startup to preheat the data | true/false | false | >= 1.0.2 | +|nacos.naming.expireInstance| Whether automatic removal of temporary instance | true/false | true | >= 1.0.2 | +|nacos.naming.distro.taskDispatchPeriod| Synchronization task generation cycle, milliseconds | positive integer | 2000 | >= 1.0.2 | +|nacos.naming.distro.batchSyncKeyCount| The number of each batch of key synchronization task | positive integer | 1000 | >= 1.0.2 | +|nacos.naming.distro.syncRetryDelay| Synchronization task failure retry intervals, milliseconds | positive integer | 5000 | >= 1.0.2 | + +In addition to the above listed to in `application.properties`configuration properties, And some can be adjusted call interface at runtime, These parameters are in the [Open API](../user/open-api.md)```examine system current data index```the API in a statement. + +### Config module + +|Parameter names |Meaning | Optional value | Default value| Support version | +|------|------|-----------|-----------------|-------| +|db.num| Number of database | positive integer | 0 | >= 0.1.0 | +|db.url.0| The first database URL | string | null | >= 0.1.0 | +|db.url.1| The second database URL | string | null | >= 0.1.0 | +|db.user| User name of the database connection | string | null | >= 0.1.0 | +|db.password| Database connection password | string | null | >= 0.1.0 | +|spring.datasource.platform|Database type|string|mysql|>=1.3.0| +|db.pool.config.xxx| Database connection pool parameters, using hikari connection pool, the parameters are the same as hikari connection pool, such as `db.pool.config.connectionTimeout` or `db.pool.config.maximumPoolSize` |string| same as hikariCp |>=1.4.1| + +Now the db config support multi data source. It can set data source num by `db.num`, and `db.url.index` as the corresponding connection's url. When `db.user` and `db.password` are set without `index`, all db connection use `db.user` and `db.password` to auth. If the username or password is different with different data source, can split by symbol `,`, or use `db.user.index`,`db.user.password` to set corresponding db connection's username or password. It is important to note that, when `db.user` or `db.password` are set without index, and the mechanism which split `db.user`,`db.password` by `,` exist, so if username or password contains `,`, it will split the value by `,`, and use split[0] to auth, failed to auth finally. + +Nacos started to use HikariCP connection pool from version 1.3, but before version 1.4.1, the connection pool configuration is system default value, and the configuration could not be customized. After 1.4.1, Nacos provide a method to configure the HikariCP connection pool. +`db.pool.config` is the configuration prefix, `xxx` is the actual hikariCP configuration, such as `db.pool.config.connectionTimeout` or `db.pool.config.maximumPoolSize` and so on. For more configuration of hikariCP, please check [HikariCP](https://github.com/brettwooldridge/HikariCP) +It should be noted that `url`, `user`, `password` will be rewrite by `db.url.n`, `db.user`, `db.password`, and driverClassName is the default MySQL8 driver which supports mysql5.x. + +### CMDB module + +|Parameter names |Meaning | Optional value | Default value| Support version | +|------|------|-----------|-----------------|-------| +|nacos.cmdb.loadDataAtStart| Whether to open the CMDB | true/false | false | >= 0.7.0 | +|nacos.cmdb.dumpTaskInterval| The full amount of the interval of the dump, the unit is in seconds | positive integer | 3600 | >= 0.7.0 | +|nacos.cmdb.eventTaskInterval| The pull interval change events, the unit is in seconds | positive integer | 10 | >= 0.7.0 | +|nacos.cmdb.labelTaskInterval| Label the pull interval set, the unit is in seconds | positive integer | 300 | >= 0.7.0 | + +## Nacos Java Client + +> TODO: Move to dependent document in user guide + +Client parameters are divided into two kinds, one kind is through the -D parameter to specify the configuration of the client is a kind of structure, through `Properties` objects specified in the configuration, the following without -D marked by `Properties` injection configuration. + +### General parameters + +|Parameter names |Meaning | Optional value | Default value| Support version | +|------|------|-----------|-----------------|-------| +|endpoint| Connection Nacos Server specify the connection point, you can refer to [file](https://nacos.io/en-us/blog/address-server.html) | domain name | null | >= 0.1.0 | +|endpointPort| Connection Nacos Server specify the connection port, you can refer to [file](https://nacos.io/en-us/blog/address-server.html) | Legal port | null | >= 0.1.0 | +|namespace| namespace ID | namespace ID | config module is empty, naming module is public| >= 0.8.0 | +|serverAddr| Nacos Server address list, this value is higher priority than the endpoint | ip:port,ip:port,... | null | >= 0.1.0 | +|JM.LOG.PATH(-D)| client log directory | directory path | root directory of the user | >= 0.1.0 | +|com.alibaba.nacos.config.log.level(-D)| Naming client log level | info,error,warn etc | info | >= 1.0.0 | +|com.alibaba.nacos.naming.log.level(-D)| Config client log level | info,error,warn etc | info | >= 1.0.0 | +|com.alibaba.nacos.client.naming.tls.enable(-D)| Whether to open the HTTPS | true/false | false | >= 1.0.0 | + +### Naming client + +|Parameter names |Meaning | Optional value | Default value| Support version | +|------|------|-----------|-----------------|-------| +|namingLoadCacheAtStart| If boot priority reads a local cache | true/false | false | >= 1.0.0 | +|namingCacheRegistryDir| The subdirectory of cache, default is `.../nacos/{SUB_DIR}/naming` | path of SUB_DIR | empty string | >=2.0.2 +|namingClientBeatThreadCount| client's heartbeat thread pool size | positive integer | number of the machine's CPU half | >= 1.0.0 | +|namingPollingThreadCount| client regularly polling data update the thread pool size | positive integer | number of the machine's CPU half | >= 1.0.0 | +|com.alibaba.nacos.naming.cache.dir(-D)| The directory of client cache | path of directory | `{user.home}/nacos/naming` | >= 1.0.0 | + +### Config client + +|Parameter names |Meaning | Optional value | Default value| Support version | +|------|------|-----------|-----------------|-------| +|configLongPollTimeout(config.long-poll.timeout 1.0.1 version)| Long polling timeout, milliseconds | positive integer | 30000 | >= 1.0.2 | +|configRetryTime(config.retry.time 1.0.1 version)| Retry time long polling tasks, milliseconds | positive integer | 2000 | >= 1.0.2 | +|maxRetry| Long polling retries | positive integer | 3 | >= 1.0.2 | +|enableRemoteSyncConfig| Listeners are added to the remote configuration for the first time | boolean value | false | >= 1.0.2 | diff --git a/src/content/docs/v3.0/en/guide/user/auth.md b/src/content/docs/v3.0/en/guide/user/auth.md new file mode 100644 index 00000000000..539de7931d2 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/auth.md @@ -0,0 +1,226 @@ +--- +title: Authentication +keywords: [Authentication] +description: Authentication +sidebar: + order: 5 +--- + +> Attention +> - Nacos is an internal micro service component, which needs to run in a trusted internal network. It can not be exposed to the public network environment to prevent security risks. +> - Nacos provides a simple authentication implementation. It is a weak authentication system to prevent business misuse, not a strong authentication system to prevent malicious attacks. +> - If you are running in an untrusted network environment or have strong authentication demands, please refer to the official simple implementation to develop [Authentication plugin](../../plugin/auth-plugin.md). + + +# Authentication + +## Related Parameters + +|Parameter|Default|Versions|Description| +|-----|------|------|----| +|nacos.core.auth.enabled|false|1.2.0 ~ latest|Whether to enable the authentication| +|nacos.core.auth.system.type|nacos|1.2.0 ~ latest|Type of authentication| +|nacos.core.auth.plugin.nacos.token.secret.key|SecretKey012345678901234567890123456789012345678901234567890123456789(No default since 2.2.0.1)|2.1.0 ~ latest|Used to generate the key used by the user to login to the temporary accessToken in the default authentication plugin. **Using the default value is a security risk**.| +|nacos.core.auth.plugin.nacos.token.expire.seconds|18000|2.1.0 ~ latest|Expiration time of user login temporary accessToken| +|nacos.core.auth.enable.userAgentAuthWhite|false|1.4.1 ~ latest|Whether to use the useragent whitelist, mainly used to adapt to the upgrade of the old version, **Setting `true` is a security risk**| +|nacos.core.auth.server.identity.key|serverIdentity(No default since 2.2.1)|1.4.1 ~ latest|Used to replace the identification key of the useragent whitelist, **Using the default value is a security risk**| +|nacos.core.auth.server.identity.value|security(No default since 2.2.1)|1.4.1 ~ latest|It is used to replace the identification value of the useragent whitelist, **Using the default value is a security risk**| +|~~nacos.core.auth.default.token.secret.key~~|SecretKey012345678901234567890123456789012345678901234567890123456789|1.2.0 ~ 2.0.4|Same as `nacos.core.auth.plugin.nacos.token.secret.key`| +|~~nacos.core.auth.default.token.expire.seconds~~|18000|1.2.0 ~ 2.0.4|Same as `nacos.core.auth.plugin.nacos.token.expire.seconds`| + + +## Use Authentication in Servers + +### Without Docker +By default, no login is required to start following the official document configuration, which can expose the configuration center directly to the outside world. However, if the authentication is enabled, one can use nacos only after he configures the user name and password. + +Before enabling authentication, the configuration in application.properties is as follow: +```java +### If turn on auth system: +nacos.core.auth.enabled=false +``` + +After enabling authentication, the configuration in application.properties is as follow: +```java +### If turn on auth system: +nacos.core.auth.system.type=nacos +nacos.core.auth.enabled=true +``` + +#### Custom SecretKey + +After enabling authentication, you can customize the key used to generate JWT tokens,the configuration in application.properties is as follow: + +> Attention: +> 1. The secret key provided in the document is a public key. Please replace it with other secret key content during actual deployment to prevent security risks caused by secret key leakage. +> 2. After version 2.2.0.1, the community release version will remove the following value as the default value in the document, which needs to be filled in by yourself, otherwise the node cannot be started. +> 3. The secret key needs to be consistent between nodes, and if it is inconsistent for a long time, it may cause 403 invalid token error. + +```properties +### The default token(Base64 String): +nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 + +### Since 2.1.0 +nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 +``` + +When customizing the key, it is recommended to set the configuration item to a **Base64 encoded** string, +and **the length of the original key must not be less than 32 characters**. For example the following example: + +```properties +### The default token(Base64 String): +nacos.core.auth.default.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= + +### Since 2.1.0 +nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= +``` + +> Attention: the authentication switch takes effect immediately after the modification, and there is no need to restart the server. When dynamic modifing `token.secret.key`, Please make sure the new value is valid, otherwise the login and request will fail. + +### With Docker + +#### Official images + +If you choose to use official images, please add the following environment parameter when you start a docker container. + +```powershell +NACOS_AUTH_ENABLE=true +``` + +For example, you can run this command to run a docker container with Authentication: + +```powershell +docker run --env PREFER_HOST_MODE=hostname \ + --env MODE=standalone \ + --env NACOS_AUTH_ENABLE=true \ + -e NACOS_AUTH_TOKEN=SecretKeyM1Z2WDc4dnVyZkQ3NmZMZjZ3RHRwZnJjNFROdkJOemEK \ + -e NACOS_AUTH_IDENTITY_KEY=mpYGXyu7 \ + -e NACOS_AUTH_IDENTITY_VALUE=mpYGXyu7 \ + -p 8848:8848 nacos/nacos-server +``` + +Besides, you can also add the other related enviroment parameters: + +| name | description | option | +| ----------------------------- | -------------------------------------- | -------------------------------------- | +| NACOS_AUTH_ENABLE | If turn on auth system | default :false | +| NACOS_AUTH_TOKEN_EXPIRE_SECONDS | The token expiration in seconds | default :18000 | +| NACOS_AUTH_TOKEN | The default token | default :SecretKey012345678901234567890123456789012345678901234567890123456789 | +| NACOS_AUTH_CACHE_ENABLE | Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. | default : false | + + + +#### Custom images + +If you choose to use custom images, please modify the application.properties before you start nacos, change this line + +``` +nacos.core.auth.enabled=false +``` +into +``` +nacos.core.auth.system.type=nacos +nacos.core.auth.enabled=true +``` + +## Authentication in Clients + +### Authentication in Java SDK + +The user name and password should be set when creating a 'Properties' class. +```java +properties.put("username","${username}"); +properties.put("password","${password}"); +``` +#### Example Code +```java +try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + // if need username and password to login + properties.put("username","nacos"); + properties.put("password","nacos"); + + ConfigService configService = NacosFactory.createConfigService(properties); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` +### Authentication in Other languages SDK + +Pending... + +### Authentication in Open-API +Firstly, the user name and password should be provided to login. + +```plain +curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos' +``` + +If the user name and password are correct, the response will be: + +``` +{"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyOTE2Nn0.2TogGhhr11_vLEjqKko1HJHUJEmsPuCxkur-CfNojDo","tokenTtl":18000,"globalAdmin":true} +``` + +Secondly, when using configuration services or naming services, accessToken in the previous response should be provided. To use the accessToken, 'accessToken=${accessToken}' should be appended at the end of request url, e.g., + +```plain +curl -X GET '127.0.0.1:8848/nacos/v1/cs/configs?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&dataId=nacos.example.1&group=nacos_group' +``` + +```plain +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=n1' +``` + +## Open feature for token cache + +Since version 2.2.1 of the server, the default authentication plug-in module supports the feature of token cache, see ISSUE #9906 +``` +https://github.com/alibaba/nacos/issues/9906 +``` +#### Background +Regardless of the client SDK or OpenAPI, after calling the `login` interface to obtain the accessToken, carry the accessToken to access the server, and the server parses the token for authentication. The action of token parsing is time-consuming. If you want to improve the performance of the server, you can consider enabling the feature of caching tokens, which using string comparison instead of token parsing. + +#### Way to open +``` +nacos.core.auth.plugin.nacos.token.cache.enable=true +``` + +#### Attention +Before enabling the feature of token cache, the server will generate a new token for each request carrying a username and password to access the `login` interface. The `tokenTtl` field in the return value of `login` interface is equal to the value set in the server configuration file. The configuration is as follows: +``` +nacos.core.auth.plugin.nacos.token.expire.seconds=18000 +``` +After enabling the feature of token cache, the server will first check whether the token corresponding to the username exists in cache for each request to access the `login` interface with username and password. If it does not exist, generate a new token, insert it into the cache and return it to requester; if it exists, return the token to requester, and the value of the `tokenTtl` field is the value set in the configuration file minus the duration of the token stored in the cache. +If the token stays in the cache for more than 90% of the value set in the configuration file, when the `login` interface receives a request, although the token corresponding to the username exists in the cache, the server will regenerate the token and return it to the requester, and update cache. Therefore, in the worst case, the `tokenTtl` received by the requester is only 10% of the value set in the configuration file. + +## Open feature for server identity + +After the authentication feature is enabled, requests between servers will also be affected by the authentication system. Considering that the communication between the servers should be credible, during the 1.2~1.4.0 version, Nacos server use whether the User-Agent includes Nacos-Server to determine whether the request comes from other servers. + +However, this implementation is too simple and fixed, leading to possible security issues. Therefore, since version 1.4.1, Nacos has added the server identification feature. Users can configure the identity of the server by themselves, and no longer use User-Agent as the judgment standard for server requests. + +Way to open server identity + +``` +### Open authentication +nacos.core.auth.enabled=true + +### Shutdown user-agent judgement for server request +nacos.core.auth.enable.userAgentAuthWhite=false + +### Config the server identity key(not empty) and value(not empty) +nacos.core.auth.server.identity.key=example +nacos.core.auth.server.identity.value=example +``` + +**Attention** All servers in cluster need to be configured with the same `server.identity` information, otherwise it may cause data inconsistency between servers or failure to delete instances. + +### Upgrade from old version + +Considering that users of the old version need to upgrade, users can turn on the `nacos.core.auth.enable.userAgentAuthWhite=true` during upgrading, and turn off it after the cluster is upgraded to 1.4.1 completely and runs stably. diff --git a/src/content/docs/v3.0/en/guide/user/failover.md b/src/content/docs/v3.0/en/guide/user/failover.md new file mode 100644 index 00000000000..2379a2abf1f --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/failover.md @@ -0,0 +1,147 @@ +--- +title: Java Client Failover +keywords: [Failover] +description: Java client failover user guide +sidebar: + order: 7 + hidden: true +--- + +# Java Client Failover + +We can turn on the local data failover feature to handle the situation when Nacos server side is unstable or has problematic data. + +There are two typical scenarios: + +1. When Nacos server is in deployment, we can switch on the failover so the clients use local data only. The data anomaly or oscillation at Nacos server won't affect the clients. After the deployment and the data verification are done, we can switch off the failover feature. +2. When there is a sudden data anomaly at Nacos server at runtime, we can turn on the failover feature to prevent Nacos clients using wrong data. + +The full detailed solution description can be found in https://github.com/alibaba/nacos/issues/11053 + +## Procedures + +image + +As shown above, the query requests to Nacos client would first be checked by FailoverReactor, and only if FailoverReactor has no related data, can the requests move on to query ServiceInfoHolder. + +## Disk based Failover + +FailoverReactor can select different data sources. Disk is the default option. + +### Disk Failover File Path + +The default path of disk failover files are: + +``` +{user.home}/nacos/naming/{namespace}/failover +``` + +This path can be customised via -D argument: + +``` +-DJM.SNAPSHOT.PATH=/mypath +``` + +So the path becomes: + +``` +/mypath/nacos/naming/{namespace}/failover +``` + +### Disk Failover Switch + +The disk failover switch is stored in a file with name: + +``` +00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 +``` + +The content of this file is just a number 0 or 1, where 0 represents failover is off, 1 is on. + +### Disk Failover Data + +The disk failover data is stored in multiple files under the failover path. Each file stores the failover data for a single service. + +The file name is in the following format: + +``` +{group.name}%40%40{service.name} +``` +The content in the file is the JSON string of one ServiceInfo object, for instance: + +``` +{ + "name":"DEFAULT_GROUP@@test.2", + "groupName":"DEFAULT_GROUP", + "clusters":"", + "cacheMillis":10000, + "hosts":[ + { + "instanceId":"1.1.2.1#8888#DEFAULT#DEFAULT_GROUP@@test.2", + "ip":"1.1.2.1", + "port":8888, + "weight":1, + "healthy":true, + "enabled":true, + "ephemeral":true, + "clusterName":"DEFAULT", + "serviceName":"DEFAULT_GROUP@@test.2", + "metadata":{ + "k1":"v1" + }, + "instanceHeartBeatInterval":5000, + "instanceHeartBeatTimeOut":15000, + "ipDeleteTimeout":30000 + } + ], + "lastRefTime":1689835375819, + "checksum":"", + "allIPs":false, + "reachProtectionThreshold":false, + "valid":true +} +``` + +## Extent Failover Data Source + +Disk failover is simple and requires no extra remote components. But sometimes we may want to use another kind of data source, such as Redis, Mysql, etc. + +Now we support extending the failover data source with SPI mechanism. Here are the steps: + +### Develop Your Own Failover Data Source + +Write a class and implement the interface com.alibaba.nacos.client.naming.backups.FailoverDataSource: + +``` +public class MyFailoverDataSource implements FailoverDataSource { + + @Override + public FailoverSwitch getSwitch() { + // TODO write your own implementation. + return null; + } + + @Override + public Map getFailoverData() { + // TODO write your own implementation. For naming module, the map + // should contain failover data with service name as key and ServiceInfo as value + return null; + } +} +``` + +### Configure Failover Data Source Class + +Create a file under the resource root path: + +``` +{resource.root}/META-INF/services/com.alibaba.nacos.client.naming.backups.FailoverDataSource +``` + +One example of `{resource.root}` is src/main/resources. + +The file content is: + +``` +your.package.MyFailoverDataSource +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/guide/user/faq.md b/src/content/docs/v3.0/en/guide/user/faq.md new file mode 100644 index 00000000000..6b840967353 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/faq.md @@ -0,0 +1,185 @@ +--- +title: FAQ +keywords: [Nacos,FAQ] +description: Nacos FAQ +sidebar: + order: 6 +--- + +# FAQ + +> Document optimizing... + +- Nacos standard questions + - [What is Nacos](#1.1) + - [Nacos how to support more than the environment](#1.2) + - [Nacos whether production is available](#1.3) + - [Nacos dependent](#1.4) + - [Nacos using open source licenses](#1.5) + +- Nacos operational questions + - [Nacos standalone deployment](#2.1) + - [Nacos standalone deployment using Mysql](#2.2) + - [Nacos production deployment](#2.3) + - [Nacos Docker deployment](#2.4) + - [How to deploy in k8s Nacos](#2.5) + - [How to monitor Nacos](#2.6) + - [Nacos cannot start in Docker, always print Nacos is starting...](#2.7) + +- Nacos used questions + - [Zookeeper service can be migrated to Nacos?](#3.1) + - [Nacos support multiple configuration files](#3.2) + - [Nacos support Dubbo](#3.3) + - [Nacos support Spring system](#3.4) + - [Don't use Nacos SDK how to access the Nacos](#3.5) + - [Nacos support for multiple languages](#3.6) + - [Nacos 0.8 version logon failure](#3.7) + - [Server error `java.lang.IllegalStateException: unable to find local peer: 127.0.0.1:8848`](#3.8) + - [Nacos configuration for encryption](#3.9) + - [Nacos at 401 error](#3.10) + - [Nacos weight not to take effect](#3.11) + - [Nacos how to enlarge shrinks capacity](#3.12) + - [Nacos client modify the log level](#3.13) + - [Nacos and Zipkin integration `Service not found` error](#3.14) + - [Why service registration is successful, the console can't see](#3.15) + - [`com.alibaba.nacos.consistency.entity` can't be found in source codes](#3.19) + - [`the length of secret key must great than or equal 32 bytes...`](#3.20) + - [`The specified key byte array is x bits ...`](#3.20) + +- Nacos principle questions + +## Nacos standard questions +

What is Nacos

+ +Nacos dedicated to help you find, micro configuration and management services. Nacos provides a set of simple and easy to use feature set, help you quickly realize dynamic service discovery, service configuration, service metadata, and traffic management. Details you can refer to [Nacos website](../../what-is-nacos.md). + +

Nacos how to support more than the environment

+ +In daily use are often need different environment, such as daily, pretest, online environment, if it is a logical isolation, can use the namespace Nacos support namespace to support more environmental isolation, can create multiple namespaces in Nacos console. If you need physical isolation, will deploy more sets of Nacos environment. + +

Nacos whether production is available

+ +Nacos in January 2019 issued a Pre - GA version, to support the security isolation, monitoring and service migration on the last mile of production, in a more stable support the user's production environment. Details you can refer to [Nacos release v0.8.0 Pre - GA version, the safe and stable production](https://www.oschina.net/news/104019/nacos-0-8-0-pre-ga). + +

Nacos dependent

+ +In stand-alone mode, Nacos without any rely on, in cluster mode, Nacos rely on Mysql storage, details you can refer to [Nacos deployment](../admin/deployment.md). + +

Nacos using open source licenses

+ +Nacos using [Apache 2.0](https://github.com/alibaba/nacos/blob/master/LICENSE). + +## Nacos operational questions +

Nacos standalone deployment

+ +You can refer to the manual Nacos website deployment [quick start](../../quickstart/quick-start.mdx). + +

Nacos standalone deployment using Mysql

+ +Nacos stand-alone mode defaults to using the embedded database as the storage engine, if you want to change your mysql installation, you can refer to [website document](../admin/deployment.md). + +

Nacos production deployment

+ +Production environment using Nacos in order to achieve high availability cannot use stand-alone mode, need to build Nacos cluster, specific details can refer to [the manual cluster deployment](../admin/cluster-mode-quick-start.md). + +

Nacos Docker deployment

+ +In addition to using compressed package deployment Nacos, Nacos also provides a corresponding Docker image, when Nacos release new versions, Nacos will release the corresponding image version supports Docker deployment.Specific details you can refer to [Nacos Docker](../../quickstart/quick-start-docker.mdx). + +

How to deploy in k8s Nacos

+ +In production deployment Nacos cluster, if for Nacos expansion operation, need to manually change the cluster IP file, start a new Nacos service.In order to automate operations, k8s Nacos and combined use of StatefulSets provides automatic operations plan, to dynamic scalability Nacos capacity, specific details reference [Kubernetes Nacos](https://github.com/nacos-group/nacos-k8s/blob/master/README.md). + +

How to monitor Nacos

+ +Nacos0.8 version provides the Metrics data exposed ability, can pass the Metrics data to monitor the running status of Nacos, the content of the details you can refer to [Nacos monitor](../admin/monitor-guide.md). + +

Nacos cannot start in Docker, always print Nacos is starting...

+ +The reason may be due to insufficient memory in the Docker environment, causing other services to fail to start normally, and finally causing the service to report an error and keep restarting. You can try to solve it by increasing the Docker memory limit. + +## Nacos used questions +

Zookeeper service can be migrated to Nacos?

+ +Can through the Nacos - Sync moved the Zookeeper service and Nacos, can also be migrated from Nacos Zookeeper, specific details can be used as [Nacos Sync reference](https://github.com/paderlol/nacos-sync-example). + +

Nacos support multiple configuration files

+ +Nacos through Spring Cloud Alibaba Nacos Config support multiple configuration files, configuration can be stored in a separate configuration file.The associated [issue](https://github.com/alibaba/nacos/issues/320), details refer to the document [Spring Cloud Alibaba Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/Nacos-config). + +

Nacos support Dubbo

+ +Nacos version 0.6 and Dubbo integration, support the use of Nacos as registry, related [issue](https://github.com/alibaba/nacos/issues/390), details refer to the document [Nacos and Dubbo fusion become registry](../../ecology/use-nacos-with-dubbo.md). + +

Nacos support Spring system

+ +Nacos perfect supports the Sping technology stack, details refer to the document [Nacos Spring](../../ecology/use-nacos-with-spring.md)、[Nacos Spring Boot](../../ecology/use-nacos-with-spring-boot.md)、[Spring Cloud](../../ecology/use-nacos-with-spring-cloud.md). + +

Don't use Nacos SDK how to access the Nacos

+ +Nacos network interaction is implemented based on Http protocol, provides the [Open-API](./open-api.md) can easily achieve Nacos access. + +

Nacos support for multiple languages

+ +Nacos currently only supports Java, support for other languages are being developed, also need your support to build together. + +

Nacos 0.8 version logon failure

+ +Nacos version 0.8 when using its and no `JAVA_HOME` environment variable, Nacos can launch successful, because `yum install` installed its the Java command to register a beneath `/bin` directory, and so can cause abnormal `SignatureException`.This problem has been repair, version 0.9 release, the specific details can refer to the [issue](https://github.com/alibaba/nacos/issues/711). + +

Server error java.lang.IllegalStateException: unable to find local peer: 127.0.0.1:8848

+ +This problem because Nacos get native IP, don't get to the correct external IP. The need to guarantee the `InetAddress.getLocalHost().getHostAddress()` or the result of the `hostname -i` was with the cluster. The conf configuration of IP is the same. + +

Nacos configuration for encryption

+ +Nacos plan in 1.X version's ability to provide encryption, currently does not support encryption, can only rely on the SDK prepared encryption endures Nacos again. + +

Nacos at 401 error

+ +Nacos server error, check the server logs, refer to the [issue](https://github.com/alibaba/nacos/issues/816). + +

Nacos weight not to take effect

+ +Nacos console editors weights, at present from SpringCloud client and Dubbo client didn't get through, so can't take effect. For SpringCloud client application can realize the load balancer Ribbon for weighting filter. + +

Nacos how to enlarge shrinks capacity

+ +Currently supported modify the `cluster.conf` file in a way that expanding capacity, after the change without restart, the Server will automatically refresh the new content to the file. + +

Nacos client modify the log level

+ +Configuration - D parameters `com.alibaba.nacos.naming.log.level` set naming the client log level, such as setting for the error:`-Dcom.alibaba.nacos.naming.log.level=error` Similarly, - D parameters `com.alibaba.nacos.config.log.level` is used to set the config client log level. + +

Nacos and Zipkin integration Service not found error

+ +Configuration `spring-cloud-seluth` parameters: `spring.zipkin.discovery-client-enabled=false`. + +If there is still a `Service not found` error, is recommended to use the open-api will Zipkin-server instance is registered as a permanent Service: + +`curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?port=9411&healthy=true&ip=127.0.0.1&weight=1.0&serviceName=zipkin-server&ephemeral=false&namespaceId=public'` + +Then, went to nacos console, find a service called `zipkin-server` service, find the cluster configuration, set the health examination mode to `TCP`, port number of `9411` (zipkin-server port). + +

Why service registration is successful, the console can't see

+ +This problem appeared in cluster mode, in the use of nacos cluster pattern, ensure that all of the machine time is consistent, can appear otherwise unable to synchronize data. + +

`com.alibaba.nacos.consistency.entity` can't be found in source codes

+ +This package will be auto-generated by `protobuf`, so if you want to read source code or do some develop, you can use `mvn compile` to generate them. If you are using IDEA, you can also use IDEA's protobuf plugin. + +

java.lang.IllegalArgumentException: the length of secret key must great than or equal 32 bytes...

+

java.lang.IllegalArgumentException: The specified key byte array is x bits which is not secure enough for any JWT HMAC-SHA algorithm.

+ +The default authentication plugin needs a secret key to generate an access token, and the secret key format needs to have a length greater than 32. If the length of `secret.key` after BASE64 decryption is less than 32, this error will occur during the startup process. +You can set the correct `secret.key` in `application.properties`, see [User Guide - Authorization Authentication](./auth.md) for details. + +

Error:Empty identity, Please set `nacos.core.auth.server.identity.key` and `nacos.core.auth.server.identity.value`

+ +Since 2.2.1, the default value of `nacos.core.auth.server.identity.key` and `nacos.core.auth.server.identity.value` has been removed and will be checked during server starting. +If the auth feature enabled but no `nacos.core.auth.server.identity.key` and `nacos.core.auth.server.identity.value` configured, nacos server will stop bootstrap and hint with the error message. +Please see [User Guide - Authorization Authentication](./auth.md) to configure them and restart. + + +## Nacos principle questions diff --git a/src/content/docs/v3.0/en/guide/user/open-api.md b/src/content/docs/v3.0/en/guide/user/open-api.md new file mode 100644 index 00000000000..70dff4f93b6 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/open-api.md @@ -0,0 +1,2807 @@ +--- +title: Open API Guide +keywords: [Open API,Guide] +description: Open API Guide +sidebar: + order: 3 +--- + +# Open API Guide + +Nacos 2.X is compatible with Nacos 1.X OpenAPI, please refer to the document [Nacos1.X OpenAPI](https://nacos.io/en/docs/v1/open-api/). + +> Attension: OpenAPIs which do not specify a supported version, will be supported since 2.2.0. + +- Documentation Conventions + - [API unified return body format](#0.1) + - [API error code summary](#0.2) + +- Configuration Management + - [Get configuration](#1.1) + - [Publish configuration](#1.2) + - [Delete configuration](#1.3) + - [Query list of history configuration](#1.4) + - [Query the history details of the configuration](#1.5) + - [Query the previous version of the configuration](#1.6) + - [Query the list of configurations under the specified namespace](#1.7) + +- Service Discovery + - [Register instance](#2.1) + - [Deregister instance](#2.2) + - [Modify instance](#2.3) + - [Query instance detail](#2.4) + - [Query instances](#2.5) + - [Batch update instance metadata(Beta)](#2.6) + - [Batch delete instance metadata(Beta)](#2.7) + - [Create service](#2.8) + - [Delete service](#2.9) + - [Update service](#2.10) + - [Query service](#2.11) + - [Query service list](#2.12) + - [Query system switches](#2.13) + - [Update system switch](#2.14) + - [Query system metrics](#2.15) + - [Update instance health status](#2.16) + - [Query client list (new)](#2.17) + - [Query client (new)](#2.18) + - [Query the registration information of the client (new)](#2.19) + - [Query the subscription information of the client (new)](#2.20) + - [Query the client that registered the specified service (new)](#2.21) + - [Query the information of clients subscribed to the specified service (new)](#2.22) + +- Namespace + - [Query namespaces](#3.1) + - [Query namespace](#3.2) + - [Create namespace](#3.3) + - [Update namespace](#3.4) + - [Delete namespace](#3.5) + +- Cluster + - [Query the current node](#4.1) + - [Query the list of cluster nodes](#4.2) + - [Query the current node health status](#4.3) + - [Switch addressing modes](#4.4) +- Connection Load Management + - [Query the List of Current Node Client Connections](#5.1) + - [Reload the Number of Current Node Client Connections](#5.2) + - [Intelligently Balance the Number of Client Connections in the Cluster](#5.3) + - [Reset a Specific Client Connection](#5.4) + - [Get SDK Metrics for the Cluster](#5.5) + + +## Documentation Conventions + +

API unified return body format

+ +In Nacos 2.X, the response to all interface requests is a return body of type `json`, which has the same format + +```json +{ + "code": 0, + "message": "success", + "data": {} +} +``` + +The meanings of the fields in the return body are shown in the following table + +| name | type | description | +|:---------:|:--------:|------------------------------------------------------------------------------------------------------| +| `code ` | `int` | Error code,`0` means the execution succeeded, non-`0` means the execution failed in one of the cases | +| `message` | `String` | Error code prompt message, execution success as "`success`" | +| `data` | `Any` | Return data, detailed error message in case of execution failure | + +> Since the `code` field is the same as the message field in case of successful execution, only the `data` field of the returned data will be introduced in the subsequent introduction of the returned results of the interface + +

API error code summary

+ +The error codes and corresponding prompt messages in the return body of the API interface are summarized in the following table + +| Error Code | message | meaning | +|------------|------------------------------|--------------------------------------------------| +| `0` | `success` | Successful execution | +| `10000` | `parameter missing` | Missing parameters | +| `10001` | `access denied` | Access Denied | +| `10002` | `data access error` | Data access error | +| `20001` | `'tenant' parameter error` | `tenant` parameter error | +| `20002` | `parameter validate error` | Parameter validation error | +| `20003` | `MediaType Error` | `MediaType` error for HTTP requests | +| `20004` | `resource not found` | Resource not found | +| `20005` | `resource conflict` | Resource access conflicts | +| `20006` | `config listener is null` | Listening configuration is empty | +| `20007` | `config listener error` | Listening configuration error | +| `20008` | `invalid dataId` | Invalid `dataId` (authentication failure) | +| `20009` | `parameter mismatch` | Request parameter mismatch | +| `21000` | `service name error` | `serviceName` error | +| `21001` | `weight error` | `weight` error | +| `21002` | `instance metadata error` | Instance `metadata` error | +| `21003` | `instance not found` | `instance` not found | +| `21004` | `instance error` | `instance` error | +| `21005` | `service metadata error` | Service `metadata` error | +| `21006` | `selector error` | `selector` error | +| `21007` | `service already exist` | Service already exists | +| `21008` | `service not exist` | Service does not exist | +| `21009` | `service delete failure` | Service instance exists, service deletion failed | +| `21010` | `healthy param miss` | `healthy` parameter miss | +| `21011` | `health check still running` | Health check is still running | +| `22000` | `illegal namespace` | `namespace` is illegal | +| `22001` | `namespace not exist` | Namespace does not exist | +| `22002` | `namespace already exist` | Namespace already exists | +| `23000` | `illegal state` | `state` is illegal | +| `23001` | `node info error` | Node information error | +| `23002` | `node down failure` | Node offline operation error | +| ... | ... | ... | +| 30000 | `server error` | Other internal errors | + +## Configuration Management + +

Get configuration

+ +### Description + +Get the specified configuration + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/cs/config` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|--------------------------------| +| `namespaceId` | `String` | N | Namespace, default is `public` | +| `group` | `String` | **Y** | Config group name | +| `dataId` | `String` | **Y** | Config name | +| `tag` | `String` | N | Tag | + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|----------------| +| `data` | `String` | Config content | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": "contentTest" + } + ``` + +

Publish configuration

+ +### Description + +Publish the specified configuration + +> Update the configuration when it already exists + +### Request Method + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/cs/config` + +### Request Body + +| Parameter | Type | Required | Description | +|---------------|----------|----------|------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace, default is `public` | +| `group` | `String` | **Y** | Config group | +| `dataId` | `String` | **Y** | Config name | +| `content` | `String` | **Y** | Config content | +| `tag` | `String` | N | Tag | +| `appName` | `String` | N | Application name | +| `srcUser` | `String` | N | Source user | +| `configTags` | `String` | N | Configure Tag list, can be multiple, comma separated | +| `desc` | `String` | N | Config description | +| `use` | `String` | N | - | +| `effect` | `String` | N | - | +| `type` | `String` | N | Config type | +| `schema` | `String` | N | - | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'dataId=nacos.example' \ + -d 'group=DEFAULT_GROUP' \ + -d 'namespaceId=public' \ + -d 'content=contentTest' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/cs/config' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Delete configuration

+ +### Description + +Delete the specified configuration + +### Request Method + +`DELETE` + +### Request URL + +`/nacos/v2/cs/config` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|--------------------------------| +| `namespaceId` | `String` | N | Namespace, default is `public` | +| `group` | `String` | **Y** | Config group name | +| `dataId` | `String` | **Y** | Config name | +| `tag` | `String` | N | Tag | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Query list of history configuration

+ +### Description + +Get a list of historical versions of the specified configuration + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/cs/history/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|------------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace, default is `public` | +| `group` | `String` | **Y** | Config group name | +| `dataId` | `String` | **Y** | Config name | +| `pageNo` | `int` | N | Current page, default is `1` | +| `pageSize` | `int` | N | Number of page entries, default is `100`, maximum is `500` | + +### Return Data + +| Parameter | Type | Description | +|-----------------------|------------|--------------------------------------------------------------------------------| +| `data` | `Object` | Paging Search Results | +| `data.totalCount` | `int` | Total | +| `data.pageNumber` | `int` | Current page | +| `data.pagesAvailable` | `int` | Total number of pages | +| `data.pageItems` | `Object[]` | List of historical configuration items, refer to [Historical configuration item](#ConfigHistoryInfo) | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/list?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "totalCount": 1, + "pageNumber": 1, + "pagesAvailable": 1, + "pageItems": [ + { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + ] + } + } + ``` + +

Query the history details of the configuration

+ +### Description + +Get the historical configuration of the specified version + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/cs/history` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|--------------------------------| +| `namespaceId` | `String` | N | Namespace, default is `public` | +| `group` | `String` | **Y** | Config group name | +| `dataId` | `String` | **Y** | Config name | +| `nid` | `long` | **Y** | History configuration id | + +

Return Data

+ +| Parameter | Type | Description | +|-------------------------|----------|---------------------------------| +| `data` | `Object` | Historical configuration item | +| `data.id` | `String` | Config `id` | +| `data.lastId` | `int` | | +| `data.dataId` | `String` | Config name | +| `data.group` | `String` | config group | +| `data.tenant` | `String` | Tenant (namespace) | +| `data.appName` | `String` | Application name | +| `data.md5` | `String` | The md5 value of config content | +| `data.content` | `String` | Config content | +| `data.srcIp` | `String` | Source ip | +| `data.srcUser` | `String` | Source user | +| `data.opType` | `String` | Operator type | +| `data.createdTime` | `String` | Creation time | +| `data.lastModifiedTime` | `String` | Last modified time | +| `data.encryptedDataKey` | `String` | | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=&nid=203' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +

Query the previous version of the configuration

+ +### Description + +Get the previous version of the specified configuration + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/cs/history/previous` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|--------------------------------| +| `namespaceId` | `String` | N | Namespace, default is `public` | +| `group` | `String` | **Y** | Config group name | +| `dataId` | `String` | **Y** | Config name | +| `id` | `long` | **Y** | config id | + +

Return Data

+ +| Parameter | Type | Description | +|-----------|----------|---------------------------------------------------------------------------------------------| +| `data` | `Object` | Historical configuration item, refer to [Historical configuration item](#ConfigHistoryInfo) | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/previous?id=309135486247505920&dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +

Query the list of configurations under the specified namespace

+ +### Description + +Get the list of configurations under the specified namespace + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/cs/history/configs` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|-------------| +| `namespaceId` | `String` | **Y** | Namespace | + +### Return Data + +| Parameter | Type | Description | +|-------------------------|------------|---------------------------------| +| `data` | `Object[]` | Config list | +| `data.id` | `String` | config `id` | +| `data.dataId` | `String` | Config name | +| `data.group` | `String` | Config group | +| `data.content` | `String` | Config content | +| `data.md5` | `String` | the md5 value of config content | +| `data.encryptedDataKey` | `String` | | +| `data.tenant` | `String` | Tenant (namespace) | +| `data.appName` | `String` | Application name | +| `data.type` | `String` | config file Type | +| `data.lastModified` | `long` | Last modified time | + +> Only the `dataId`, `group`, `tenant`, `appName`, `type` fields are valid for the configuration information in the returned data, the other fields are default values + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/configs?namespaceId=' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "id": "0", + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "content": null, + "md5": null, + "encryptedDataKey": null, + "tenant": "", + "appName": "", + "type": "yaml", + "lastModified": 0 + } + ] + } + ``` + +## Service Discovery + +

Register instance

+ +### Description + +Register an instance + +### Request Method + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/instance` + +### Request Body + +| Parameter | Type | Required | Description | +|---------------|----------------------|----------|-----------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `ip` | `String` | **Y** | `IP` address | +| `port` | `int` | **Y** | Port number | +| `clusterName` | `String` | N | Cluster name, default is `DEFAULT` | +| `healthy` | `boolean` | N | Whether to find only healthy instances, default is `true` | +| `weight` | `double` | N | Instance weights, default is `1.0` | +| `enabled` | `boolean` | N | Enable or not, default is `true` | +| `metadata` | `JSON format String` | N | Instance metadata | +| `ephemeral` | `boolean` | N | Whether it is a temporary instance | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Deregister instance

+ +### Description + +Deregister a specified instance + +### Request Method + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/instance` + +### Request Body + +| Parameter | Type | Required | Description | +|---------------|----------------------|----------|-----------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `ip` | `String` | **Y** | `IP` address | +| `port` | `int` | **Y** | Port number | +| `clusterName` | `String` | N | Cluster name, default is `DEFAULT` | +| `healthy` | `boolean` | N | Whether to find only healthy instances, default is `true` | +| `weight` | `double` | N | Instance weights, default is `1.0` | +| `enabled` | `boolean` | N | Enable or not, default is `true` | +| `metadata` | `JSON format String` | N | Instance metadata | +| `ephemeral` | `boolean` | N | Whether it is a temporary instance | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Modify instance

+ +### Description + +Modify instance information + +> The metadata updated through this interface has a higher priority and has the ability to remember. After the instance removed, it will still exist for a period of time. If the instance is re-registered during this period, the metadata will still be Effective. You can modify the memory time through `nacos.naming.clean.expired-metadata.expired-time` **and** `nacos.naming.clean.expired-metadata.interval` + +### Request Method + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/instance` + +### Request Body + +| Parameter | Type | Required | Description | +|---------------|----------------------|----------|-----------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `ip` | `String` | **Y** | `IP` address | +| `port` | `int` | **Y** | Port number | +| `clusterName` | `String` | N | Cluster name, default is `DEFAULT` | +| `healthy` | `boolean` | N | Whether to find only healthy instances, default is `true` | +| `weight` | `double` | N | Instance weights, default is `1.0` | +| `enabled` | `boolean` | N | Enable or not, default is `true` | +| `metadata` | `JSON format String` | N | Instance metadata | +| `ephemeral` | `boolean` | N | Whether it is a temporary instance | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Query instance detail

+ +### Description + +Query the details of a specific instance + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/instance` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|----------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `clusterName` | `String` | N | Cluster name, default is `DEFAULT` | +| `ip` | `String` | **Y** | `IP` address | +| `port` | `int` | **Y** | Port number | + +### Return Data + +| Parameter | Type | Description | +|--------------------|-----------|------------------------------| +| `data` | `Object` | Instance details information | +| `data.serviceName` | `String` | Service name | +| `data.ip` | `String` | `IP` address | +| `data.port` | `int` | Port number | +| `data.clusterName` | `String` | Cluster name | +| `data.weight` | `double` | Instance weight | +| `data.healthy` | `boolean` | healthy | +| `data.instanceId` | `String` | Instance `id` | +| `data.metadata` | `map` | Instance metadata | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance?namespaceId=public&groupName=&serviceName=test_service&ip=127.0.0.1&port=8080' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "serviceName": "DEFAULT_GROUP@@test_service", + "ip": "127.0.0.1", + "port": 8080, + "clusterName": "DEFAULT", + "weight": 1.0, + "healthy": true, + "instanceId": null, + "metadata": { + "value": "1" + } + } + } + ``` + +

Query instances

+ +### Description + +Query the list of instances under the specified service + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/instance/list` + +### Request Header + +| Parameter | Type | Required | Description | +|------------------|----------|----------|----------------------------------| +| `User-Agent` | `String` | N | User agent, default is empty | +| `Client-Version` | `String` | N | Client version, default is empty | + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|-----------|----------|---------------------------------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is empty | +| `serviceName` | `String` | **Y** | Service name | +| `clusterName` | `String` | N | Cluster name, default is `DEFAULT` | +| `ip` | `String` | N | `IP` address, the default is empty, which means no restrictions on `IP` address | +| `port` | `int` | N | Port numberThe default is `0`, which means no restriction on port number | +| `healthyOnly` | `boolean` | N | Whether to get only healthy instances, default is `false` | +| `app` | `String` | N | Application name, default is empty | + +### Return Data + +| Parameter | Type | Description | +|----------------------------------------|------------|---------------------------------------------------| +| `data` | | List of instances of the specified service | +| `data.name` | `String` | Group name@@Service name | +| `data.groupName` | `String` | Group name | +| `data.clusters` | `String` | Cluster name | +| `data.cacheMillis` | `int` | Cache name | +| `data.hosts` | `Object[]` | Instance list | +| `data.hosts.ip` | `String` | Instance `IP` | +| `data.hosts.port` | `int` | Instance Port number | +| `data.hosts.weight` | `double` | Instance weight | +| `data.hosts.healthy` | `boolean` | Instance healthy | +| `data.hosts.enabled` | `boolean` | Instance is enabled | +| `data.hosts.ephemeral` | `boolean` | Whether it is a temporary instance | +| `data.hosts.clusterName` | `String` | Name of the cluster where the instance is located | +| `data.hosts.serviceName` | `String` | Service name | +| `data.hosts.metadata` | `map` | Instance metadata | +| `data.hosts.instanceHeartBeatTimeOut` | `int` | Instance heartbeat timeout time | +| `data.hosts.ipDeleteTimeout` | `int` | Instance delete timeout time | +| `data.hosts.instanceHeartBeatInterval` | `int` | Instance heartbeat interval | +| `data.lastRefTime` | `int` | last refresh time | +| `data.checksum` | `int` | checksum | +| `data.allIPs` | `boolean` | | +| `data.reachProtectionThreshold` | `boolean` | Whether the protection threshold is reached | +| `data.valid` | `boolean` | Valid | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance/list?serviceName=test_service&ip=127.0.0.1' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "name": "DEFAULT_GROUP@@test_service", + "groupName": "DEFAULT_GROUP", + "clusters": "", + "cacheMillis": 10000, + "hosts": [ + { + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "enabled": true, + "ephemeral": true, + "clusterName": "DEFAULT", + "serviceName": "DEFAULT_GROUP@@test_service", + "metadata": { + "value": "1" + }, + "instanceHeartBeatTimeOut": 15000, + "ipDeleteTimeout": 30000, + "instanceHeartBeatInterval": 5000 + } + ], + "lastRefTime": 1662554390814, + "checksum": "", + "allIPs": false, + "reachProtectionThreshold": false, + "valid": true + } + } + ``` + +

Batch update instance metadata

+ +### Description + +Batch update instance metadata + +> If the key corresponding to the metadata does not exist, add the corresponding metadata. + +### Request Method + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/instance/metadata/batch` + +### Request Body + +| Parameter | Type | Required | Description | +|-------------------|----------------------|----------|----------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `consistencyType` | `String` | N | Persistence type, default is empty | +| `instances` | `JSON format String` | N | Instance list, default is empty | +| `metadata` | `JSON format String` | **Y** | Instance metadata | + +### Parameter Description + +> - `consistencyType`: Persistence type of Instance, when ``persist`'' means update the metadata of persistent Instance; otherwise means update the metadata of temporary Instance +> - `instances`: Instance list to be updated, `json` array, locate an instance by `ip+port+ephemeral+cluster`, null means update the metadata of all instances under the specified service + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Batch delete instance metadata

+ +### Description + +Batch delete instance metadata + +> If the key corresponding to the metadata does not exist, then no operation is performed + +### Request Method + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/instance/metadata/batch` + +### Request Body + +| Parameter | Type | Required | Description | +|-------------------|----------------------|----------|----------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `consistencyType` | `String` | N | Persistence type, default is empty | +| `instances` | `JSON format String` | N | Instance list, default is empty | +| `metadata` | `JSON format String` | **Y** | Instance metadata | + +### Parameter Description + +> - `consistencyType`: Persistence type of Instance, when ``persist`'' means update the metadata of persistent Instance; otherwise means update the metadata of temporary Instance +> - `instances`: Instance list to be updated, `json` array, locate an instance by `ip+port+ephemeral+cluster`, null means update the metadata of all instances under the specified service + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Create service

+ +### Description + +Create a service + +> Failed to create when service already exists + +### Request Method + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/service` + +### Request Body + +| Parameter | Type | Required | Description | +|--------------------|----------------------|----------|--------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `metadata` | `JSON format String` | N | Service metadata, default is empty | +| `ephemeral` | `boolean` | N | Whether it is a temporary instance, default is `false` | +| `protectThreshold` | `float` | N | Protection threshold, default is `0` | +| `selector` | `JSON format String` | N | Selector, default is empty | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ephemeral=true' \ + -d 'metadata={"k1":"v1"}' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Delete service

+ +### Description + +Delete the specified service + +> An error is reported when the service does not exist, and deletion fails when an instance of the service still exists + +### Request Method + +`DELETE` + +### Request URL + +`/nacos/v2/ns/service` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|----------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Update service

+ +### Description + +Update the specified service + +> Error when service does not exist + +### Request Method + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/service` + +### Request Parameters + +| Parameter | Type | Required | Description | +|--------------------|----------------------|----------|----------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `metadata` | `JSON format String` | N | Service metadata, default is empty | +| `protectThreshold` | `float` | N | Protection threshold, default is `0` | +| `selector` | `JSON format String` | N | Selector, default is empty | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'metadata={"k1":"v2"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Query service

+ +### Description + +Query detailed information about a specific service + +> Error when service does not exist + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/service` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is empty | +| `serviceName` | `String` | **Y** | Service name | + +### Return Data + +| Parameter | Type | Description | +|-------------------------|-----------|------------------------------------| +| `data` | | Service information | +| `data.namespace` | `String` | Namespace | +| `data.groupName` | `String` | Group name | +| `data.serviceName` | `String` | Service name | +| `data.clusterMap` | `map` | Cluster information | +| `data.metadata` | `map` | Service metadata | +| `data.protectThreshold` | `float` | Protection threshold | +| `data.selector` | `Object` | Selector | +| `data.ephemeral` | `Boolean` | Whether it is a temporary instance | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "public", + "serviceName": "nacos.test.1", + "groupName": "DEFAULT_GROUP", + "clusterMap": {}, + "metadata": {}, + "protectThreshold": 0, + "selector": { + "type": "none", + "contextType": "NONE" + }, + "ephemeral": false + } + } + ``` + +

Query service list

+ +### Description + +Check the list of eligible services + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/service/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------------------|----------|----------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is empty | +| `selector` | `JSON format String` | **Y** | Selector | +| `pageNo` | `int` | N | Current page, default is `1` | +| `pageSize` | `int` | N | Number of page, default is `20`, Up to `500` | + +### Return Data + +| Parameter | Type | Description | +|-----------------|------------|---------------------------| +| `data` | | Service list | +| `data.count` | `String` | Number of services | +| `data.services` | `String[]` | Service list after paging | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service/list' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "count": 2, + "services": [ + "nacos.test.1", + "nacos.test.2" + ] + } + } + ``` + +

Query system switches

+ +### Description + +Query system switches + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/operator/switches` + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|---------------------------| +| `data` | `Object` | System switch information | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/operator/switches' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "masters": null, + "adWeightMap": {}, + "defaultPushCacheMillis": 10000, + "clientBeatInterval": 5000, + "defaultCacheMillis": 3000, + "distroThreshold": 0.7, + "healthCheckEnabled": true, + "autoChangeHealthCheckEnabled": true, + "distroEnabled": true, + "enableStandalone": true, + "pushEnabled": true, + "checkTimes": 3, + "httpHealthParams": { + "max": 5000, + "min": 500, + "factor": 0.85 + }, + "tcpHealthParams": { + "max": 5000, + "min": 1000, + "factor": 0.75 + }, + "mysqlHealthParams": { + "max": 3000, + "min": 2000, + "factor": 0.65 + }, + "incrementalList": [], + "serverStatusSynchronizationPeriodMillis": 2000, + "serviceStatusSynchronizationPeriodMillis": 5000, + "disableAddIP": false, + "sendBeatOnly": false, + "lightBeatEnabled": true, + "doubleWriteEnabled": false, + "limitedUrlMap": {}, + "distroServerExpiredMillis": 10000, + "pushGoVersion": "0.1.0", + "pushJavaVersion": "0.1.0", + "pushPythonVersion": "0.4.3", + "pushCVersion": "1.0.12", + "pushCSharpVersion": "0.9.0", + "enableAuthentication": false, + "overriddenServerStatus": null, + "defaultInstanceEphemeral": true, + "healthCheckWhiteList": [], + "name": "00-00---000-NACOS_SWITCH_DOMAIN-000---00-00", + "checksum": null + } + } + ``` + +

Update system switch

+ +### Description + +Update system switch + +### Request Method + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/operator/switches` + +### Request Body + +| Parameter | Type | Required | Description | +|-----------|-----------|----------|--------------------------------------------------------------------------------------------------------------------------------------| +| `entry` | `String` | **Y** | Entry | +| `value` | `String` | **Y** | Value | +| `debug` | `boolean` | N | Whether it takes effect on local machine only,`true` means it takes effect on local machine,`false` means it takes effect on cluster | + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|-------------------------------------| +| `data` | `String` | "ok" indicates successful execution | + +### Example + +* Request Example + + ```shell + curl -d 'entry=pushEnabled' \ + -d 'value=false' \ + -d 'debug=true' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/operator/switches' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": "ok" + } + ``` + +

Query system metrics

+ +### Description + +Query system metrics + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/operator/metrics` + +### Request Parameters + +| Parameter | Type | Required | Description | +|--------------|-----------|----------|-------------------------------------| +| `onlyStatus` | `boolean` | N | Show status only, default is `true` | + +> When `onlyStatus` is set to `true`, only the string indicating the system status is returned + +### Return Data + +| Parameter | Type | Description | +|------------------------------------|----------|----------------------------------| +| `data` | `Object` | System metrics | +| `data.status` | `String` | System status | +| `data.serviceCount` | `int` | Number of services | +| `data.instanceCount` | `int` | Number of instances | +| `data.subscribeCount` | `int` | Number of subscriptions | +| `data.raftNotifyTaskCount` | `int` | Number of `Raft` notify task | +| `data.responsibleServiceCount` | `int` | | +| `data.responsibleInstanceCount` | `int` | | +| `data.clientCount` | `int` | Number of client | +| `data.connectionBasedClientCount` | `int` | Number of connectionBasedClient | +| `data.ephemeralIpPortClientCount` | `int` | Number of ephemeralIpPortClient | +| `data.persistentIpPortClientCount` | `int` | Number of persistentIpPortClient | +| `data.responsibleClientCount` | `int` | | +| `data.cpu` | `float` | `cpu` utilization | +| `data.load` | `float` | load | +| `data.mem` | `float` | Memory usage | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/operator/metrics' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "status": "UP", + "serviceCount": 2, + "instanceCount": 2, + "subscribeCount": 2, + "raftNotifyTaskCount": 0, + "responsibleServiceCount": 0, + "responsibleInstanceCount": 0, + "clientCount": 2, + "connectionBasedClientCount": 2, + "ephemeralIpPortClientCount": 0, + "persistentIpPortClientCount": 0, + "responsibleClientCount": 2, + "cpu": 0, + "load": -1, + "mem": 1 + } + } + ``` + +

Update instance health status

+ +### Description + +Update the health status of the instance + +### Request Method + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/ns/health/instance` + +### Request Body + +| Parameter | Type | Required | Description | +|---------------|-----------|----------|----------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `clusterName` | `String` | N | Cluster name, default is `DEFAULT` | +| `ip` | `String` | **Y** | `IP` address | +| `port` | `int` | **Y** | Port number | +| `healthy` | `boolean` | **Y** | healthy | + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|---------------------------------------| +| `data` | `String` | "`ok`" indicates successful execution | + +### Example + +* Request Example + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ip=127.0.0.1' \ + -d 'port=8080' \ + -d 'healthy=false' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/health/instance' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": "ok" + } + ``` + +

Query client list (new)

+ +### Description + +Query the current list of all clients + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/client/list` + +### Return Data + +| Parameter | Type | Description | +|-----------|------------|------------------| +| `data` | `String[]` | Client `id` list | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/list' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + "10.128.164.35:9956#true", + "1664358687402_127.0.0.1_2300", + "1664358642902_127.0.0.1_2229", + "192.168.139.1:49825#true", + "10.128.164.35:9954#true", + "192.168.139.1:53556#true" + ] + } + ``` + +> For different versions of the nacos client, there are different ways to create clients. +> +> For `nacos client` in `1.x` version, each Instance will create two clients based on `ip+port`, corresponding to Instance registration and service subscription, respectively, with `clientId` in the format `ip:port#ephemeral` +> +> For `nacos client` in `2.x` version, each Instance establishes a `RPC` connection, which corresponds to an `RPC` connection-based client with both registration and subscription functions, with `clientId` in the format `time_ip_port` + + +

Query client (new)

+ +### Description + +Query the details of the specified client + +> Error when client does not exist + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/client` + +### Request Parameters + +| Parameter | Type| Required | Description | +|------------|----------|----------|------------| +| `clientId` | `String` | **Y** | Client `id` | + +### Return Data + +| Parameter | Type | Description | +|------------------------|-----------|------------------------------------| +| `data` | `Object` | Client Information | +| `data.clientId` | `String` | Client `id` | +| `data.ephemeral` | `boolean` | Whether it is a temporary instance | +| `data.lastUpdatedTime` | `int` | Last update time | +| `data.clientType` | `String` | Client type | +| `data.clientIp` | `String` | Client `IP` | +| `data.clientPort` | `String` | Client `port` | +| `data.connectType` | `String` | Connection type | +| `data.appName` | `String` | Application name | +| `data.Version` | `String` | Client version | + +> Only when `clientType` is `connection`, the `connectType`, `appName` and `appName` fields will be displayed + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client?clientId=1664527081276_127.0.0.1_4400' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "clientId": "1664527081276_127.0.0.1_4400", + "ephemeral": true, + "lastUpdatedTime": 1664527081642, + "clientType": "connection", + "connectType": "GRPC", + "appName": "-", + "version": "Nacos-Java-Client:v2.1.0", + "clientIp": "10.128.164.35", + "clientPort": "4400" + } + } + ``` + +

Query the registration information of the client (new)

+ +### Description + +Query the registration information of the specified client + +> Error when client does not exist + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/client/publish/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|------------|----------|----------|-------------| +| `clientId` | `String` | **Y** | Client `id` | + +### Return Data + +| Parameter | Type | Description | +|-----------------------------------|------------|-------------------------------------------| +| `data` | `Object[]` | List of services registered by the client | +| `data.namespace` | `String` | Namespace | +| `data.group` | `String` | Group name | +| `data.serviceName` | `String` | Service name | +| `data.registeredInstance` | `Object` | Instances registered under this service | +| `data.registeredInstance.ip` | `String` | `IP` address | +| `data.registeredInstance.port` | `int` | Port number | +| `data.registeredInstance.cluster` | `String` | Cluster name | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/publish/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "registeredInstance": { + "ip": "10.128.164.35", + "port": 9950, + "cluster": "DEFAULT" + } + } + ] + } + ``` + +

Query the subscription information of the client (new)

+ +### Description + +Query the subscription information of the specified client + +> Error when client does not exist + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/client/subscribe/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|------------|----------|----------|-------------| +| `clientId` | `String` | **Y** | Client `id` | + +### Return Data + +| Parameter | Type | Description | +|-----------------------------|------------|----------------------------------------------------| +| `data` | `Object[]` | List of services to which the client is subscribed | +| `data.namespace` | `String` | Namespace | +| `data.group` | `String` | Group name | +| `data.serviceName` | `String` | Service name | +| `data.subscriberInfo` | `Object` | Subscription Information | +| `data.subscriberInfo.app` | `String` | Application | +| `data.subscriberInfo.agent` | `String` | Client Information | +| `data.subscriberInfo.addr` | `String` | Address | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/subscribe/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "subscriberInfo": { + "app": "unknown", + "agent": "Nacos-Java-Client:v2.1.0", + "addr": "10.128.164.35" + } + } + ] + } + ``` + +

Query the client that registered the specified service (new)

+ +### Description + +Query the client information of the registered specified service + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/client/service/publisher/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|-----------|----------|---------------------------------------------------------------------| +| `namespaceId` | `String` | N | Namespace`Id`, default is `public` | +| `groupName` | `String` | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | `String` | **Y** | Service name | +| `ephemeral` | `boolean` | N | Whether it is a temporary instance | +| `ip` | `String` | N | `IP` address, default is empty, indicates unrestricted `IP` address | +| `port` | `int` | N | Port number, default is empty, Indicates unrestricted `Port` number | + +### Return Data + +| Parameter | Type | Description | +|-----------------|----------|---------------| +| `data` | | Client list | +| `data.clientId` | `String` | Client `id` | +| `data.ip` | `String` | Client `IP` | +| `data.port` | `int` | Client `port` | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/publisher/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527081276_127.0.0.1_4400", + "ip": "10.128.164.35", + "port": 9950 + }, + { + "clientId": "10.128.164.35:9954#true", + "ip": "10.128.164.35", + "port": 9954 + } + ] + } + ``` + +

Query the information of clients subscribed to the specified service (new)

+ +### Description + +Query the clients subscribed to the specified service + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/ns/client/service/subscriber/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|---------|----------|---------------------------------------------------------------------| +| `namespaceId` | String | N | Namespace`Id`, default is `public` | +| `groupName` | String | N | Group name, default is `DEFAULT_GROUP` | +| `serviceName` | String | **Y** | Service name | +| `ephemeral` | boolean | N | Whether it is a temporary instance | +| `ip` | String | N | `IP` address, default is empty, indicates unrestricted `IP` address | +| `port` | int | N | Port number, default is empty, Indicates unrestricted `Port` number | + +### Return Data + +| Parameter | Type | Description | +|-----------------|----------|---------------| +| `data` | | Client list | +| `data.clientId` | `String` | Client `id` | +| `data.ip` | `String` | Client `IP` | +| `data.port` | `int` | Client `port` | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/subscriber/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527125645_127.0.0.1_4443", + "ip": "10.128.164.35", + "port": 0 + }, + { + "clientId": "172.24.144.1:54126#true", + "ip": "172.24.144.1", + "port": 54126 + } + ] + } + ``` + +## Namespace + +

Query namespaces

+ +### Description + +Query all namespaces + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/console/namespace/list` + +### Return Data + +| Parameter | Type | Description | +|--------------------------|------------|-----------------------------------| +| `data` | `Object[]` | Namespaces | +| `data.namespace` | `String` | Namespace`ID` | +| `data.namespaceShowName` | `String` | Namespace name | +| `data.namespaceDesc` | `String` | Namespace description | +| `data.quota` | `int` | the capacity of Namespace | +| `data.configCount` | `int` | Number of configs under namespace | +| `data.type` | `int` | Namespace type | + +> There are 3 types of Namespace, `0 ` - Global Namespace `1 ` - Default Private Namespace `2 ` - Custom Namespace + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace/list' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "", + "namespaceShowName": "public", + "namespaceDesc": null, + "quota": 200, + "configCount": 1, + "type": 0 + } + ] + } + ``` + +

Query namespace

+ +### Description + +Query information about a specific Namespace + +> Error when namespace does not exist + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/console/namespace` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|---------------| +| `namespaceId` | `String` | **Y** | Namespace`Id` | + +### Return Data + +| Parameter | Type | Description | +|--------------------------|----------|-----------------------------------| +| `data` | `Object` | Namespace | +| `data.namespace` | `String` | Namespace `ID` | +| `data.namespaceShowName` | `String` | Namespace name | +| `data.namespaceDesc` | `String` | Namespace description | +| `data.quota` | `int` | the capacity of Namespace | +| `data.configCount` | `int` | Number of configs under namespace | +| `data.type` | `int` | Namespace type | + +> There are 3 types of Namespace, `0 ` - Global Namespace `1 ` - Default Private Namespace `2 ` - Custom Namespace + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace?namespaceId=test_namespace' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "test_namespace", + "namespaceShowName": "test", + "namespaceDesc": null, + "quota": 200, + "configCount": 0, + "type": 2 + } + } + ``` + +

Create namespace

+ +### Description + +Create a namespace + +> Error when namespace already exists + +### Request Method + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/console/namespace` + +### Request Body + +| Parameter | Type | Required | Description | +|-----------------|----------|----------|-----------------------| +| `namespaceId` | `String` | **Y** | Namespace`Id` | +| `namespaceName` | `String` | **Y** | Namespace name | +| `namespaceDesc` | `String` | N | Namespace Description | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Update namespace

+ +### Description + +Edit namespace + +### Request Method + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/console/namespace` + +### Request Body + +| Parameter | Type | Required | Description | +|-----------------|----------|----------|-----------------------| +| `namespaceId` | `String` | **Y** | Namespace`Id` | +| `namespaceName` | `String` | **Y** | Namespace name | +| `namespaceDesc` | `String` | N | Namespace Description | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test.nacos' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

Delete namespace

+ +### Description + +Delete the specified namespace + +### Request Method + +`DELETE` + +### Request URL + +`/nacos/v2/console/namespace` + +### Request Parameters + +| Parameter | Type | Required | Description | +|---------------|----------|----------|---------------| +| `namespaceId` | `String` | **Y** | Namespace`Id` | + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'namespaceId=test_namespace' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +## Cluster + +

Query the current node

+ +### Description + +Query the current `nacos` node + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/cluster/node/self` + +

Return Data

+ +| Parameter | Type | Description | +|----------------------|----------|--------------------------| +| `data` | `Object` | Current node | +| `data.ip` | `String` | Node `IP` address | +| `data.port` | `int` | Node port | +| `data.state` | `String` | Node status | +| `data.extendInfo` | `Object` | Node extend information | +| `data.address` | `String` | Node address (`IP:port`) | +| `data.failAccessCnt` | `int` | Number of failed access | +| `data.abilities` | `Object` | | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/self' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": { + "ip": "10.128.164.35", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1664521263623, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_persistent_service_v2": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_service_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.1.0" + }, + "address": "10.128.164.35:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + } + } + } + ``` + +

Query the list of cluster nodes

+ +### Description + +Query the information of all nodes in the cluster + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/cluster/node/list` + +### Request Parameters + +| Parameter | Type | Required | Description | +|-----------|----------|----------|--------------------------------| +| `address` | `String` | N | Node address, default is empty | +| `state` | `String` | N | Node status, default is empty | + +> `address`corresponds to the prefix match condition of the Node address to be queried, and is not restricted when it is empty +> +> `state`corresponds to the filter condition of node status and is not restricted when it is empty + +### Return Data + +| Parameter | Type | Description | +|-----------|------------|------------------------------------------| +| `data` | `Object[]` | Node list, refer to [Node info](#Member) | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/list' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "ip": "10.128.164.35", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1664521263623, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_persistent_service_v2": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_service_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.1.0" + }, + "address": "10.128.164.35:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + } + } + ] + } + ``` + +

Query the current node health status

+ +### Description + +Query the current `nacos` node health status + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/cluster/node/self/health` + +

Return Data

+ +| Parameter | Type | Description | +|-----------|----------|----------------------------| +| `data` | `String` | Current node health status | + +> Node has five states: `STARTING`, `UP`, `SUSPICIOUS`, `DOWN` and `ISOLATION`. + +### Example + +* Request Example + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/self/health' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": "UP" + } + ``` + + +

Switch addressing modes

+ +### Description + +Switch addressing modes + +### Request Method + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### Request URL + +`/nacos/v2/core/cluster/lookup` + +### Request Body + +| Parameter | Type | Required | Description | +|-----------|----------|----------|-----------------| +| `type` | `String` | **Y** | Addressing mode | + +> There are two addressing modes: `file` (file configuration) and `address-server` (address server) + +### Return Data + +| Parameter | Type | Description | +|-----------|-----------|-------------------------------------| +| `data` | `boolean` | Whether the execution is successful | + +### Example + +* Request Example + + ```shell + curl -d 'type=file' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/core/cluster/lookup' + ``` + +* Response Example + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +## Connection Load Management + +###

Query the List of Current Node Client Connections

+ +#### Description + +Query the list of client connections on the current `Nacos` node. + +#### Request Method + +`GET` + +#### Request URL + +`/nacos/v2/core/loader/current` + +#### Return Data + +| Parameter | Type | Description | +|----------------|-----------|----------------------| +| `traced` | `Boolean` | Monitoring indicator | +| `abilityTable` | `Map` | Capability table | +| `metaInfo` | `Object` | Metadata | +| `connected` | `Integer` | Connection status | +| `labels` | `Map` | Labels | + +#### Example + +* Request Example + +```shell +curl -X GET 'http://localhost:8848/nacos/v2/core/loader/current' +``` + +* Response Example + +```json +{ + "1697424543845_127.0.0.1_11547": { + "traced": false, + "abilityTable": null, + "metaInfo": { + "connectType": "GRPC", + "clientIp": "192.168.49.1", + "localPort": 9848, + "version": "Nacos-Java-Client:v2.1.0", + "connectionId": "1697424543845_127.0.0.1_11547", + "createTime": "2023-10-16T10:49:03.907+08:00", + "lastActiveTime": 1697424869827, + "appName": "unknown", + "tenant": "", + "labels": { + "source": "sdk", + "taskId": "0", + "module": "config", + "AppName": "unknown" + }, + "tag": null, + "sdkSource": true, + "clusterSource": false + }, + "connected": true, + "labels": { + "source": "sdk", + "taskId": "0", + "module": "config", + "AppName": "unknown" + } + } +} + +``` + +

Reload the Number of Current Node Client Connections

+ +### Description + +Reload the number of client connections on the current `Nacos` node. + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/loader/current/reloadCurrent` + +### Request Parameters + +| Parameter | Type | Required | Description | +|-------------------|-----------|----------|-------------------| +| `count` | `Integer` | **Y** | ID of connections | +| `redirectAddress` | `String` | N | Redirect address | + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|------------------| +| `data` | `String` | Execution result | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://localhost:8848/nacos/v2/core/loader/reloadCurrent?count=1&redirectAddress=127.0.0.1:8848' + ``` + +* Response Example + + ```text + success + ``` + +

Intelligently Balance the Number of Client Connections in the Cluster

+ +### Description + +Intelligently balance the client connections among all nodes in the `Nacos` cluster. + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/loader/current/smartReloadCluster` + +### Request Parameters + +| Parameter | Type | Required | Description | +|----------------|----------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `loaderFactor` | `Float` | N | The loading factor, with a default value of 0.1, determines the number of SDKs per node, calculated as (1 - loaderFactor) * avg ~ (1 + loaderFactor) * avg. | +| `force` | `String` | N | Force flag | + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|------------------| +| `data` | `String` | Execution result | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://localhost:8848/nacos/v2/core/loader/smartReloadCluster?loaderFactor=1' + ``` + +* Response Example + + ```text + Ok + ``` + +

Reset a Specific Client Connection

+ +### Description + +Send a connection reset request based on the `SDK` connection ID. + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/loader/current/reloadClient` + +### Request Body + +| Parameter | Type | Required | Description | +|-------------------|----------|----------|---------------| +| `connectionId` | `String` | **Y** | Connection ID | +| `redirectAddress` | `String` | N | Reset address | + +### Return Data + +| Parameter | Type | Description | +|-----------|----------|------------------| +| `data` | `String` | Execution result | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://localhost:8848/nacos/v2/core/loader/reloadClient?connectionId=1&redirectAddress=127.0.0.1:8848' + ``` + +* Response Example + + ```text + success + ``` + +

Get SDK Metrics for the Cluster

+ +### Description + +Get SDK metrics for all nodes in the `Nacos` cluster. + +### Request Method + +`GET` + +### Request URL + +`/nacos/v2/core/loader/current/cluster` + +### Return Data + +| Parameter | Type | Description | +|------------------|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `total` | `Integer` | Current number of cluster nodes | +| `min` | `Integer` | Minimum load value | +| `avg` | `Integer` | Average load value | +| `max` | `Integer` | Maximum load value | +| `memberCount` | `Integer` | Number of members in the current node | +| `metricsCount` | `Integer` | Number of load information | +| `threshold` | `Float` | Load threshold. The threshold is calculated as: Average load value * 1.1 | +| `detail` | `String` | Contains detailed load information for each node | +| `detail.address` | `Map` | Node address | +| `detail.metric` | `Map` | Metric information | +| `completed` | `Boolean` | Indicates whether the collection of load information has been completed. If true, it means that load information for all nodes has been collected, otherwise, it is false | + +### Example + +* Request Example + + ```shell + curl -X GET 'http://localhost:8848/nacos/v2/core/loader/cluster' + ``` + +* Response Example + + ```json + { + "1697424543845_127.0.0.1_11547": { + "traced": false, + "abilityTable": null, + "metaInfo": { + "connectType": "GRPC", + "clientIp": "192.168.49.1", + "localPort": 9848, + "version": "Nacos-Java-Client:v2.1.0", + "connectionId": "1697424543845_127.0.0.1_11547", + "createTime": "2023-10-16T10:49:03.907+08:00", + "lastActiveTime": 1697424869827, + "appName": "unknown", + "tenant": "", + "labels": { + "source": "sdk", + "taskId": "0", + "module": "config", + "AppName": "unknown" + }, + "tag": null, + "sdkSource": true, + "clusterSource": false + }, + "connected": true, + "labels": { + "source": "sdk", + "taskId": "0", + "module": "config", + "AppName": "unknown" + } + } + } + ``` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/guide/user/other-language.md b/src/content/docs/v3.0/en/guide/user/other-language.md new file mode 100644 index 00000000000..c4cb3c7e9c6 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/other-language.md @@ -0,0 +1,18 @@ +--- +title: Other languages SDK +keywords: [Other languages,SDK] +description: Other languages SDK +sidebar: + order: 2 +--- + +# Other languages SDK + +We will rely mainly on the community to develop multi-language clients. We will recommend the most widely used client to Nacos users as the official one in the future. + +* [go](https://github.com/nacos-group/nacos-sdk-go) +* [cpp](https://github.com/nacos-group/nacos-sdk-cpp) +* [python](https://github.com/nacos-group/nacos-sdk-python) +* [nodejs](https://github.com/nacos-group/nacos-sdk-nodejs) +* [c#](https://github.com/nacos-group/nacos-sdk-csharp) +* more ... diff --git a/src/content/docs/v3.0/en/guide/user/parameters-check.md b/src/content/docs/v3.0/en/guide/user/parameters-check.md new file mode 100644 index 00000000000..a9d0a4edf0f --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/parameters-check.md @@ -0,0 +1,10 @@ +--- +title: Parameter validation +keywords: [Parameter validation,rules] +description: Parameter validation +date: 2023-10-24 +sidebar: + order: 4 +--- + +//TBD \ No newline at end of file diff --git a/src/content/docs/v3.0/en/guide/user/sdk.md b/src/content/docs/v3.0/en/guide/user/sdk.md new file mode 100644 index 00000000000..dcf516ec127 --- /dev/null +++ b/src/content/docs/v3.0/en/guide/user/sdk.md @@ -0,0 +1,671 @@ +--- +title: Java SDK +keywords: [Java,SDK] +description: Java SDK +sidebar: + order: 1 +--- + +# Java SDK + +## Overview + +Maven coordinates +``` + + com.alibaba.nacos + nacos-client + ${version} + +``` + +> Note: Since version 2.0, the Nacos Java SDK introduced gRPC, and use shaded technology to directly encapsulate some dependencies into nacos-client in order to avoid conflicts caused by different versions of gRPC introduced by users, which resulting in a larger nacos-client. +> If the users don't introduce gRPC or confirms that there is no conflict between the versions, and wants to use the pure version of nacos-client to reduce dependencies, users can use the classifier to specify the pure version. + +```xml + + + 2.1.2 + + + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + pure + + + + ${project.groupId} + nacos-common + ${nacos.version} + + + ${project.groupId} + nacos-api + ${nacos.version} + + +``` + +## Configuration Management +### Get configuration +#### Description + +Get configuration from Nacos when a service starts. + +```java +public String getConfig(String dataId, String group, long timeoutMs) throws NacosException +``` + +#### Request parameters + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Name
+
+
Type
+
+
Description
+
+
dataId
+
+
string
+
+
+
Configuration ID. Use a naming rule similar to package.class (for example, com.taobao.tc.refund.log.level) to ensure global uniqueness. It is recommended to indicate business meaning of the configuration in the "class" section. Use + lower case for all characters. Use alphabetical letters and these four special characters (".", ":", "-", "_") only. Up to 256 characters are allowed.
+
+
group
+
+
string
+
+
Configuration group. To ensure uniqueness, format such as product name: module name (for example, Nacos:Test) is preferred. Use alphabetical letters and these four special characters (".", ":", "-", + "_") only. Up to 128 characters are allowed.
+
+
timeout
+
+
long
+
+
Length of configuration read time-out (in ms). Recommended value: 3000.
+
+
+ + +#### Return values + +| Type | Description | +| :--- | :--- | +| string | configuration value | + + +#### Request example + +```java +try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + // Actively get the configuration. + String content = configService.getConfig(dataId, group, 5000); + System.out.println(content); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` + +#### Exception specification + +A ConfigException is thrown in case of a configuration read time-out or a network error. + +### Listen configuration +#### Description + +Use dynamic configuration listening API to enable Nacos to send configuration change notifications. + +```java +public void addListener(String dataId, ConfigChangeListenerAdapter listener) +``` + +#### Request parameters + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Name
+
+
Type
+
+
Description
+
+
dataId
+
+
string
+
+
Configuration ID. Use a naming rule similar to package.class (for example, com.taobao.tc.refund.log.level) to ensure global uniqueness. It is recommended to indicate business meaning of the configuration in the "class" section. Use + lower case for all characters. Use alphabetical letters and these four special characters (".", ":", "-", "_") only. Up to 256 characters are allowed.
+
+
group
+
+
string
+
+
Configuration group. To ensure uniqueness, format such as product name: module name (for example, Nacos:Test) is preferred. Use alphabetical letters and these four special characters (".", ":", "-", + "_") only. Up to 128 characters are allowed.
+
+
listener
+
+
Config Change Listener
+
+
Listener. Configuration changes go into the callback function of the listener.
+
+
+ +#### Return values + +| Type | Description | +| :--- | :--- | +| string | Configuration value. This value is returned through the callback function during initialization or configuration modification. | + +#### Request example + +```java + +// Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +String content = configService.getConfig(dataId, group, 5000); +System.out.println(content); +configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("receive1:" + configInfo); + } + @Override + public Executor getExecutor() { + return null; + } +}); + +// Keep the main thread alive throughout the test, because the configuration subscription runs in a daemon thread, which exits once the main thread exits. The following code is not required in a real environment. +while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } +} +``` + +### Delete Listening +#### Description + +Cancel listen configuration. No more notification after cancellation. + +```java +public void removeListener(String dataId, String group, Listener listener) +``` + +#### Request parameters + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Name
+
+
Type
+
+
Description
+
+
dataId
+
+
string
+
+
Configuration ID. Use a naming rule similar to package.class (for example, com.taobao.tc.refund.log.level) to ensure global uniqueness. It is recommended to indicate business meaning of the configuration in the "class" section. Use + lower case for all characters. Use alphabetical letters and these four special characters (".", ":", "-", "_") only. Up to 256 characters are allowed.
+
+
group
+
+
string
+
+
Configuration group
+
+
listener
+
+
ConfigChangeListenerAdapter
+
+
Listener. Configuration changes go into the callback function of the listener.
+
+
+ +#### Request example + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +configService.removeListener(dataId, group, yourListener); +``` + +### Publish configuration +#### Description + +Publish Nacos configurations automatically to reduce the operation and maintenance cost. + +__Note:__ It uses the same publishing interface to create or modify a configuration. If the specified configuration doesn’t exist, it will create a configuration. If the specified configuration exists, it will update the configuration. + +```java +public boolean publishConfig(String dataId, String group, String content) throws NacosException; + +@Since 1.4.1 +public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException; + +``` + +#### Request parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| dataId | string | Configuration ID. Naming rule is similar to package.class (com.taobao.tc.refund.log.level) is used to ensure the global uniqueness We recommend that you define class by business meaning. All characters must be in lower case. Use alphabetical letters and these four special characters (".", ":", "-", "\_") only. Up to 256 characters are allowed. | +| group | string | Configuration group. We recommend that you use product name: module name (for example Nacos:Test) to ensure the uniqueness. Use alphabetical letters and these four special characters (".", ":", "-", "\_") only. Up to 128 characters are allowed. | +| content | string | Configuration content. No more than 100K bytes. | +| type | string | @Since 1.4.1. Configuration type. See com.alibaba.nacos.api.config.ConfigType, default as TEXT. | + +#### Response parameters + +| Type | Description | +| :--- | :--- | +| boolean | If the publishing is successful | + + +#### Request example + +```java +try { + // Initialize the configuration service. Retrieves the following parameters in console with sample code + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isPublishOk = configService.publishConfig(dataId, group, "content"); + System.out.println(isPublishOk); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` + +#### Exceptions + +In case of reading configuration timeout or network issues, ConfigException exception is thrown. + + +### Delete configuration +#### Description + +It deletes Nacos configurations automatically with program to reduce operation and maintenance costs with automation. + +__Note:__ If the specified configuration exists, then it deletes the configuration. If the specified configuration doesn’t exist, then it returns a successful message. + +```java +public boolean removeConfig(String dataId, String group) throws NacosException + +``` + +#### Request parameters + + +| Parameter name | Parameter type | Description | +| :--- | :--- | :--- | +| dataId | String | Configuration ID | +| group | String | Configuration group | + + +#### Response parameters + + +| Parameter type | Description | +| :--- | :--- | +| boolean | If the deletion is successful | + + +#### Request example + +```java +try { + // Initialize the configuration service. Retrieves the following parameters in console with sample code + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isRemoveOk = configService.removeConfig(dataId, group); + System.out.println(isRemoveOk); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` + +#### Exceptions + +In case of reading configuration timeout or network issues, ConfigException exception is thrown. + +## Service Discovery SDK +### Register Instance +#### Description +Register an instance to service. +```java +void registerInstance(String serviceName, String ip, int port) throws NacosException; + +void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; + +void registerInstance(String serviceName, Instance instance) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | --- | +| serviceName | String | service name | +| ip | String | instance ip | +| port | int | instance port | +| clusterName | String | cluster name | +| instance | Refer to Java docs | instance properties | + +#### Response +void +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.registerInstance("nacos.test.3", "11.11.11.11", 8888, "TEST1"); + +Instance instance = new Instance(); +instance.setIp("55.55.55.55"); +instance.setPort(9999); +instance.setHealthy(false); +instance.setWeight(2.0); +Map instanceMeta = new HashMap<>(); +instanceMeta.put("site", "et2"); +instance.setMetadata(instanceMeta); + +Service service = new Service("nacos.test.4"); +service.setApp("nacos-naming"); +service.setHealthCheckMode("server"); +service.setProtectThreshold(0.8F); +service.setGroup("CNCF"); +Map serviceMeta = new HashMap<>(); +serviceMeta.put("symmetricCall", "true"); +service.setMetadata(serviceMeta); +instance.setService(service); + +Cluster cluster = new Cluster(); +cluster.setName("TEST5"); +AbstractHealthChecker.Http healthChecker = new AbstractHealthChecker.Http(); +healthChecker.setExpectedResponseCode(400); +healthChecker.setCurlHost("USer-Agent|Nacos"); +healthChecker.setCurlPath("/xxx.html"); +cluster.setHealthChecker(healthChecker); +Map clusterMeta = new HashMap<>(); +clusterMeta.put("xxx", "yyyy"); +cluster.setMetadata(clusterMeta); + +instance.setCluster(cluster); + +naming.registerInstance("nacos.test.4", instance); +``` + +### Deregister Instance +#### Description +Remove instance from service. +```java +void deregisterInstance(String serviceName, String ip, int port) throws NacosException; + +void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | :--- | +| serviceName | String | service name | +| ip | String | instance ip | +| port | int | instance port | +| clusterName | String | cluster name | + +#### Response +None +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.deregisterInstance("nacos.test.3", "11.11.11.11", 8888, "DEFAULT"); +``` +### Get all instances of service +#### Description +Get all instances of service. +```java +List getAllInstances(String serviceName) throws NacosException; + +List getAllInstances(String serviceName, List clusters) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | --- | +| serviceName | String | service name | +| clusters | List | cluster list | + +#### Response +List<Instance> instance list。 +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.getAllInstances("nacos.test.3")); +``` + +### Get filtered list of instance +#### Description +Get healthy or unhealthy instances of service. +```java +List selectInstances(String serviceName, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | --- | +| serviceName | String | service name | +| clusters | List | cluster list | +| healthy | boolean | healthy or not | + +#### Response +List<Instance> instance list. +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.selectInstances("nacos.test.3", true)); +``` + +### Get one healthy instance +#### Description +Get one healthy instance selected by load-balance strategy. +```java +Instance selectOneHealthyInstance(String serviceName) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | --- | +| serviceName | String | service name | +| clusters | List | cluster list | + +#### Response +Instance + +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.selectOneHealthyInstance("nacos.test.3")); +``` + +### Listen Service +#### Description +Listen for changes of instances under a service. +```java +void subscribe(String serviceName, EventListener listener) throws NacosException; + +void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | --- | +| serviceName | String | service name | +| clusters | List | cluster list | +| listener | EventListener | event listener | + +#### Response +void + +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.subscribe("nacos.test.3", event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}); +``` + +### Unlisten Service +#### Description +Cancel listening service. +```java +void unsubscribe(String serviceName, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException; +``` + +#### Request Parameters + +| Name | Type | Description | +| :--- | :--- | --- | +| serviceName | String | service name | +| clusters | List | cluster list | +| listener | EventListener | event listener | + +#### Response +void + +#### Request Example +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.unsubscribe("nacos.test.3", event -> {}); +``` diff --git a/src/content/docs/v3.0/en/manual/admin/admin-api.md b/src/content/docs/v3.0/en/manual/admin/admin-api.md new file mode 100644 index 00000000000..3de19ff807a --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/admin-api.md @@ -0,0 +1,974 @@ +--- +title: 运维API +keywords: [Nacos,运维API] +description: Nacos Server的一些运维API,一般给予控制台使用或需要进行自定义Nacos运维工具开发的相关程序和人员使用。 +sidebar: + order: 5 +--- + +# 运维API + +Nacos默认搭载了一整套专为管理控制台和运维人员设计的运维API,赋予运维专家更多的配置权限、更广阔的数据检索能力等。这些API为Nacos的运维团队提供了方便,使他们能够高效地处理故障、排查问题,以确保系统的稳定运行。 + +## 1. Nacos Core 运维 API + +### 1.1. 获取当前节点连接 + +#### 接口描述 + +通过该接口,可以获取连接到当前Nacos Server节点中的gRPC连接详情。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/current` + +#### 请求参数 + +无 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| ${connectionId} | `jsonString` | 每条gRPC连接的连接id | +| ${connectionId}.abilityTable | `jsonString` | 该gRPC连接(即客户端)支持的能力列表 | +| ${connectionId}.metaInfo.clientIp | `String` | 该gRPC连接的来源IP | +| ${connectionId}.metaInfo.localPort | `int` | 该Nacos Server的gRPC端口 | +| ${connectionId}.metaInfo.version | `String` | 该gRPC连接(即客户端)的版本 | +| ${connectionId}.metaInfo.createTime | `String` | 该gRPC连接的连接时间 | +| ${connectionId}.metaInfo.lastActiveTime | `timestamp` | 该gRPC连接的最后一次的心跳时间 | +| ${connectionId}.metaInfo.labels.source | `String` | 该gRPC连接的模块,可选值为`naming`,`config`和`cluster`分别代表注册中心、配置中心以及集群间的连接 | +| ${connectionId}.metaInfo.clusterSource | `boolean` | 该gRPC连接的是否为集群间连接,为`true`时,`${connectionId}.metaInfo.labels.source`为 `cluster` | +| ${connectionId}.metaInfo.sdkSource | `boolean` | 该gRPC连接的是否为客户端来源连接,为`true`时,`${connectionId}.metaInfo.labels.source`为 `naming`或`config`| + + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/current' +``` + +* 返回示例 + +```json +{ + "1709273546779_127.0.0.1_35042": { + "traced": false, + "abilityTable": {}, + "metaInfo": { + "connectType": "GRPC", + "clientIp": "127.0.0.1", + "localPort": 9849, + "version": "Nacos-Java-Client:v2.4.2", + "connectionId": "1709273546779_127.0.0.1_35042", + "createTime": "2024-03-01T14:12:26.800+08:00", + "lastActiveTime": 1710754816373, + "appName": "-", + "tenant": null, + "labels": { + "source": "naming", + "tls.enable": "false" + }, + "tag": null, + "clusterSource": false, + "sdkSource": true + }, + "connected": true, + "labels": { + "source": "naming", + "tls.enable": "false" + } + } +} +``` + +### 1.2. 均衡指定数量的连接 + +#### 接口描述 + +通过该接口,可以指定一定数量的连接到当前Nacos Server节点中的gRPC连接,将这部分连接断开后迁移到其他Nacos Server节点中。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/reloadCurrent` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `count` | `Integer` | **是** | 需要均衡的连接个数 | +| `redirectAddress` | `String` | 否 | 预期均衡的Nacos Server目标,仅提供给客户端参考。 | + +#### 返回数据 + +成功则返回`success`,失败则返回[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式) + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/reloadCurrent?count=100' +``` + +* 返回示例 + +```text +success +``` + +### 1.3. 均衡指定的单个连接 + +#### 接口描述 + +通过该接口,可以指定某一条的连接到当前Nacos Server节点中的gRPC连接,将该连接断开后迁移到其他Nacos Server节点中。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/reloadClient` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `connectionId` | `String` | **是** | 需要均衡的连接Id | +| `redirectAddress` | `String` | 否 | 预期均衡的Nacos Server目标,仅提供给客户端参考。 | + +#### 返回数据 + +成功则返回`success`,失败则返回[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式) + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/reloadClient?connectionId=1709273546779_127.0.0.1_35042' +``` + +* 返回示例 + +```text +success +``` + +### 1.4. 获取集群连接概览信息 + +#### 接口描述 + +通过该接口,查看Nacos Server集群中各节点的连接数概览。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/cluster` + +#### 请求参数 + +无 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| `total` | `Integer` | 该集群中所有节点的连接数总和 | +| `min` | `Integer` | 该集群中所有节点的最小连接数 | +| `avg` | `Integer` | 该集群中所有节点的平均连接数 | +| `max` | `Integer` | 该集群中所有节点的最大连接数 | +| `memberCount` | `Integer` | 该集群中所有节点的个数 | +| `metricsCount` | `Integer` | 该集群中已统计到概览信息的节点个数 | +| `detail` | `jsonArray` | 该集群中所有节点的概览信息,格式见下表 | +| `detail[].address` | `String` | 节点地址 | +| `detail[].metric.load` | `Double` | 节点的负载率,主要对应节点的Load指标,参考值 | +| `detail[].metric.sdkConCount` | `Integer` | 连接到该节点的SDK连接数,主要对应客户端连接数 | +| `detail[].metric.conCount` | `Integer` | 连接到该节点的总连接数,包含了SDK和集群间的连接 | +| `detail[].metric.cpu` | `Double` | 节点的CPU使用率,参考值 | + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/cluster' +``` + +* 返回示例 + +```json +{ + "total": 0, + "min": 0, + "avg": 0, + "max": 0, + "memberCount": 3, + "metricsCount": 3, + "threshold": 0.0, + "detail": [{ + "address": "nacos-node1:8848", + "metric": { + "load": "0.0", + "sdkConCount": "0", + "cpu": "0.0", + "conCount": "2" + } + }, { + "address": "nacos-node2:8848", + "metric": { + "load": "0.03", + "sdkConCount": "0", + "cpu": "-1.0", + "conCount": "2" + } + }, { + "address": "nacos-node3:8848", + "metric": { + "load": "0.0", + "sdkConCount": "0", + "cpu": "-1.0", + "conCount": "2" + } + }], + "completed": true +} +``` + +### 1.5. 获取本节点信息 + +#### 接口描述 + +通过该接口,可以获取Nacos Server集群当前节点的详细信息。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/cluster/node/self` + +#### 请求参数 + +无 + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式),下表只阐述`data`字段中的返回参数。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`ip`|`String` | 节点IP | +|`port`|`Integer` | 节点端口 | +|`state`|`String` | 节点状态,可选值为`UP`、`DOWN`、`SUSPICIOUS` | +|`extendInfo`|`jsonObject` | 节点扩展信息,具体字段见下表 | +|`extendInfo.lastRefreshTime`|`Long` | 节点上一次更新时间戳,单位毫秒 | +|`extendInfo.raftMetaData`|`jsonObject` | 节点的Raft元数据, 包含每个Raft Group的 `leader`, `term`等字段 | +|`extendInfo.raftPort`|`Integer` | 节点的Raft端口 | +|`extendInfo.version`|`String` | 节点的版本 | +|`address`|`String` | 节点地址,格式为`ip:port` | +|`abilities`|`jsonObject` | 该节点所支持的能力 | +|~~extendInfo.readyToUpgrade~~|`Boolean` | 是否ready升级到Nacos2.0,于2.2版本后废弃,即将移除 | + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/self' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": { + "ip": "nacos-node-0", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1709273550501, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service": { + "leader": "nacos-node-1:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service_v2": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 2 + }, + "naming_service_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.4.2" + }, + "address": "nacos-node-0:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true, + "grpcReportEnabled": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + }, + "grpcReportEnabled": true + } +} +``` + +### 1.6. 获取集群所有节点信息 + +#### 接口描述 + +通过该接口,可以获取Nacos Server集群中所有节点的详细信息。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/cluster/node/list` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`address`|`String`|否|过滤的节点地址,支持前缀匹配,不输入时返回所有节点信息| +|`nodeState`|`String`|否|返回的节点状态,可选值为`UP`、`DOWN`、`SUSPICIOUS`,不输入时返回所有节点信息| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式),`data`字段为[获取本节点信息](#返回数据-4)的返回数据的列表。 + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/list' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": [{ + "ip": "nacos-node-0", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1709273550501, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service": { + "leader": "nacos-node-1:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service_v2": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 2 + }, + "naming_service_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.4.2" + }, + "address": "nacos-node-0:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true, + "grpcReportEnabled": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + }, + "grpcReportEnabled": true + }, { + "ip": "nacos-node-2", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1710813796567, + "raftMetaData": { + .... + }, + .... + }, + .... + }, { + "ip": "nacos-node-1", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1710813796567, + "raftMetaData": { + .... + }, + .... + }, + .... + }] +} +``` + +### 1.7. 快速查询本节点健康状态 + +#### 接口描述 + +通过该接口,可以快速查询本节点健康状态。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/cluster/node/self/health` + +#### 请求参数 + +无 + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|`UP`表示节点健康,`DOWN`表示节点不健康,`SUSPICIOUS`表示节点疑似不健康| + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/list' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": "UP" +} +``` + +### 1.8. 动态修改Server集群地址发现方式 + +#### 接口描述 + +通过该接口,可以在不重启Nacos Server的情况下,动态切换Nacos Server集群地址发现的方式,目前支持两种方式:`file`和`address-server`。 + +#### 请求方式 + +`PUT` + +#### 请求URL + +`/nacos/v2/core/cluster/lookup` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`type`|`String`|是|切换到的地址发现方式,可选值为`file`和`address-server`| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`Boolean`|`true`表示更新成功,`false`表示更新失败。| + +#### 示例 + +* 请求示例 + +```shell +curl -X PUT 'http://127.0.0.1:8848/nacos/v2/core/cluster/lookup?type=file' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": true +} +``` + +### 1.9. Raft 相关操作 + +#### 接口描述 + +通过该接口,可以对Nacos Server集群中的Raft协议进行部分运维操作,如执行快照,主动选主等。 + +#### 请求方式 + +`POST` + +#### 请求URL + +`/nacos/v2/core/ops/raft` + +#### 请求参数 + +该API需要以Json的方式,将请求参数放在请求体中,请求体格式如下: + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`command` |`String`|**是**|Raft运维操作指令,具体的命令请参考下表。| +|`value`|`String`|**是**|命令的参数,具体的命令内容请参考下表。| +|`groupId` |`String`|否|Raft集群的groupId,如果不输入则对所有Raft Group生效| + +|command|value|说明| +|---------------|----------|--------| +|`doSnapshot`|`${nacos-server-address}:${raft-port}`|执行快照,参数为要执行快照的节点地址。| +|`transferLeader`|`${nacos-server-address}:${raft-port}`|主动选主,参数为要期望的Leader的节点地址。| +|`restRaftCluster`|`${nacos-server-address}:${raft-port}[,${nacos-server-address}:${raft-port}]`|重置集群状态,参数为要重置节点地址列表,','分割。| +|`removePeer`|`${nacos-server-address}:${raft-port}`|移除Raft Member节点,参数为要移除的节点地址。| +|`removePeers`|`${nacos-server-address}:${raft-port}[,${nacos-server-address}:${raft-port}]`|批量移除Raft Member节点,参数为要批量移除的节点地址列表,','分割。| +|`changePeers`|`${nacos-server-address}:${raft-port}[,${nacos-server-address}:${raft-port}]`|修改Raft Member节点,参数为要修改后的节点地址列表,','分割。| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|固定为`null`。| + +#### 示例 + +* 请求示例 + +```shell +curl -X POST -H 'Content-Type:application/json' 'http://127.0.0.1:8848/nacos/v2/core/ops/raft' -d '{"command":"doSnapshot","value":"nacos-node-0:7848"}' +``` + +* 返回示例 + +```json +{ + "code": 200, + "message": null, + "data": null +} +``` + +### 1.10. 动态修改Nacos Core相关日志级别 + +#### 接口描述 + +通过该接口,可以在不重启Nacos Server的情况下,动态修改Nacos Core相关日志级别的配置。 + +#### 请求方式 + +`PUT` + +#### 请求URL + +`/nacos/v2/core/ops/log` + +#### 请求参数 + +该API需要以Json的方式,将请求参数放在请求体中,请求体格式如下: + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`logName` |`String`|**是**|具体的日志文件的名称,具体支持的日志名称见下表。| +|`logLevel`|`String`|**是**|日志的级别,可选值为`ALL`、`TRACE`、`DEBUG`、`INFO`、`WARN`、`ERROR`、`OFF`。| + +|logName|对应的具体日志文件| +|---------------|----------| +|`core-auth`|`core-auth.log`| +|`core-raft`|`protocol-raft.log`| +|`core-distro`|`protocol-distro.log`| +|`core-cluster`|`nacos-cluster.log`| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|固定为`null`。| + +#### 示例 + +* 请求示例 + +```shell +curl -X PUT -H 'Content-Type:application/json' 'http://127.0.0.1:8848/nacos/v2/core/ops/log' -d '{"logName":"core-distro","logLevel":"DEBUG"}' +``` + +* 返回示例 + +```json +{ + "code": 200, + "message": null, + "data": null +} +``` + +## 2. Nacos Naming 运维 API + +### 2.1. 查看Naming模块的相关开关 + +#### 接口描述 + +通过该接口,可以查看Nacos Naming模块的相关开关。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/operator/switches` 或 `/nacos/v2/ns/ops/switches` + +#### 请求参数 + +无 + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式),返回数据`data`字段为json格式,展示各个开关的和配置的具体内容: + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`clientBeatInterval`|`int`|Nacos1.X客户端的默认心跳间隔| +|`defaultCacheMillis`|`int`|客户端订阅的服务列表的默认缓存时间| +|`defaultPushCacheMillis`|`int`|推送的服务列表的默认缓存时间,优先级高于`defaultCacheMillis`| +|`distroEnabled` |`boolean`|是否开启`Distro`协议同步,仅当集群压力过大,影响到集群稳定性时,临时修改为`false`缓解,改为`false`后可能导致部分数据不一致,需要尽快恢复| +|`healthCheckEnabled`|`boolean`|是否开启健康检查,仅当集群压力过大,影响到集群稳定性时,临时修改为`false`缓解,改为`false`后不会因为心跳过期,tcp/http探测超时而修改实例的健康状态,以及不会因过期删除实例,需要尽快恢复| +|`lightBeatEnabled` |`boolean`|是否开启轻量心跳,针对Nacos`1.2.X~1.4.X版本`客户端生效,修改为`false`后,`Nacos1.2.X~1.4.X`版本客户端将使用全量心跳进行续约| +|`pushEnabled`|`boolean`|是否开启推送功能,仅当集群压力过大,影响到集群稳定性时,临时修改为`false`,改为`false`后,Nacos客户端将不再收到服务端的主动推送| +|`push${Language}Version`|`String`|可支持推送的最小客户端版本,当不希望针对小于某些版本进行数据推送时,可以修改该值,比如修改pushJavaVersion为`2.0.0`,则小于2.0.0的Java客户端将不会收到推送数据| +|`${type}HealthParams`|`json`|健康检查参数,设置健康检查的最大/最小间隔,随机间隔系数等,健康检查时将根据这几个值进行下一次健康检查流量的打散。| + +> 注意: 其余未列出的参数,均为Nacos旧版本的开关或配置内容,已废弃或即将废弃,请谨慎使用。 + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/ops/switches' +``` + +* 返回示例 + + +```json +{ + "code": 0, + "data": { + "adWeightMap": {}, + "autoChangeHealthCheckEnabled": true, + "checkTimes": 3, + "checksum": null, + "clientBeatInterval": 5000, + "defaultCacheMillis": 3000, + "defaultInstanceEphemeral": true, + "defaultPushCacheMillis": 10000, + "disableAddIP": false, + "distroEnabled": true, + "distroServerExpiredMillis": 10000, + "distroThreshold": 0.7, + "enableAuthentication": false, + "enableStandalone": true, + "healthCheckEnabled": true, + "healthCheckWhiteList": [], + "httpHealthParams": { + "factor": 0.85, + "max": 5000, + "min": 500 + }, + "incrementalList": [], + "lightBeatEnabled": true, + "limitedUrlMap": {}, + "masters": null, + "mysqlHealthParams": { + "factor": 0.65, + "max": 3000, + "min": 2000 + }, + "name": "00-00---000-NACOS_SWITCH_DOMAIN-000---00-00", + "overriddenServerStatus": null, + "pushCSharpVersion": "0.9.0", + "pushCVersion": "1.0.12", + "pushEnabled": true, + "pushGoVersion": "0.1.0", + "pushJavaVersion": "0.1.0", + "pushPythonVersion": "0.4.3", + "sendBeatOnly": false, + "serverStatusSynchronizationPeriodMillis": 2000, + "serviceStatusSynchronizationPeriodMillis": 5000, + "tcpHealthParams": { + "factor": 0.75, + "max": 5000, + "min": 1000 + } + }, + "message": "success" +} +``` + +### 2.2. 修改Naming模块的相关开关 + +#### 接口描述 + +通过该接口,可以修改Nacos Naming模块的相关开关。 + +#### 请求方式 + +`PUT` + +#### 请求URL + +`/nacos/v2/ns/operator/switches` 或 `/nacos/v2/ns/ops/switches` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|--------|----------|----|----------| +|`entry`|`String`|**是**|修改的开关或配置名称| +|`value`|`Object`|**是**|开关或配置的新值,不同的开关或配置的类型不同,具体请参考[开关和配置参数](#返回数据-10)| +|`debug`|`boolean`|否|是否开启调试模式,开启后,修改的配置不会同步到集群其他节点中,仅在本节点生效,默认为`false`| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式): + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|成功为`ok`,否则为`null`| + +#### 示例 + +* 请求示例 + +```shell +curl -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/ops/switches?entry=pushEnabled&value=false' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": "ok" +} +``` + +### 2.3. 查询系统当前数据指标 + +#### 接口描述 + +通过该接口,可以查询系统当前数据指标 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/operator/metrics` 或 `/nacos/v2/ns/ops/metrics` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------|-----------|----------|--------------------------| +| `onlyStatus` | `boolean` | 否 | 只显示状态,默认为`true` | + +> 当`onlyStatus`设置为`true`时,只返回表示系统状态的字符串 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|------------------------------------|----------|--------------------| +| `data` | `Object` | 系统当前数据指标 | +| `data.status` | `String` | 系统状态 | +| `data.serviceCount` | `int` | 服务数量 | +| `data.instanceCount` | `int` | 实例数量 | +| `data.subscribeCount` | `int` | 订阅数量 | +| `data.raftNotifyTaskCount` | `int` | `Raft`通知任务数量 | +| `data.responsibleServiceCount` | `int` | | +| `data.responsibleInstanceCount` | `int` | | +| `data.clientCount` | `int` | 客户端数量 | +| `data.connectionBasedClientCount` | `int` | 连接数量 | +| `data.ephemeralIpPortClientCount` | `int` | 临时客户端数量 | +| `data.persistentIpPortClientCount` | `int` | 持久客户端数量 | +| `data.responsibleClientCount` | `int` | | +| `data.cpu` | `float` | `cpu`使用率 | +| `data.load` | `float` | 负载 | +| `data.mem` | `float` | 内存使用率 | + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/operator/metrics' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": { + "status": "UP", + "serviceCount": 2, + "instanceCount": 2, + "subscribeCount": 2, + "raftNotifyTaskCount": 0, + "responsibleServiceCount": 0, + "responsibleInstanceCount": 0, + "clientCount": 2, + "connectionBasedClientCount": 2, + "ephemeralIpPortClientCount": 0, + "persistentIpPortClientCount": 0, + "responsibleClientCount": 2, + "cpu": 0, + "load": -1, + "mem": 1 + } +} +``` + +## 3. Nacos Config 运维 API + +### 3.1. 查询指定命名空间下的配置列表 + +#### 接口描述 + +获取指定命名空间下的配置信息列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history/configs` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|----------| +| `namespaceId` | `String` | **是** | 命名空间 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|------------|----------------------| +| `data` | `Object[]` | 配置信息列表 | +| `data.id` | `String` | 配置`id` | +| `data.dataId` | `String` | 配置名 | +| `data.group` | `String` | 配置分组 | +| `data.content` | `String` | 配置内容 | +| `data.md5` | `String` | 配置内容的md5值 | +| `data.encryptedDataKey` | `String` | | +| `data.tenant` | `String` | 租户信息(命名空间) | +| `data.appName` | `String` | 应用名 | +| `data.type` | `String` | 配置文件类型 | +| `data.lastModified` | `long` | 上次修改时间 | + +> 返回数据中的配置信息只有`dataId`, `group`, `tenant`, `appName`, `type`字段有效,其他字段为默认值 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/configs?namespaceId=' + ``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": [ + { + "id": "0", + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "content": null, + "md5": null, + "encryptedDataKey": null, + "tenant": "", + "appName": "", + "type": "yaml", + "lastModified": 0 + } + ] +} +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/admin/auth.mdx b/src/content/docs/v3.0/en/manual/admin/auth.mdx new file mode 100644 index 00000000000..b48d2b7d5e0 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/auth.mdx @@ -0,0 +1,167 @@ +--- +title: 权限校验 +keywords: [Authorization] +description: 本文介绍了如何开启Nacos的鉴权(访问控制)功能,及一些示例。 +sidebar: + order: 6 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# 权限校验 + +> 注意 +> - Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。 +> - Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。 +> - 如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做进行[自定义插件开发](../../plugin/auth-plugin.md)。 + +## 1. 相关参数说明 + +|参数名|默认值|对应docker镜像环境变量|说明| +|-----|------|------|----| +|nacos.core.auth.enabled|false|NACOS_AUTH_ENABLE|是否开启鉴权功能| +|nacos.core.auth.system.type|nacos|NACOS_AUTH_SYSTEM_TYPE|鉴权类型| +|nacos.core.auth.plugin.nacos.token.secret.key|无默认值|NACOS_AUTH_TOKEN|默认鉴权插件用于生成用户登陆临时accessToken所使用的密钥,**使用老版本默认值有安全风险**| +|nacos.core.auth.plugin.nacos.token.expire.seconds|18000|NACOS_AUTH_TOKEN_EXPIRE_SECONDS|用户登陆临时accessToken的过期时间| +|nacos.core.auth.server.identity.key|无默认值|NACOS_AUTH_IDENTITY_KEY|用于服务端之间请求的身份识别key,**使用老版本默认值有安全风险**| +|nacos.core.auth.server.identity.value|无默认值|NACOS_AUTH_IDENTITY_VALUE|用于服务端之间请求的身份识别value,**使用老版本默认值有安全风险**| +|~~nacos.core.auth.enable.userAgentAuthWhite~~|false|NACOS_AUTH_USER_AGENT_AUTH_WHITE_ENABLE|是否使用useragent白名单,主要用于适配老版本升级,**置为true时有安全风险**| +|nacos.core.auth.plugin.nacos.token.cache.enable|false|NACOS_AUTH_CACHE_ENABLE|是否开启Token缓存,默认关闭,开启后accessToken会缓存到内存中,但权限更新时可能存在15s左右的延迟。| + +## 2. 默认控制台登录页 + +在用户开启鉴权后,控制台才需要进行登录访问。 同时针对不同的鉴权插件,提供新的接口方法,用于提示控制台是否开启登录页;同时Nacos支持关闭开源控制台,并引导到用户自定义的Nacos控制台,详情可查看[Nacos鉴权插件-服务端插件](../../plugin/auth-plugin.md)及[控制台手册-关闭登录功能](./console.md#32-关闭登录功能) + +## 3. 服务端如何开启鉴权 + + + + - 配置`token.secret.key` + + 开启鉴权之前,你需要配置自定义的用于生成JWT令牌的密钥,在application.properties中的配置信息,自定义密钥时,推荐将配置项设置为**Base64编码**的字符串,且**原始密钥长度不得低于32字符**: + + > 注意: + > 1. 密钥需要保持节点间一致,长时间不一致可能导致403 invalid token错误。 + > 2. 修改`token.secret.key`时,请确保token是有效的,如果修改成无效值,会导致后续无法登录,请求访问异常。 + + ```properties + ### 用于生成JWT令牌的密钥(Base64 String): + nacos.core.auth.default.token.secret.key=$custom_base64_token_secret_key + ``` + + - 配置`server.identity` + + 开启鉴权之前,还需要配置用于服务端之间请求的身份识别信息,在application.properties中的配置信息: + + > 注意:身份识别信息保持节点间一致,长时间不一致可能导致节点见数据不一致。 + + ```properties + ### 配置自定义身份识别的key(不可为空)和value(不可为空) + nacos.core.auth.server.identity.key=$custom_server_identity_key + nacos.core.auth.server.identity.value=$custom_server_identity_value + ``` + + - 打开鉴权开关 + + 开启鉴权开关,需要修改application.properties中的配置信息为: + ```properties + ### If turn on auth system: + nacos.core.auth.system.type=nacos + nacos.core.auth.enabled=true + ``` + + > 注意:鉴权开关是修改之后立马生效的,不需要重启服务端。 + + + + + 使用官方镜像,请在启动docker容器时,添加如下环境变量: + ```powershell + NACOS_AUTH_ENABLE=true + NACOS_AUTH_TOKEN=${custom_base64_token_secret_key} + NACOS_AUTH_IDENTITY_KEY=$custom_server_identity_key + NACOS_AUTH_IDENTITY_VALUE=$custom_server_identity_value + ``` + + 例如,可以通过如下命令运行开启了鉴权的容器: + + ```powershell + docker run --env PREFER_HOST_MODE=hostname --env MODE=standalone --env NACOS_AUTH_ENABLE=true --env NACOS_AUTH_TOKEN=$custom_base64_token_secret_key --env NACOS_AUTH_IDENTITY_KEY=$custom_server_identity_key --env NACOS_AUTH_IDENTITY_VALUE=$custom_server_identity_value -p 8848:8848 -p 9848:9848 nacos/nacos-server + ``` + + + +### 3.1. 设置管理员密码 + +自2.4.0版本开始,Nacos构建时不再提供管理员用户`nacos`的默认密码,需要在首次开启鉴权后,通过API或Nacos控制台进行管理员用户`nacos`的密码初始化步骤,具体操作如下: + + + + 通过Admin API,指定或随机生成一个管理员用户`nacos`的密码,当密码指定或生成后,该API将无法再进行调用。 + + ```powelshell + curl -X POST 'http://$nacos_server_host:$nacos_server_port/nacos/v1/auth/users/admin' -d 'password=$your_password' + ``` + + > 注意:若输入的`$your_password`为空字符串,或未带有`password`参数,Nacos将会生成随机密码,请保存好生成的随机密码。 + + 此时若指定或生成随机密码成功后,会返回如下: + + ```json + {"username":"nacos", "password":"$your_password"} + ``` + + + + 当Nacos集群开启鉴权后访问Nacos控制台时,会校验是否已经初始化过管理员用户`nacos`的密码,若发现未初始化密码时,则会跳转至初始化密码的页面进行初始化。在该页面密码文本框内输入自定义密码,然后点击提交即可; + + > 注意:若密码文本框内未输入自定义密码或输入空白密码,Nacos将会生成随机密码,请保存好生成的随机密码。 + + 初始化成功后会弹窗提示初始化成功,并展示指定的密码或随机生成的密码,请保存好此密码。 + + + + +### 3.2. 密码强度注意事项 + +> 重要:在Nacos在初始化管理员用户`nacos`、创建或用户密码更新操作期间不强制执行特定密码长度。Nacos集群管理员有责任对自行构建的密码强度进行控制。为了避免与密码强度相关的安全风险,强烈建议初始化时使用强度较高的密码。 + +### 3.3. 升级集群注意事项 + +> 若Nacos集群升级自旧版本的Nacos,Nacos会认为当前已经存在管理员用户`nacos`,因此并不会要求进行管理员用户`nacos`的初始化流程,管理员用户`nacos`的密码仍然会保留旧版本时所使用的密码;因此若是使用旧版本时未修改过管理员用户`nacos`的默认密码,强烈建议升级后尽快修改管理员用户`nacos`的密码。 + +## 4. 客户端如何进行鉴权 + +请参考[用户手册-配置鉴权](../user/auth.mdx) + +## 5. 开启Token缓存功能 + +无论是客户端SDK还是OpenAPI,在调用login接口获取accessToken之后,携带accessToken访问服务端,服务端解析Token进行鉴权。解析的动作比较耗时,如果想要提升接口的性能,可以考虑开启缓存Token的功能,用字符串比较代替Token解析。 + +默认鉴权插件模块支持token缓存功能,可参见[ISSUE #9906](https://github.com/alibaba/nacos/issues/9906) + +### 5.1. 开启方式 + + + + + 在application.properties中的配置信息: + ```properties + nacos.core.auth.plugin.nacos.token.cache.enable=true + ``` + + + + 镜像暂不支持该功能 + + + + +### 5.2. 注意事项 +在开启Token缓存功能之前,服务端对每一个携带用户名密码访问login接口的请求都会生成新的token,接口的返回值中的tokenTtl字段跟服务端配置文件中设置的值相等,配置如下: +``` +nacos.core.auth.plugin.nacos.token.expire.seconds=18000 +// 对应的Docker镜像变量为NACOS_AUTH_TOKEN_EXPIRE_SECONDS +``` +在开启Token缓存功能之后,服务端对每一个携带用户名密码访问login接口的请求,会先检查缓存中是否存在该用户名对应的token。若不存在,生成新的Token,插入缓存再返回;若存在,返回该token,此时tokenTtl字段的值为配置文件中设置的值减去该Token在缓存中存留的时长。 +如果Token在缓存中存留的时长超过配置文件设置的值的90%,当login接口收到请求时,尽管缓存中存在该用户名对应的Token,服务端会重新生成Token返回给请求方,并更新缓存。因此,最差情况下,请求方收到的tokenTtl只有配置文件设置的值的10%。 diff --git a/src/content/docs/v3.0/en/manual/admin/console.md b/src/content/docs/v3.0/en/manual/admin/console.md new file mode 100644 index 00000000000..22bf466849c --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/console.md @@ -0,0 +1,177 @@ +--- +title: 控制台手册 +keywords: [控制台,手册] +description: Nacos 控制台主要旨在于增强对于服务列表,健康状态管理,服务治理,分布式配置管理等方面的管控能力。 +sidebar: + order: 9 +--- + +# 控制台手册 + +[Nacos 控制台](http://console.nacos.io/nacos/index.html)主要旨在于增强对于服务列表,健康状态管理,服务治理,分布式配置管理等方面的管控能力,以便进一步帮助用户降低管理微服务应用架构的成本,将提供包括下列基本功能: + +* 服务管理 + * 服务列表及服务健康状态展示 + * 服务元数据存储及编辑 + * 服务流量权重的调整 + * 服务优雅上下线 +* 配置管理 + * 多种配置格式编辑 + * 编辑DIFF + * 示例代码 + * 推送状态查询 + * 配置版本及一键回滚 +* 命名空间 +* 登录管理 + +## 1. 特性详解 + +### 1.1. 服务管理 + +开发者或者运维人员往往需要在服务注册后,通过友好的界面来查看服务的注册情况,包括当前系统注册的所有服务和每个服务的详情。并在有权限控制的情况下,进行服务的一些配置的编辑操作。Nacos在这个版本开放的控制台的服务发现部分,主要就是提供用户一个基本的运维页面,能够查看、编辑当前注册的服务。 + +#### 1.1.1. 服务列表管理 + +服务列表帮助用户以统一的视图管理其所有的微服务以及服务健康状态。整体界面布局是左上角有服务的搜索框和搜索按钮,页面中央是服务列表的展示。服务列表主要展示服务名、集群数目、实例数目、健康实例数目和详情按钮五个栏目。 + +![image.png | left | 747x281](https://cdn.nlark.com/lark/0/2018/png/15356/1540536911804-3660f0e9-855f-4439-ac23-e76f6f644360.png "") + +在服务列表页面点击详情,可以看到服务的详情。可以查看服务、集群和实例的基本信息。 + +#### 1.1.2. 服务流量权重支持及流量保护 + +Nacos 为用户提供了流量权重控制的能力,同时开放了服务流量的阈值保护,以帮助用户更好的保护服务服务提供者集群不被意外打垮。如下图所以,可以点击实例的编辑按钮,修改实例的权重。如果想增加实例的流量,可以将权重调大,如果不想实例接收流量,则可以将权重设为0。 + +![image.png | left | 747x266](https://cdn.nlark.com/lark/0/2018/png/15356/1540537029452-dffbb078-4ae5-4397-9f70-083e0ebbb5be.png "") + +#### 1.1.3. 服务元数据管理 + +Nacos提供多个维度的服务元数据的暴露,帮助用户存储自定义的信息。这些信息都是以K-V的数据结构存储,在控制台上,会以k1=v1,k2=v2这样的格式展示。类似的,编辑元数据可以通过相同的格式进行。例如服务的元数据编辑,首先点击服务详情页右上角的“编辑服务”按钮,然后在元数据输入框输入:version=1.0,env=prod。 + +![image.png | left | 747x271](https://cdn.nlark.com/lark/0/2018/png/15356/1540537359751-217d7500-c19c-4bad-8508-27f347f48a2f.png "") + +点击确认,就可以在服务详情页面,看到服务的元数据已经更新了。 + +![image.png | left | 747x145](https://cdn.nlark.com/lark/0/2018/png/15356/1540537452673-01dc6c92-329a-4b6f-a616-36dc546c3355.png "") + +#### 1.1.4. 服务优雅上下线 + +Nacos还提供服务实例的上下线操作,在服务详情页面,可以点击实例的“上线”或者“下线”按钮,被下线的实例,将不会包含在健康的实例列表里。 + +![image.png | left | 747x142](https://cdn.nlark.com/lark/0/2018/png/15356/1540537640435-b28cb279-75af-4965-8a9a-54cee213f1a5.png "") + +### 1.2. 配置管理 + +Nacos支持基于Namespace和Group的配置分组管理,以便用户更灵活的根据自己的需要按照环境或者应用、模块等分组管理微服务以及Spring的大量配置,在配置管理中主要提供了配置历史版本、回滚、订阅者查询等核心管理能力。 + +![image.png | left | 747x297](https://cdn.nlark.com/lark/0/2018/png/9687/1540458893745-219a46a8-ebd9-405b-9e8f-226f3f0c7e76.png "") + +#### 1.2.1. 多配置格式编辑器 + +Nacos支持 YAML、Properties、TEXT、JSON、XML、HTML 等常见配置格式在线编辑、语法高亮、格式校验,帮助用户高效编辑的同时大幅降低格式错误带来的风险。 + +Nacos支持配置标签的能力,帮助用户更好、更灵活的做到基于标签的配置分类及管理。同时支持用户对配置及其变更进行描述,方便多人或者跨团队协作管理配置。 + +![image.png | left | 747x426](https://cdn.nlark.com/lark/0/2018/png/9687/1540458995051-b3e67fd4-c905-4552-9e52-f54b6ef59941.png "") + +#### 1.2.2. 编辑DIFF + +Nacos支持编辑DIFF能力,帮助用户校验修改内容,降低改错带来的风险。 + +![image.png | left | 747x338](https://cdn.nlark.com/lark/0/2018/png/9687/1540457990344-a60e1db3-ca1a-47ed-a03e-f92e37745247.png "") + +#### 1.2.3. 示例代码 + +Nacos提供示例代码能力,能够让新手快速使用客户端编程消费该配置,大幅降低新手使用门槛。 + +![image.png | left | 747x223](https://cdn.nlark.com/lark/0/2018/png/9687/1540456991412-01acc11c-8b48-48d8-9032-589ebb9388d9.png "") + +![image.png | left | 747x380](https://cdn.nlark.com/lark/0/2018/png/9687/1540532899571-ccea6b6f-a1e1-44d1-a130-f9afaba01c51.png "") + +#### 1.2.4. 监听者查询 + +Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配置变更是否推送到 Client 端。 + +![image.png | left | 747x185](https://cdn.nlark.com/lark/0/2018/png/9687/1540459212236-0abdc558-68b9-4585-b11e-c9a1924ce7ef.png "") + +#### 1.2.5. 配置的版本及一键回滚 + +Nacos通过提供配置版本管理及其一键回滚能力,帮助用户改错配置的时候能够快速恢复,降低微服务系统在配置管理上的一定会遇到的可用性风险。 + +![image.png | left | 747x242](https://cdn.nlark.com/lark/0/2018/png/9687/1540459226967-a258b9a7-f95f-41b0-874f-2a0a5da2fc5c.png "") + +![image.png | left | 747x493](https://cdn.nlark.com/lark/0/2018/png/9687/1540459237821-d4c06d16-b356-4953-a6e7-da949b1f3aec.png "") + +## 2. 命名空间管理 + +Nacos 基于Namespace 帮助用户逻辑隔离多个命名空间,这可以帮助用户更好的管理测试、预发、生产等多环境服务和配置,让每个环境的同一个配置(如数据库数据源)可以定义不同的值。 + +![image.png | left | 747x298](https://cdn.nlark.com/lark/0/2018/png/9687/1540519411777-74908cc2-29bc-4270-be58-aed62605228f.png "") + +![image.png | left | 747x206](https://cdn.nlark.com/lark/0/2018/png/9687/1540519427066-effd5153-02c9-4e21-ae9f-1a2e9ae7713e.png "") + +## 3. 登录管理 + +Nacos支持简单登录功能,在开启[鉴权](./auth.mdx)功能后启用,管理员用户名为: `nacos`, 密码需要在首次开启控制台时进行初始化。 + +![login](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561262748106-4fc05174-bf70-4806-bcbd-90296c5bcbaa.jpeg) + +### 3.1. 修改默认用户名/密码方法 + +#### 3.1.1. 初始化时生成 + +当Nacos集群开启鉴权后访问Nacos控制台时,会校验是否已经初始化过管理员用户`nacos`的密码,若发现未初始化密码时,则会跳转至初始化密码的页面进行初始化。在该页面密码文本框内输入自定义密码,然后点击提交即可; + +> 注意:若密码文本框内未输入自定义密码或输入空白密码,Nacos将会生成随机密码,请保存好生成的随机密码。 + +初始化成功后会弹窗提示初始化成功,并展示指定的密码或随机生成的密码,请保存好此密码。 + +#### 3.1.2. 控制台上修改 + +1. 登录控制台,选择`权限控制` -> `用户列表`。 +2. 找到nacos用户,并在该用户对应的`操作`列表中点击`修改`按钮,进行密码修改 + +### 3.2. 关闭登录功能 + +Nacos默认控制台在`2.2.2`版本前,无论是否开启[鉴权](../../manual/admin/auth.mdx)功能,默认控制台都会跳转到登录页,导致用户被误导认为控制台存在鉴权功能,实际没有开启鉴权,存在安全隐患。 + +经过社区和安全工程师协商讨论,需要在使用Nacos默认控制台时,鉴权开关关闭时将会自动关闭控制台登录功能。 + +当鉴权开关`nacos.core.auth.enabled`关闭时,Nacos默认控制台将不再跳转登录页,同时添加页面提示,提示当前集群未开启鉴权功能。 + +同时针对自定义的[鉴权插件](../../plugin/auth-plugin.md)添加新接口`com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService#isLoginEnabled(默认返回false)`来对自定义插件进行登录页控制。 + +### 3.3. 关闭默认控制台 + +部分公司或用户希望关闭默认控制台,使用公司的统一平台进行Nacos的配置和服务管理;或将控制台鉴权和客户端访问的鉴权分离,即控制台操作进行鉴权但客户端请求不进行鉴权。 + +可以通过`${nacoshome}/conf/application.properties`中的`nacos.console.ui.enabled`来开启或关闭Nacos默认控制台,默认为开启。 + +同时在关闭默认控制台时,默认控制台会读取`${nacoshome}/conf/console-guide.conf`文件中的内容,并在默认控制台中生成引导页,让维护者自定义将使用默认控制台的用户引导向自定义的统一平台上进行操作。 + +### 3.4. 会话时间 + +默认会话保持时间为30分钟。30分钟后需要重新登录认证。可通过`${nacoshome}/conf/application.properties`中的`nacos.core.auth.plugin.nacos.token.expire.seconds`来设置会话保持时间。 + +## 4. 社区参与的前端共建 + +在Nacos前端风格、布局的讨论中,社区踊跃投票,最终选择了这套经典黑白蓝风格的皮肤,并且通过我们UED程瑶同学的设计,布局,让交互变得十分自然流畅。 后续又由社区添加了深色主题风格,可以让Nacos控制台在两种样式主题中切换。 + +在控制台的开发之前,我们通过社区招募到了很多前端同学一起参与了前端代码的开发,在此尤其感谢李晨、王庆、王彦民等同学在Nacos前端开发过程中的大力支持! + +## 5. 坚持社区化发展,欢迎加入并贡献社区 + +> 比吐槽更重要的是搭把手,参与社区一起发展Nacos! + +要加入Nacos 社区群进行讨论和参与贡献,请扫描下方二维码加入社区群。 + +![image.png](https://cdn.nlark.com/yuque/0/2023/png/1577777/1679276899363-83081d59-67c6-4501-9cf8-0d84ba7c6d7e.png#averageHue=%23c1c2c2&clientId=u9dfeac18-3281-4&from=paste&height=551&id=ubcf45e51&name=image.png&originHeight=1102&originWidth=854&originalType=binary&ratio=2&rotation=0&showTitle=false&size=155261&status=done&style=none&taskId=ud6bea1fe-b003-441b-a810-84435d2aeff&title=&width=427) + +更多与 Nacos 相关的开源项目信息: + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring Project](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) +* [Dubbo](https://github.com/apache/dubbo) +* [Sentinel](https://github.com/alibaba/Sentinel) \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/admin/deployment/deployment-cluster.md b/src/content/docs/v3.0/en/manual/admin/deployment/deployment-cluster.md new file mode 100644 index 00000000000..d31bd41984b --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/deployment/deployment-cluster.md @@ -0,0 +1,365 @@ +--- +title: Cluster Deployment +keywords: [Nacos,Deployment,Cluster mode] +description: Nacos Cluster Deployment Guide, This document provides references for deploying Nacos in cluster mode using various methods. +sidebar: + order: 3 +--- + +# Nacos集群模式 + +本部署手册是帮忙您快速在你的电脑上,下载安装并使用Nacos,部署生产使用的集群模式。 + +### 集群部署架构图 + +无论采用何种部署方式,推荐用户把Nacos集群中所有服务节点放到一个vip下面,然后挂到一个域名下面。 + +`` 直连ip模式,机器挂则需要修改ip才可以使用。 + +`` 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。 + +`` 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式 + +![deployDnsVipMode.jpg](/img/doc/manual/admin/deployment/deploy-dns-vip-mode.svg) + +在使用VIP时,需要开放Nacos服务的主端口(默认8848)以及gRPC端口(默认9848)、同时如果对Nacos的主端口有所修改的话,需要对vip中的端口映射作出配置,具体端口的映射方式参考[部署手册概览-Nacos部署架构](./deployment-overview/#1-Nacos部署架构) + +## 1. 发行版部署 + +### 1.1. 使用MySQL数据库(推荐) + +#### 1.1.1. 环境准备 + +参考[快速开始](../../../quickstart/quick-start.mdx)中,进行Nacos的环境准备、发行版的下载等。 + +同时在使用MySQL数据源部署Nacos单机模式时,需要自行准备MySQL数据库: + +- 1.安装数据库,版本要求:5.6.5+ +- 2.初始化mysql数据库,数据库初始化文件:[mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +#### 1.1.2. 配置集群配置文件 + +在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点) + +```plain +# ip:port +200.8.9.16:8848 +200.8.9.17:8848 +200.8.9.18:8848 +``` + +#### 1.1.3. 修改配置文件 + +然后修改`${nacos.home}/conf/application.properties`文件,增加支持MySQL数据源配置,添加MySQL数据源的url、用户名和密码。 + +``` +spring.sql.init.platform=mysql + +db.num=1 +db.url.0=jdbc:mysql://${mysql_host}:${mysql_port}/${nacos_database}?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true +db.user=${mysql_user} +db.password=${mysql_password} +``` + +##### 1.1.3.1. 开启默认鉴权插件(可选,推荐) + +修改`conf`目录下的`application.properties`文件。 + +设置其中 + +```properties +nacos.core.auth.enabled=true +nacos.core.auth.system.type=nacos +nacos.core.auth.plugin.nacos.token.secret.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.value=${自定义,保证所有节点一致} +``` + +上述内容详情可查看[权限认证](../../../plugin/auth-plugin.md). + +> 注意,文档中的默认值`SecretKey012345678901234567890123456789012345678901234567890123456789`和`VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=`为公开默认值,可用于临时测试,实际使用时请**务必**更换为自定义的其他有效值。 + +#### 1.1.4. 启动Nacos集群 + +在每个部署节点上,执行如下命名,逐台或同时启动Nacos节点。 + +```bash +# Linux/Unix/Mac +sh startup.sh + +# Ubuntu + +bash startup.sh + +# Windows +startup.cmd +``` + +### 1.2. 使用Derby数据库 + +> 注意:Derby数据库为本地内置数据库,本身不支持集群模式,Nacos通过Raft协议将各个节点的Derby数据库组成逻辑集群,因此使用此模式部署集群模式的Nacos是,需要对Raft协议较为熟悉,能够进行问题排查、恢复等,建议使用MySQL数据库进行部署。 + +#### 1.2.1. 环境准备 + +参考[快速开始](../../../quickstart/quick-start.mdx)中,进行Nacos的环境准备、发行版的下载等。 + +#### 1.2.2. 配置集群配置文件 + +在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点) + +```plain +# ip:port +200.8.9.16:8848 +200.8.9.17:8848 +200.8.9.18:8848 +``` + +#### 1.2.3. 开启默认鉴权插件(可选,推荐) + +修改`conf`目录下的`application.properties`文件。 + +设置其中 + +```properties +nacos.core.auth.enabled=true +nacos.core.auth.system.type=nacos +nacos.core.auth.plugin.nacos.token.secret.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.value=${自定义,保证所有节点一致} +``` + +上述内容详情可查看[权限认证](../../../plugin/auth-plugin.md). + +> 注意,文档中的默认值`SecretKey012345678901234567890123456789012345678901234567890123456789`和`VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=`为公开默认值,可用于临时测试,实际使用时请**务必**更换为自定义的其他有效值。 + +#### 1.2.4. 启动Nacos集群 + +在每个部署节点上,执行如下命名,逐台或同时启动Nacos节点。 + +```bash +# Linux/Unix/Mac +sh startup.sh -p embedded + +# Ubuntu + +bash startup.sh -p embedded + +# Windows +startup.cmd -p embedded +``` + +### 1.3. 高级使用 + +#### 1.3.1. 自定义配置 + +Nacos提供了丰富的可配置项,帮助您调整Nacos的性能、控制Nacos提供的功能能力,例如鉴权、监控、数据库、连接、日志等;详情请参考[系统参数](../system-configurations.md)。 + +## 2. Docker部署 + +### 2.1. 使用MySQL数据库(推荐) + +参考[快速开始 Docker](../../../quickstart/quick-start-docker.mdx)中,进行`nacos-docker`项目的下载,然后执行如下命令,即可启动Nacos集群。 + +```powershell +docker-compose -f example/cluster-hostname.yaml up +``` + +### 2.2. 使用Derby数据库 + +> 注意:Derby数据库为本地内置数据库,本身不支持集群模式,Nacos通过Raft协议将各个节点的Derby数据库组成逻辑集群,因此使用此模式部署集群模式的Nacos是,需要对Raft协议较为熟悉,能够进行问题排查、恢复等,建议使用MySQL数据库进行部署。 + +参考[快速开始 Docker](../../../quickstart/quick-start-docker.mdx)中,进行`nacos-docker`项目的下载,然后执行如下命令,即可启动Nacos集群。 + +```powershell +docker-compose -f example/cluster-embedded.yaml up +``` + +### 2.3 高级配置 + +如果你有很多自定义配置的需求,可以通过指定[系统参数-镜像环境变量](../system-configurations/#2-镜像环境变量)的方式进行配置,例如需要开启鉴权时: + +```powershell +docker run --name nacos-cluster-auth -e MODE=cluster -e NACOS_AUTH_ENABLE=true -e NACOS_AUTH_TOKEN=${customToken} -e NACOS_AUTH_IDENTITY_KEY=${customKey} NACOS_AUTH_IDENTITY_VALUE=${customValue} -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +同时,可以通过对application.properties文件进行挂卷定义的方式,将更多复杂的自定义配置导入Nacos容器中,强烈建议在生产环境中使用方式,例如: + +```powershell +docker run --name nacos-cluster -e MODE=cluster -v /path/application.properties:/home/nacos/conf/application.properties -v /path/cluster.conf:/home/nacos/conf/cluster.conf -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +如果仍然无法满足自定义需求,可以基于nacos-docker项目中的`Dockerfile`自行构建镜像。 + +## 3. Kubernetes部署 + +通过[快速开始 Kubernetes](../../../quickstart/quick-start-kubernetes.mdx)文档,已经能够部署使用MySQL数据库的Nacos的集群模式。 + +但快速开始所部署的Nacos集群没有使用持久化卷的,可能存在数据丢失风险;因此推荐使用PVC持久卷方式进行部署,本例中将使用的是NFS来使用PVC。 + +#### Tips + +* 推荐使用[Nacos Operator](https://github.com/nacos-group/nacos-k8s/blob/master/operator/README-CN.md)在Kubernetes部署Nacos Server. + +### 3.1. 部署 NFS + +* 创建角色 + +```shell +kubectl create -f deploy/nfs/rbac.yaml +``` + +> 如果的K8S命名空间不是**default**,请在部署RBAC之前执行以下脚本: + +```shell +# Set the subject of the RBAC objects to the current namespace where the provisioner is being deployed +$ NS=$(kubectl config get-contexts|grep -e "^\*" |awk '{print $5}') +$ NAMESPACE=${NS:-default} +$ sed -i'' "s/namespace:.*/namespace: $NAMESPACE/g" ./deploy/nfs/rbac.yaml + +``` + +* 创建 `ServiceAccount` 和部署 `NFS-Client Provisioner` + +```shell +kubectl create -f deploy/nfs/deployment.yaml +``` + +* 创建 NFS StorageClass + +```shell +kubectl create -f deploy/nfs/class.yaml +``` + +* 验证NFS部署成功 + +```shell +kubectl get pod -l app=nfs-client-provisioner +``` + +### 3.2. 部署数据库 + +```shell + +cd nacos-k8s + +kubectl create -f deploy/mysql/mysql-nfs.yaml +``` + +* 验证数据库是否正常工作 + +```shell + +kubectl get pod +NAME READY STATUS RESTARTS AGE +mysql-gf2vd 1/1 Running 0 111m + +``` +### 3.3. 执行数据库初始化语句 + +数据库初始化语句位置 [mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +### 3.4. 部署Nacos + +* 修改 **deploy/nacos/nacos-pvc-nfs.yaml** + +```yaml +data: + mysql.host: "数据库地址" + mysql.db.name: "数据库名称" + mysql.port: "端口" + mysql.user: "用户名" + mysql.password: "密码" +``` + +* 创建 Nacos + +``` shell +kubectl create -f nacos-k8s/deploy/nacos/nacos-pvc-nfs.yaml +``` + +* 验证Nacos节点启动成功 + +```shell +kubectl get pod -l app=nacos + + +NAME READY STATUS RESTARTS AGE +nacos-0 1/1 Running 0 19h +nacos-1 1/1 Running 0 19h +nacos-2 1/1 Running 0 19h +``` + +### 3.5. 扩容测试 + +* 在扩容前,使用 [`kubectl exec`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands/#exec)获取在pod中的Nacos集群配置文件信息 + +```powershell +for i in 0 1; do echo nacos-$i; kubectl exec nacos-$i cat conf/cluster.conf; done +``` + +StatefulSet控制器根据其序数索引为每个Pod提供唯一的主机名。 主机名采用 - 的形式。 因为nacos StatefulSet的副本字段设置为2,所以当前集群文件中只有两个Nacos节点地址 + +![k8s](https://cdn.nlark.com/yuque/0/2019/gif/338441/1562846123635-e361d2b5-4bbe-4347-acad-8f11f75e6d38.gif) + +* 使用kubectl scale 对Nacos动态扩容 + +```bash +kubectl scale sts nacos --replicas=3 +``` + +![scale](https://cdn.nlark.com/yuque/0/2019/gif/338441/1562846139093-7a79b709-9afa-448a-b7d6-f57571d3a902.gif) + +* 在扩容后,使用 [`kubectl exec`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands/#exec)获取在pod中的Nacos集群配置文件信息 + +```bash +for i in 0 1 2; do echo nacos-$i; kubectl exec nacos-$i cat conf/cluster.conf; done +``` + +![get_cluster_after](https://cdn.nlark.com/yuque/0/2019/gif/338441/1562846177553-c1c7f379-1b41-4026-9f0b-23e15dde02a8.gif) + +* 使用 [`kubectl exec`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands/#exec)执行Nacos API 在每台节点上获取当前**Leader**是否一致 + +```bash +for i in 0 1 2; do echo nacos-$i; kubectl exec nacos-$i curl -X GET "http://localhost:8848/nacos/v1/ns/raft/state"; done +``` + +到这里你可以发现新节点已经正常加入Nacos集群当中。 + +### 3.6. 配置属性 + +* `nacos-pvc-nfs.yaml` or `nacos-quick-start.yaml` + +| 名称 | 必要 | 描述 | +| ----------------------- | -------- | --------------------------------------- | +| `mysql.host` | Y | 自建数据库地址,使用外部数据库时必须指定 | +| `mysql.db.name` | Y | 数据库名称 | +| `mysql.port` | N | 数据库端口 | +| `mysql.user` | Y | 数据库用户名(请不要含有符号```,```) | +| `mysql.password` | Y | 数据库密码(请不要含有符号```,```) | +| `SPRING_DATASOURCE_PLATFORM` | Y | 数据库类型,默认为embedded嵌入式数据库,参数只支持mysql或embedded | +| `NACOS_REPLICAS` | N | 确定执行Nacos启动节点数量,如果不适用动态扩容插件,就必须配置这个属性,否则使用扩容插件后不会生效 | +| `NACOS_SERVER_PORT` | N | Nacos 端口 为peer_finder插件提供端口| +| `NACOS_APPLICATION_PORT` | N | Nacos 端口| +| `PREFER_HOST_MODE` | Y | 启动Nacos集群按域名解析 | + +* **nfs** `deployment.yaml` + +| 名称 | 必要 | 描述 | +| ------------ | --------| ------------------------ | +| `NFS_SERVER` | Y | NFS 服务端地址 | +| `NFS_PATH` | Y | NFS 共享目录 | +| `server` | Y | NFS 服务端地址 | +| `path` | Y | NFS 共享目录 | + +* mysql + +| 名称 | 必要 | 描述 | +| -------------------------- | -------- | ----------------------------------------------------------- | +| `MYSQL_ROOT_PASSWORD` | N | ROOT 密码 | +| `MYSQL_DATABASE` | Y | 数据库名称 | +| `MYSQL_USER` | Y | 数据库用户名 | +| `MYSQL_PASSWORD` | Y | 数据库密码 | +| `MYSQL_REPLICATION_USER` | Y | 数据库复制用户 | +| `MYSQL_REPLICATION_PASSWORD` | Y | 数据库复制用户密码 | +| `Nfs:server` | N | NFS 服务端地址,如果使用本地部署不需要配置 | +| `Nfs:path` | N | NFS 共享目录,如果使用本地部署不需要配置 | diff --git a/src/content/docs/v3.0/en/manual/admin/deployment/deployment-overview.md b/src/content/docs/v3.0/en/manual/admin/deployment/deployment-overview.md new file mode 100644 index 00000000000..e88247ad4ae --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/deployment/deployment-overview.md @@ -0,0 +1,91 @@ +--- +title: Deployment Overview +keywords: [Nacos,Deployment] +description: Nacos Support three types deployment mode. +sidebar: + order: 1 +--- + +# Nacos部署手册 + +> Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。 +> +> 以下文档中提及的VIP,网卡等所有网络相关概念均处于内部网络环境。 + +## 1. Nacos部署架构 + +Nacos2.X版本新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port,默认8848)基础上,进行一定偏移量自动生成,具体端口内容及偏移量请参考如下: + +|端口|与主端口的偏移量|描述| +|--|--|--| +|9848|1000|客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求| +|9849|1001|服务端gRPC请求服务端端口,用于服务间同步等| +|7848|-1000|Jraft请求服务端端口,用于处理服务端间的Raft相关请求| + +> **使用VIP/nginx请求时,需要配置成TCP转发,不能配置http2转发,否则连接会被nginx断开。** +> +> **对外暴露端口时,只需要暴露主端口(默认8848)和gRPC端口(默认9848),其他端口为服务端之间的通信端口,请勿暴露其他端口,同时建议所有端口均不暴露在公网下。** + +![nacos2_port_exposure.png](/img/doc/manual/admin/deployment/deploy-port-export.svg) + +客户端拥有相同的计算逻辑,用户如同1.X的使用方式,配置主端口(默认8848),通过相同的偏移量,计算对应gRPC端口(默认9848)。 + +因此如果客户端和服务端之前存在端口转发,或防火墙时,需要对端口转发配置和防火墙配置做相应的调整。 + +## 2. Nacos支持三种部署模式 + +* 单机模式 - 又称单例模式,主要用于测试和单机试用。 +* 集群模式 - 主要用于生产环境,确保高可用。 +* 多集群模式(TODO) - 用于多数据中心场景。 + +![Nacos部署模式图](/img/doc/overview/deploy-structure.svg) + +### 2.1. 单机模式 + +单机模式又称`单例模式`, 拥有所有Nacos的功能及特性,具有极易部署、快速启动等优点。但无法与其他节点组成集群,无法在节点或网络故障时提供高可用能力。单机模式同样可以使用内置Derby数据库(默认)和外置数据库进行存储。 + +单机模式主要适合于工程师于本地搭建或于测试环境中搭建Nacos环境,主要用于开发调试及测试使用;也能够兼顾部分对稳定性和可用性要求不高的业务场景。 + +单机模式的部署参考文档: [单机模式部署](./deployment-standalone.mdx) + +### 2.2. 集群模式 + +集群模式通过自研一致性协议Distro以及Raft协议,将多个Nacos节点构建成了高可用的Nacos集群。数据将在集群中各个节点进行同步,保证数据的一致性。集群模式具有高可用、高扩展、高并发等优点,确保在故障发生时不影响业务的运行。集群模式**默认**采用外置数据库进行存储,但也可以通过内置数据库进行存储。 + +该模式主要适合于生产环境,也是社区最为推荐的部署模式。 + +集群模式的部署参考文档: [集群模式部署](./deployment-cluster.md) + +### 2.3. 多集群模式(TODO) + +Nacos支持NameServer路由请求模式,通过它您可以设计一个有用的映射规则来控制请求转发到相应的集群,在映射规则中您可以按命名空间或租户等分片请求... + +## 3. 多网卡IP选择 + +当本地环境比较复杂的时候,Nacos服务在启动的时候需要选择运行时使用的IP或者网卡。Nacos从多网卡获取IP参考Spring Cloud设计,通过nacos.inetutils参数,可以指定Nacos使用的网卡和IP地址。目前支持的配置参数有: + +- ip-address参数可以直接设置nacos的ip + +``` +nacos.inetutils.ip-address=10.11.105.155 +``` + +- use-only-site-local-interfaces参数可以让nacos使用局域网ip,这个在nacos部署的机器有多网卡时很有用,可以让nacos选择局域网网卡 + +``` +nacos.inetutils.use-only-site-local-interfaces=true +``` + +- ignored-interfaces支持网卡数组,可以让nacos忽略多个网卡 + +``` +nacos.inetutils.ignored-interfaces[0]=eth0 +nacos.inetutils.ignored-interfaces[1]=eth1 +``` + +- preferred-networks参数可以让nacos优先选择匹配的ip,支持正则匹配和前缀匹配 + +``` +nacos.inetutils.preferred-networks[0]=30.5.124. +nacos.inetutils.preferred-networks[0]=30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))),30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))) +``` diff --git a/src/content/docs/v3.0/en/manual/admin/deployment/deployment-standalone.mdx b/src/content/docs/v3.0/en/manual/admin/deployment/deployment-standalone.mdx new file mode 100644 index 00000000000..4f2a175aef5 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/deployment/deployment-standalone.mdx @@ -0,0 +1,88 @@ +--- +title: Standalone Deployment +keywords: [Nacos,Deployment,Standalone mode] +description: Nacos Standalone Deployment Guide, This document provides references for deploying Nacos in standalone mode using various methods. +sidebar: + order: 2 +--- +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Nacos单机模式 + +## 1. 发行版部署 + +### 1.1. 部署步骤 + + + + 在[快速开始](../../../quickstart/quick-start.mdx)中,我们使用了内置的数据库Derby,快速部署了Nacos的单机模式,可参考该文档进行使用Derby数据库的Nacos单机模式部署。 + + + 参考[快速开始](../../../quickstart/quick-start.mdx)中,进行Nacos的环境准备、发行版的下载等。 + + 同时在使用MySQL数据源部署Nacos单机模式时,需要自行准备MySQL数据库: + + - 1.安装数据库,版本要求:5.6.5+ + - 2.初始化mysql数据库,数据库初始化文件:[mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + + 然后修改`${nacos.home}/conf/application.properties`文件,增加支持MySQL数据源配置,添加MySQL数据源的url、用户名和密码。 + + ``` + spring.sql.init.platform=mysql + + db.num=1 + db.url.0=jdbc:mysql://${mysql_host}:${mysql_port}/${nacos_database}?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true + db.user=${mysql_user} + db.password=${mysql_password} + ``` + + 然后使用[快速开始-启动服务器](../../../quickstart/quick-start/#4启动服务器)中的操作,启动Nacos即可。 + + + +### 1.2. 高级配置 + +Nacos提供了丰富的可配置项,帮助您调整Nacos的性能、控制Nacos提供的功能能力,例如鉴权、监控、数据库、连接、日志等;详情请参考[系统参数](../system-configurations.md)。 + +## 2. Docker部署 + +### 2.1. 部署步骤 + + + + 在[快速开始 Docker](../../../quickstart/quick-start-docker.mdx)中,我们通过`Docker`使用了内置的数据库Derby,快速部署了Nacos的单机模式,可参考该文档进行使用Derby数据库的Nacos单机模式部署。 + + + 执行 docker-compose 命令启动Nacos + + ```powershell + docker-compose -f example/standalone-mysql-8.yaml up + ``` + + **如果希望使用MySQL5.7** + + ```powershell + docker-compose -f example/standalone-mysql-5.7.yaml up + ``` + + + +### 2.2. 高级配置 + +如果你有很多自定义配置的需求,可以通过指定[系统参数-镜像环境变量](../system-configurations/#2-镜像环境变量)的方式进行配置,例如需要开启鉴权时: + +```powershell +docker run --name nacos-standalone-auth -e MODE=standalone -e NACOS_AUTH_ENABLE=true -e NACOS_AUTH_TOKEN=${customToken} -e NACOS_AUTH_IDENTITY_KEY=${customKey} NACOS_AUTH_IDENTITY_VALUE=${customValue} -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +同时,可以通过对application.properties文件进行挂卷定义的方式,将更多复杂的自定义配置导入Nacos容器中,强烈建议在生产环境中使用方式,例如: + +```powershell +docker run --name nacos-standalone -e MODE=standalone -v /path/application.properties:/home/nacos/conf/application.properties -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +如果仍然无法满足自定义需求,可以基于nacos-docker项目中的`Dockerfile`自行构建镜像。 + +## 3. Kubernetes部署 + +Kubernetes暂时不提供单例模式的部署,推荐使用集群模式部署。 \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/admin/monitor.md b/src/content/docs/v3.0/en/manual/admin/monitor.md new file mode 100644 index 00000000000..71af5fcadc5 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/monitor.md @@ -0,0 +1,170 @@ +--- +title: 监控手册 +keywords: [Nacos,监控] +description: Nacos 如何开启和部署监控 +sidebar: + order: 8 +--- + +# 监控手册 + +## 1. Nacos 集群监控 + +Nacos 支持通过暴露metrics数据接入第三方监控系统监控Nacos运行状态,目前支持prometheus、elastic search和influxdb,下面结合prometheus和grafana为例,介绍如何监控Nacos。与elastic search和influxdb结合可自己查找相关资料。 + +### 1.1. 开启Nacos集群metrics数据暴露 + +按照[部署文档](./deployment/deployment-overview.md)搭建好Nacos集群后,需要在Nacos集群的每个节点上修改如下参数: + +`application.properties`文件,暴露metrics数据 + +``` +management.endpoints.web.exposure.include=prometheus +``` + +重启后,访问`{ip}:8848/nacos/actuator/prometheus`,即可访问到Nacos集群的metrics数据。 + +### 1.2. 搭建prometheus采集Nacos metrics数据 + +请参考[FIRST STEPS WITH PROMETHEUS](https://prometheus.io/docs/introduction/first_steps/)部署prometheus + +其中,需要将prometheus的配置文件`prometheus.yml`关于采集目标相关的配置,修改为如下内容 +``` + metrics_path: '/nacos/actuator/prometheus' + static_configs: + - targets: ['{nacos.ip1}:8848','{nacos.ip2}:8848','{nacos.ip3}:8848',...] +``` + +搭建并启动完成prometheus后,即可通过浏览器访问`http://{prometheus_ip}:9090/graph`可以看到prometheus的采集数据,在搜索栏搜索nacos_monitor可以搜索到Nacos数据说明采集数据成功。 + +### 1.3. 搭建grafana图形化展示Nacos metrics数据 + +参考[Install Grafana](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) 部署grafana。 + +之后在浏览器中访问 `http://{grafana_ip}:3000` + +然后参考[Configure Prometheus](https://grafana.com/docs/grafana/latest/datasources/prometheus/configure-prometheus-data-source/),将刚才部署的prometheus作为Grafana的数据源。 + +随后参考[Import dashboards](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/import-dashboards/)导入Nacos grafana监控[模版](https://github.com/nacos-group/nacos-template/blob/master/nacos-grafana-upper-2.4.json) + +导入后可看见Nacos监控模版,Nacos监控主要分为三个模块: + +1. nacos overview: 展示Nacos集群当前的概览信息,如节点个数,服务数,服务提供者数、配置数、连接数,CPU使用率等。 +![nacos overview](/img/doc/manual/admin/monitor-overview.jpg) + +2. nacos core monitor: 展示Nacos集群核心的监控指标,如服务提供者数,配置数,ops,rt等,并能够查看一定时间内的变化趋势。 +![nacos core monitor](/img/doc/manual/admin/monitor-core-monitor.jpg) + +3. nacos basic monitor: 展示Nacos集群基础的监控指标,如CPU使用率,内存使用率,线程池使用情况等,并能够查看一定时间内的变化趋势。 +![nacos basic monitor](/img/doc/manual/admin/monitor-basic-monitor.jpg) + +### 1.4. 配置Nacos集群告警 + +#### 1.4.1. 配置Grafana告警 + +可以参考[Configure Grafana-managed alert rules](https://grafana.com/docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/),配置自定义的Nacos相关告警。 + +也可以通过大盘中的对应指标内容,快速配置告警: + +1. 选择需要配置告警的指标,例如`cpu usage`; +2. 在指标的右上角,点击`Menu`(展示为竖直的3个`.`),选择`编辑(Edit)`; + ![nacos grafana edit](/img/doc/manual/admin/monitor-fast-alert-edit.jpg) +3. 在Panel页面中,选择`Alert`,点击`New alert rule`,配置告警规则。 + ![nacos grafana alert](/img/doc/manual/admin/monitor-fast-alert-new.jpg) +4. 之后同样参考[Configure Grafana-managed alert rules](https://grafana.com/docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/),配置自定义的Nacos相关告警。 + +#### 1.4.2. 配置Grafana告警通知 + +参考[Configure contact points](https://grafana.com/docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/) 来配置Grafana告警时的通知方式。例如配置邮件通知、钉钉WebHook通知等。 + +### 1.5. Nacos 指标含义 + +#### 1.5.1. Nacos 系统基础资源指标 + +指标|含义 +---|--- +system_cpu_usage|CPU使用率 +system_load_average_1m|load +jvm_memory_used_bytes|JVM内存使用字节,包含各种内存区 +jvm_memory_max_bytes|JVM内存最大字节,包含各种内存区 +jvm_gc_pause_seconds_count|gc次数,包含各种gc +jvm_gc_pause_seconds_sum|gc耗时,包含各种gc +jvm_threads_daemon|线程数 + +#### 1.5.2. Nacos 集群应用指标 + +指标|含义 +---|--- +http_server_requests_seconds_count|http请求次数,包括多种(url,方法,code) +http_server_requests_seconds_sum|http请求总耗时,包括多种(url,方法,code) +grpc_server_requests_seconds_count|Nacos grpc请求次数,包括多种(requestClass,code) +grpc_server_requests_seconds_sum|Nacos grpc请求总耗时,包括多种(requestClass,code) +nacos_timer_seconds_sum|Nacos config水平通知耗时 +nacos_timer_seconds_count|Nacos config水平通知次数 +grpc_server_executor{name='maximumPoolSize'}|Nacos grpc服务器线程池的最大线程数 +grpc_server_executor{name='corePoolSize'}|Nacos grpc服务器线程池的核心线程数 +grpc_server_executor{name='taskCount'}|Nacos grpc服务器线程池的任务数量 +grpc_server_executor{name='poolSize'}|Nacos grpc服务器线程池当前线程数量 +grpc_server_executor{name='activeCount'}|Nacos grpc服务器线程池当前活跃的线程数量 +grpc_server_executor{name='completedTaskCount'}|Nacos grpc服务器线程池完成的任务数量 +grpc_server_executor{name='inQueueTaskCount'}|Nacos grpc服务器线程池在任务队列中的任务数量 +nacos_monitor{name='longPolling'}|Nacos config长连接数 +nacos_monitor{name='configCount'}|Nacos config配置个数 +nacos_monitor{name='dumpTask'}|Nacos config配置落盘任务堆积数 +nacos_monitor{name='notifyTask'}|Nacos config配置水平通知任务堆积数 +nacos_monitor{name='getConfig'}|Nacos config读配置统计数 +nacos_monitor{name='publish'}|Nacos config写配置统计数 +nacos_monitor{name='ipCount'}|Nacos naming ip个数 +nacos_monitor{name='serviceCount'}|Nacos naming域名个数 +nacos_monitor{name='failedPush'}|Nacos naming推送失败数 +nacos_monitor{name='avgPushCost'}|Nacos naming平均推送耗时(ms) +nacos_monitor{name='leaderStatus'}|Nacos naming角色状态 +nacos_monitor{name='maxPushCost'}|Nacos naming最大推送耗时(ms) +nacos_monitor{name='mysqlhealthCheck'}|Nacos naming mysql健康检查次数 +nacos_monitor{name='httpHealthCheck'}|Nacos naming http健康检查次数 +nacos_monitor{name='tcpHealthCheck'}|Nacos naming tcp健康检查次数 +nacos_monitor{name='longConnection'}|Nacos基于模块划分的连接数量 +nacos_naming_subscriber{version='v1/v2'}|Nacos naming服务订阅者数量,v1/v2表示订阅者的客户端版本 +nacos_config_subscriber{version='v1/v2'}|Nacos config配置监听者数量,v1/v2表示订阅者的客户端版本 +nacos_naming_publisher{version='v1/v2'}|Nacos naming服务提供者数量,v1/v2表示订阅者的客户端版本 + +## 2. Nacos-Sync监控 + +Nacos-Sync 同样支持了第三方监控系统,能通过metrics数据观察Nacos-Sync服务的运行状态,提升了Nacos-Sync的在生产环境的监控能力。 +整体的监控体系的搭建参考上文的[Nacos 集群监控](#1-nacos-集群监控),进行prometheus和grafana的部署即可。 + +### 2.1. grafana监控Nacos-Sync +和Nacos监控一样,Nacos-Sync也提供了监控模版,导入监控[模版](https://github.com/nacos-group/nacos-template/blob/master/nacos-sync-grafana) + +Nacos-Sync监控同样也分为三个模块: +- nacos-sync monitor展示核心监控项 + ![monitor](https://img.alicdn.com/tfs/TB1GeNWKmzqK1RjSZFHXXb3CpXa-2834-1588.png) +- nacos-sync detail和alert展示监控曲线和告警 + ![detail](https://img.alicdn.com/tfs/TB1kP8UKbvpK1RjSZPiXXbmwXXa-2834-1570.png) + +### 2.2. Nacos-Sync 指标含义 + +Nacos-Sync的metrics分为jvm层和应用层 + +#### 2.2.1. jvm 指标 + +指标|含义 +---|--- +system_cpu_usage|CPU使用率 +system_load_average_1m|load +jvm_memory_used_bytes|内存使用字节,包含各种内存区 +jvm_memory_max_bytes|内存最大字节,包含各种内存区 +jvm_gc_pause_seconds_count|gc次数,包含各种gc +jvm_gc_pause_seconds_sum|gc耗时,包含各种gc +jvm_threads_daemon|线程数 + +#### 2.2.2. 应用层 指标 + +指标|含义 +---|--- +nacosSync_task_size|同步任务数 +nacosSync_cluster_size|集群数 +nacosSync_add_task_rt|同步任务执行耗时 +nacosSync_delete_task_rt|删除任务耗时 +nacosSync_dispatcher_task|从数据库中分发任务 +nacosSync_sync_task_error|所有同步执行时的异常 \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/admin/system-configurations.md b/src/content/docs/v3.0/en/manual/admin/system-configurations.md new file mode 100644 index 00000000000..4a9627cd90f --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/system-configurations.md @@ -0,0 +1,250 @@ +--- +title: 系统参数 +keywords: [Nacos,系统参数] +description: Nacos系统参数介绍 +sidebar: + order: 4 +--- + +# Nacos 系统参数介绍 + +## 1. Nacos Server + +对于Server端来说,一般是设置在`{nacos.home}/conf/application.properties`里,如果参数名后标注了(-D)的,则表示是 JVM 的参数,需要在`{nacos.home}/bin/startup.sh`里进行相应的设置。例如像设置 nacos.home 的值,可以在`{nacos.home}/bin/startup.sh`进行如下设置: + +``` +JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}" +``` + +若没有标注(-D)的参数,则同时可以在`{nacos.home}/conf/application.properties`里和JVM参数中配置,如果同时配置了JVM参数和`{nacos.home}/conf/application.properties`,那么JVM参数的优先级更高。 + +### 1.1. 全局参数 + +#### 1.1.1. 基础参数 + +|参数名 | 含义 | 可选值 | 默认值| +|------|--------------------------------------------------------------------------------------------|-----------|-----------------| +|nacos.home(-D)| Nacos的根目录 | 目录路径| Nacos安装的目录 | +|nacos.standalone(-D)| 是否在单机模式 | true/false | false | +|nacos.functionMode(-D)| 启动模式,支持只启动某一个模块,不设置时所有模块都会启动 | config/naming/空 | 空 | +|nacos.server.ip(-D)| Nacos服务端的IP,优先级比`nacos.inetutils.ip-address`更高,如果配置了该参数,则`nacos.inetutils.ip-address`不再生效 | 本机IP | null | +|nacos.inetutils.prefer-hostname-over-ip| 节点优先使用hostname作为本机ip,若为`true`时,`cluster.conf`里是否应该填`hostname` | true/false| false | +|nacos.inetutils.ip-address | 本机IP,该参数设置后,将会使用这个IP去`cluster.conf`里进行匹配,请确保这个IP的值在`cluster.conf`里是存在的 | 本机IP| null | +|nacos.core.sys.basic.processors| 指定服务端的处理器个数,用于部分虚拟化场景,防止读取CPU个数时读取到错误的值,导致线程数过多或过少 |正整数| CPU个数| +|nacos.core.monitor.topn.enabled| Nacos Server topN 监控统计能力开关 | true/false | true | +|nacos.core.monitor.topn.count| Nacos Server topN 监控统计 top的个数,如如配置为10,表示top10的配置和服务 | 正整数 | 10 | +|nacos.core.snowflake.worker-id| Nacos Server 的snowflake workerId | 正整数 | -1 | +|nacos.core.param.check.enabled| Nacos Server 参数校验能力开关,开启后将会校验请求时的参数是否符合规范,不符合将被拦截,详情查看 [参数校验](../user/parameters-check.md) | true/false | true | +|server.port| Nacos Server 的端口 | 正整数 | 8848 | +|server.servlet.context-path| Nacos Server 的Servlet上下文路径 | 正则表达式 | /nacos | +|spring.config.additional-location| Nacos Server 的额外配置文件路径,除`{nacos.home}/conf/application.properties`外,用户可以添加额外的配置文件 | 文件路径,多个文件路径用逗号分隔 | null | + +#### 1.1.2. 数据库 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|spring.sql.init.platform|Nacos Server 使用的数据库类型 | mysql/空,指定为空时会根据`nacos.standalone`判断使用derby数据库还是mysql数据库;在使用[数据源插件](../../plugin/datasource-plugin.md)时,可以指定为插件对应的数据库值,比如oracle或postgresql | null | +|~~spring.datasource.platform~~|Nacos Server 使用的数据库类型,即将被废弃,请使用`spring.sql.init.platform`代替 | mysql/空 | null | +|db.num| 数据库数目 | 正整数 | 0 | +|db.url.0| 第一个数据库的URL | 字符串 | 空 | +|db.url.1| 第二个数据库的URL,当db.num=2时生效 | 字符串 | 空 | +|db.user| 数据库连接的用户名 | 字符串 | 空 | +|db.password| 数据库连接的密码 | 字符串 | 空 | +|db.pool.config.xxx| 数据库连接池参数,使用的是hikari连接池,参数与hikari连接池相同,如`db.pool.config.connectionTimeout`或`db.pool.config.maximumPoolSize`|字符串|同hikariCp对应默认配置| + +当前数据库配置支持多数据源。通过`db.num`来指定数据源个数,`db.url.index`为对应的数据库的链接。`db.user`以及`db.password`没有设置`index`时,所有的链接都以`db.user`和`db.password`用作认证。如果不同数据源的用户名称或者用户密码不一样时,可以通过符号`,`来进行切割,或者指定`db.user.index`,`db.user.password`来设置对应数据库链接的用户或者密码。需要注意的是,当`db.user`和`db.password`没有指定下标时,因为当前机制会根据`,`进行切割。所以当用户名或者密码存在`,`时,会把`,`切割后前面的值当成最后的值进行认证,会导致认证失败。 + +Nacos从1.3版本开始使用HikariCP连接池,但在1.4.1版本前,连接池配置由系统默认值定义,无法自定义配置。在1.4.1后,提供了一个方法能够配置HikariCP连接池。 +`db.pool.config`为配置前缀,`xxx`为实际的hikariCP配置,如`db.pool.config.connectionTimeout`或`db.pool.config.maximumPoolSize`等。更多hikariCP的配置请查看[HikariCP](https://github.com/brettwooldridge/HikariCP) +需要注意的是,url,user,password会由`db.url.n`,`db.user`,`db.password`覆盖,driverClassName则是默认的MySQL8 driver(该版本mysql driver支持mysql5.x) + +#### 1.1.2. Remoting + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.remote.server.grpc.sdk.max-inbound-message-size|Nacos Server gRPC 能接收的单次最大客户端请求大小,单位byte| 正整数 |10 * 1024 * 1024| +|nacos.remote.server.grpc.cluster.max-inbound-message-size|Nacos Server gRPC 能接收的单次最大集群间请求大小,单位byte| 正整数 |10 * 1024 * 1024| +|nacos.metric.grpc.server.executor.enabled| Nacos Server gRPC线程池 监控能力开关 | true/false | true | +|nacos.metric.grpc.server.executor.interval| Nacos Server gRPC线程池的间隔时间,单位为毫秒 | 正整数 | 15000 | +|nacos.metric.grpc.server.connection.enabled| Nacos Server gRPC长连接 监控能力开关 | true/false | true | +|nacos.metric.grpc.server.connection.interval| Nacos Server gRPC长连接的间隔时间,单位为**秒** | 正整数 | 15 | +|remote.executor.times.of.processors(-D)| 服务端,处理请求的线程池大小的倍数, 例如配置为2,表示线程池大小为2 * CPU | 正整数 | 16 | +|remote.executor.queue.size(-D)|服务端,处理请求的线程池队列大小 | 正整数 | 16384 | + +#### 1.1.3. 集群列表 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.member.list|Nacos Server 地址列表,在`cluster.conf`不存在时生效 | `ip1:port1,ip2:port2` | null | +|nacos.member-change-event.queue.size| Nacos Server 集群节点变更事件队列的大小,当集群节点通过`cluster.conf`或地址服务器变更时,会将变更事件放入该队列,该队列会异步通知Server中的一些机制,比如Distro/Raft协议。 | 正整数 | 128 | +|nacos.core.member.lookup.type| Nacos Server 集群节点的发现方式,支持配置文件`cluster.conf`和地址服务器模式| file/address-server | file| +|nacos.core.address-server.retry|当`nacos.core.member.lookup.type`指定为`address-server`时生效,请求地址服务器的重试次数,超过重试次数后不再尝试从地址服务器获取NacosServer的集群列表| 正整数 | 5 | +|address.server.domain|当`nacos.core.member.lookup.type`指定为`address-server`时生效,地址服务器的域名 | 域名 | jmenv.tbsite.net| +|address.server.port|当`nacos.core.member.lookup.type`指定为`address-server`时生效,地址服务器的端口 | 0~65535 | 8080 | +|address.server.url|当`nacos.core.member.lookup.type`指定为`address-server`时生效,地址服务器的url | 字符串 | /serverlist | + +### 1.2. Distro 协议 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.protocol.distro.data.sync.delayMs|Distro协议同步数据的延迟时间,同一份数据处于延迟时间内多次变更时,会被合并为一次同步,单位为毫秒 | 正整数 | 1000 | +|nacos.core.protocol.distro.data.sync.timeoutMs|Distro协议同步数据的超时时间,同步到目标节点时超过该时间,则会认为同步失败,进行延迟后重试,单位为毫秒 | 正整数 | 3000 | +|nacos.core.protocol.distro.data.sync.retryDelayMs|Distro协议同步数据的重试间隔,当数据同步到目标节点失败时,进行该值时间的延迟后再重试,避免同步重试风暴,单位为毫秒 | 正整数 | 3000 | +|nacos.core.protocol.distro.data.verify.intervalMs|Distro协议数据验证的间隔,已经同步过的数据,会定期进行数据有效性验证,验证失败会重新发起该数据的同步,单位为毫秒 | 正整数 | 5000 | +|nacos.core.protocol.distro.data.verify.timeoutMs|Distro协议数据验证的超时时间,单位为毫秒 | 正整数 | 3000 | +|nacos.core.protocol.distro.data.load.retryDelayMs|Distro协议快照数据加载的重试间隔,在节点刚启动时生效,单位为毫秒 | 正整数 | 30000 | +|nacos.core.protocol.distro.data.load.timeoutMs|Distro协议快照数据加载的超时时间,超过该时间未读取到其他节点的快照数据,则认为加载快照失败,单位为毫秒 | 正整数 | 30000 | + +### 1.3 Raft 协议 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.protocol.raft.election_timeout_ms|Raft协议选举超时时间,单位ms|正整数| 5000| +|nacos.core.protocol.raft.snapshot_interval_secs|Raft协议快照写入间隔时间,单位s|正整数| 3600| +|nacos.core.protocol.raft.core_thread_num|Raft协议的核心线程数,用于处理Raft同步的请求线程数|正整数| 8| +|nacos.core.protocol.raft.cli_service_thread_num|Raft协议的核心线程数,用于发起Raft同步数据的请求线程数|正整数| 4| +|nacos.core.protocol.raft.rpc_request_timeout_ms|Raft协议请求的超时时间,单位ms|正整数| 5000| +|nacos.core.protocol.raft.max_byte_count_per_rpc|Raft协议单次请求最大字节数|正整数| 128 * 1024| +|nacos.core.protocol.raft.max_entries_size|Raft协议单个日志的最大个数|正整数| 1024| +|nacos.core.protocol.raft.max_body_size|Raft协议发送日志的最大 body 大小|正整数| 512 * 1024| +|nacos.core.protocol.raft.max_append_buffer_size|Raft协议日志存储缓冲区最大大小|正整数| 256 * 1024| +|nacos.core.protocol.raft.max_election_delay_ms|Raft协议选举的最大随机间隔,选举定时器间隔会在指定时间之外随机的最大范围|正整数| 1000| +|nacos.core.protocol.raft.strict_mode|从`2.4.2`版本开始支持,Raft的启动校验是否采用严格模式,开启后,当raft无法选举时,引擎的readiness接口将返回500 | true/false | false | + +### 1.4. Naming模块 + +|参数名 | 含义 | 可选值 | 默认值 | +|------|-------------------------------------------------------------------------------------------------|-----------|---------------| +|nacos.naming.expireInstance| 是否自动摘除临时实例 | true/false | true | +|nacos.naming.data.warmup| 从`2.4.2`版本开始支持,是否在启动时校验数据是否预热,开启可能造成Server的readiness接口返回500,需要等待预热完成,启动时间变长 | true/false | false | +|nacos.naming.clean.empty-service.interval| Naming模块的空服务清理间隔,单位毫秒 | 正整数 | 60 * 1000 | +|nacos.naming.clean.empty-service.expired-time| Naming模块的空服务过期时间,过期的空服务会被清理,单位毫秒 | 正整数 | 60 * 1000 | +|nacos.naming.clean.expired-metadata.interval| Naming模块的元数据清理间隔,单位毫秒 | 正整数 | 5000 | +|nacos.naming.clean.expired-metadata.expired-time| Naming模块的元数据过期时间,过期的元数据会被清理,单位毫秒 | 正整数 | 60 * 1000 | +|nacos.naming.client.expired.time| 临时Client对应数据的过期时间,当Distro协议停止对该Client的数据进行续约且时间超过该值时,该Client数据将被删除,主要应对Nacos Server之间断网的场景,单位毫秒 | 正整数 | 3 * 60 * 1000 | +|nacos.naming.push.pushTaskDelay| 服务数据推送的延迟时间,同一个人服务处于延迟时间内多次变更时,会被合并为一次推送,单位为毫秒 | 正整数 | 500 | +|nacos.naming.push.pushTaskTimeout| 服务数据推送的超时时间,超过该时间未收到客户端的确认,将延迟后重试,单位为毫秒 | 正整数 | 5000 | +|nacos.naming.push.pushTaskRetryDelay| 服务数据推送失败后的重试间隔时间,单位为毫秒 | 正整数 | 1000 | + +### 1.5. Config模块 + +|参数名 |含义 | 可选值 | 默认值 | +|------|------|-----------|-------| +|nacos.config.push.maxRetryTime|配置变更数据推送的延迟时间,同一个人配置处于延迟时间内多次变更时,会被合并为一次推送,单位为毫秒| 正整数 | 50 | +|nacos.config.retention.days|Nacos配置中心配置变更历史保留天数,超过该时间的配置变更历史会被删除| 正整数 | 30 | +|nacos.config.search.max_capacity|Nacos配置中心,根据配置内容查找配置功能的最大队列个数,由于基于内容的检索十分消耗性能,因此对该功能的并发进行限制,最大不可超过32| 0~32 | 4 | +|nacos.config.search.max_thread|根据配置内容查找配置功能的最大线程数,最大并发数,最大不可超过16| 0~16 | 2 | +|nacos.config.search.wait_timeout|根据配置内容查找配置功能的等待超时时间,超过等待时间的查找任务会直接失败丢弃,单位毫秒| 正整数 | 8000 | +|nacos.config.derby.ops.enabled|当使用derby数据库作为存储时,是否开启derby的相关运维接口| true/false | false | +|nacos.persistence.sql.derby.limit.enabled|当使用derby数据库作为存储时,限制derby数据库可执行的SQL范围为DML和部分DDL,从`2.4.1`版本开始支持| true/false | true | + +### 1.6. CMDB模块 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.cmdb.loadDataAtStart| 是否打开CMDB | true/false | false | +|nacos.cmdb.dumpTaskInterval| 全量dump的间隔,单位为秒 | 正整数 | 3600 | +|nacos.cmdb.eventTaskInterval| 变更事件的拉取间隔,单位为秒 | 正整数 | 10 | +|nacos.cmdb.labelTaskInterval| 标签集合的拉取间隔,单位为秒 | 正整数 | 300 | + +### 1.7. Istio模块 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.extension.naming.istio.enabled| 是否加载istio模块| true/false | false | +|nacos.istio.mcp.server.enabled| 是否开启Istio MCP协议 | true/false | false | +|nacos.istio.mcp.server.port | Istio MCP协议监听端口 | 正整数 | 18848 | + +### 1.8. 插件 + +#### 1.8.1. 鉴权插件 + +关于如何开发鉴权插件,请参考[鉴权插件](../../plugin/auth-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.auth.enabled|Nacos是否开启鉴权|true/false|false| +|nacos.core.auth.system.type|Nacos鉴权插件的类型|nacos/ldap/自定义插件类型|nacos| +|nacos.core.auth.server.identity.key|Nacos Server节点身份信息的key,用户Server节点之间通信的识别,当开启鉴权时为必填项|字符串|null| +|nacos.core.auth.server.identity.value|Nacos Server节点身份信息的value,用户Server节点之间通信的识别,当开启鉴权时为必填项|字符串|null| +|nacos.core.auth.enable.userAgentAuthWhite|Nacos Server使用UserAgent来进行Server节点之间通信的识别,在1.4.1版本后仅作为升级时的兼容,开启后会存在安全问题,后续版本将移除该参数|true/false|false| + +同时对于Nacos默认鉴权插件的使用及更多默认鉴权插件的配置项,请参考[权限校验](../../guide/user/auth/#相关参数) + +#### 1.8.2. 数据源插件 + +其他和数据库相关的开发,请参考[全局参数-数据库](#112-数据库) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|spring.sql.init.platform|Nacos Server 使用的数据库类型 | mysql/空,指定为空时会根据`nacos.standalone`判断使用derby数据库还是mysql数据库;在使用[数据源插件](../../plugin/datasource-plugin.md)时,可以指定为插件对应的数据库值,比如oracle或postgresql | null | +|nacos.plugin.datasource.log.enabled|Nacos Server 是否开启SQL日志打印,开启后会打印每一次执行的SQL,方便进行插件开发时的问题排查,但是较为损耗性能,日常状态建议关闭 | true/false | false | + +#### 1.8.3. 环境变量插件 + +关于如何开发环境变量插件,请参考[环境变量插件](../../plugin/custom-environment-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.custom.environment.enabled|Nacos Server 是否开启环境变量插件 | true/false | false | + +#### 1.8.4. 反脆弱插件 + +反脆弱插件的开发,请参考[反脆弱插件](../../plugin/control-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.plugin.control.manager.type|Nacos反脆弱插件的类型 | nacos/其他自定义插件类型 | null | +|nacos.plugin.control.rule.external.storage|Nacos反脆弱插件,反脆弱规则外部存储类型,需要自行实现 | 字符串 | null | +|nacos.plugin.control.rule.local.basedir|Nacos反脆弱插件,反脆弱规则本地存储目录 | 文件路径 | ${nacos.home}/data/ | + +#### 1.8.5. 配置变更插件 + +反脆弱插件的开发,请参考[配置变更插件](../../plugin/config-change-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.config.plugin.${configChangePluginName}.enabled=true|Nacos Server 是否开启配置变更插件 | true/false | false | +|nacos.core.config.plugin.${configChangePluginName}.${propertyKey}=${propertyValue}|配置变更插件的配置项 | 插件自定义 | 插件自定义 | + +## 2. 镜像环境变量 + +属性配置列表 + +| 属性名称 | 描述 | 选项 | +|-----------------------------------------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| MODE | 系统启动方式: 集群/单机,对应`nacos.standalone` | cluster/standalone 默认 **cluster** | +| NACOS_SERVERS | 集群地址,对应`nacos.member.list` | p1:port1空格ip2:port2 空格ip3:port3 | +| PREFER_HOST_MODE | 支持IP还是域名模式,对应`nacos.inetutils.prefer-hostname-over-ip` | hostname/ip 默认**IP** | +| NACOS_SERVER_PORT | Nacos 运行端口,对应`server.port` | 默认**8848** | +| NACOS_SERVER_IP | 多网卡模式下可以指定IP,对应`nacos.server.ip` | | +| SPRING_DATASOURCE_PLATFORM | 单机模式下支持MYSQL数据库,对应`spring.sql.init.platform` | mysql / 空 默认:空 | +| MYSQL_SERVICE_HOST | 数据库 连接地址 | | +| MYSQL_SERVICE_PORT | 数据库端口 | 默认 : **3306** | +| MYSQL_SERVICE_DB_NAME | 数据库库名 | | +| MYSQL_SERVICE_USER | 数据库用户名 | | +| MYSQL_SERVICE_PASSWORD | 数据库用户密码 | | +| MYSQL_SERVICE_DB_PARAM | 数据库连接参数 | 默认:**characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false** | +| MYSQL_DATABASE_NUM | 数据库个数 | 默认:**1** | +| JVM_XMS | -Xms | 默认 :1g | +| JVM_XMX | -Xmx | 默认 :1g | +| JVM_XMN | -Xmn | 512m | +| JVM_MS | - XX:MetaspaceSize | 默认 :128m | +| JVM_MMS | -XX:MaxMetaspaceSize | 默认 :320m | +| NACOS_DEBUG | 是否开启远程DEBUG | y/n 默认 :n | +| TOMCAT_ACCESSLOG_ENABLED | `server.tomcat.accesslog.enabled` | 默认 :false | +| NACOS_AUTH_SYSTEM_TYPE | 权限系统类型选择,目前只支持nacos类型 | 默认 :nacos | +| NACOS_AUTH_ENABLE | 是否开启权限系统,对应`nacos.core.auth.enabled` | 默认 :false | +| NACOS_AUTH_TOKEN_EXPIRE_SECONDS | token 失效时间 | 默认 :18000 | +| NACOS_AUTH_TOKEN | token | `注意:该环境变量的默认值在Nacos 2.2.1版本中已移除,开启鉴权时需要指定` | +| NACOS_AUTH_CACHE_ENABLE | 权限缓存开关 ,开启后权限缓存的更新默认有15秒的延迟 | 默认 : false | +| MEMBER_LIST | 通过环境变量的方式设置集群地址 | 例子:192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 | +| EMBEDDED_STORAGE | 是否开启集群嵌入式存储模式 | `embedded` 默认 : none | +| NACOS_AUTH_CACHE_ENABLE | nacos.core.auth.caching.enabled | default : false | +| NACOS_AUTH_USER_AGENT_AUTH_WHITE_ENABLE | nacos.core.auth.enable.userAgentAuthWhite | default : false | +| NACOS_AUTH_IDENTITY_KEY | nacos.core.auth.server.identity.key | `注意:该环境变量的默认值在Nacos 2.2.1版本中已移除,开启鉴权时需要指定` | +| NACOS_AUTH_IDENTITY_VALUE | nacos.core.auth.server.identity.value | `注意:该环境变量的默认值在Nacos 2.2.1版本中已移除,开启鉴权时需要指定` | +| NACOS_SECURITY_IGNORE_URLS | nacos.security.ignore.urls | default : `/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**` | +| DB_POOL_CONNECTION_TIMEOUT | 数据库连接池超时时间,单位为毫秒 | 默认 : **30000** | +| NACOS_CONSOLE_UI_ENABLED | nacos.console.ui.enabled | default : `true` | +| NACOS_CORE_PARAM_CHECK_ENABLED | nacos.core.param.check.enabled | default : `true` | \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/admin/upgrading.mdx b/src/content/docs/v3.0/en/manual/admin/upgrading.mdx new file mode 100644 index 00000000000..15fc6504c12 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/admin/upgrading.mdx @@ -0,0 +1,135 @@ +--- +title: 升级手册 +keywords: [Nacos,升级] +description: Nacos版本升级指南,本文档将介绍Nacos升级的一般步骤和方法。 +sidebar: + order: 7 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# 升级手册 + +## 1. 升级版本兼容性 + +当前文档所对应的版本为2.4.X,所有Nacos版本可升级到该版本的兼容性如下: + +| Nacos版本 | 是否支持升级到当前版本 | 备注 | +|----------|--------------------|------| +| 0.X ~ 1.1.X | 不支持 | 0.X ~ 1.1.X 版本需要先升级到1.2以上的,然后参考[Nacos2.0升级文档](https://nacos.io/docs/v2/upgrading/200-upgrading/) 先升级到2.0或2.1版本后再进行升级 | +| 1.2.X ~ 1.4.X | 不支持 | 2.4.X版本不支持从 1.2.X ~ 1.4.X 之间的版本直接升级,请参考[Nacos2.0升级文档](https://nacos.io/docs/v2/upgrading/200-upgrading/) 先升级到2.0或2.1版本后再进行升级 | +| 2.0.X | 支持 | 2.4.X版本支持从 2.0.X 升级到 2.4.X, 但数据库表结构有发生变化,请升级前对比[mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql)文件,并应用新的表结构后进行升级 | +| 2.1.X ~ 2.3.X | 支持 | 2.4.X版本支持从 2.1.X ~ 2.3.X 升级到 2.4.X | + +## 2. 升级步骤 + +### 2.1. 发行版升级 + +#### 2.1.1 确认表结构 + +先对比部署的旧版本Nacos版本的mysql-schema.sql文件和将升级版本的mysql-schema.sql文件,确认表结构是否有变化。 + +若文件中表结构存在变化,请先进行数据库变更,例如: + +```SQL +ALTER TABLE `config_info` ADD COLUMN `encrypted_data_key` varchar(255) NOT NULL COMMENT '密钥', +``` + +#### 2.1.2. 下载新版本 + + + + 进入Nacos官网[版本下载页面](/download/nacos-server/),选择 [稳定版本](/download/nacos-server/#稳定版本), 然后点击`二进制包下载`列中的`${nacos.version}.zip`进行下载。 + + > 注意:有时大量用户同时进行下载时,可能会遇到下载限流失败的情况,若出现下载限流失败,请稍等后重试,或采用`从 Github 下载方式`。 + + + 进入Nacos Github 的 [最新稳定版本](https://github.com/alibaba/nacos/releases) ,选择需要下载的Nacos版本,在`Assets`中点击下载 `nacos-server-$version.zip` 包。 + + + +#### 2.1.3. 替换二进制jar包 + +解压新下载的发行版压缩包 + +```bash + unzip nacos-server-$version.zip + # 或者 tar -xvf nacos-server-$version.tar.gz +``` + +然后 找到`nacos/targer/nacos-server.jar` 将该jar包替换到旧的发行版中,例如: + +```bash +cp nacos/target/nacos-server.jar ${old.nacos.home}/target/ +``` + +#### 2.1.4. 修改配置文件(可选) + +对比部署的旧版本的配置文件和新版本的配置文件`conf/application.properties`,确认是否有新增或修改的配置项,将这些配置项添加到旧的配置文件中,例如: + +```bash +diff nacos/conf/application.properties ${old.nacos.home}/conf/application.properties +``` + +#### 2.1.5. 修改启动参数(可选) + +对比部署的旧版本的启动脚本和新版本的启动脚本`bin/startup.sh`或`bin/startup.cmd`,确认是否有新增或修改的配置项,将这些配置项添加到旧的启动脚本件中,例如: + +```bash +diff nacos/bin/startup.sh ${old.nacos.home}/bin/startup.sh +``` + +#### 2.1.6. 重启Nacos Server + +均替换完成后,重启Nacos Server: + +``` +# 以集群模式为例 +## Linux +${nacos.home}/bin/shutdown.sh +${nacos.home}/bin/startup.sh + +## Windows +${nacos.home}/bin/shutdown.cmd +${nacos.home}/bin/startup.cmd +``` +### 2.2. Docker/Kubernetes升级 + +#### 2.2.1 确认表结构 + +先对比部署的旧版本Nacos版本的mysql-schema.sql文件和将升级版本的mysql-schema.sql文件,确认表结构是否有变化。 + +若文件中表结构存在变化,请先进行数据库变更,例如: + +```SQL +ALTER TABLE `config_info` ADD COLUMN `encrypted_data_key` varchar(255) NOT NULL COMMENT '密钥', +``` + +#### 2.2.2. 确认环境变量 + +可以对比当前部署版本的环境变量与[系统参数-镜像环境变量](./system-configurations/#2-镜像环境变量)中的环境变量是否有变化,如有,需要修改Docker的环境变量文件或Kubernetes的regcenter中的环境变量。 + +#### 2.2.3. 修改镜像版本 + + + + 首先修改docker compose所对应的文件中的Nacos版本,例如[Nacos Docker](https://github.com/nacos-group/nacos-docker)项目下的standalone-mysql-8.yaml中的 + ```yaml + services: + nacos: + image: + nacos/nacos-server:${target_version} + ``` + 然后执行如下命令进行升级 + ```bash + docker-compose pull + docker-compose up -d --remove-orphans + ``` + + + 通过kubectl set image 命令更新版本,如: + ```bash + kubectl set image deployment/nacos-deployment ${container_name}=nacos/nacos-server:${target_version} + ``` + + diff --git a/src/content/docs/v3.0/en/manual/test/nacos-e2e.md b/src/content/docs/v3.0/en/manual/test/nacos-e2e.md new file mode 100644 index 00000000000..0ce5a5331fc --- /dev/null +++ b/src/content/docs/v3.0/en/manual/test/nacos-e2e.md @@ -0,0 +1,163 @@ +--- +title: Nacos E2E Tests +keywords: [Nacos,Tests,E2E] +description: The end-to-end testing project for Nacos is designed to verify the functional correctness of Nacos in various scenarios. +sidebar: + order: 2 +--- + +# Nacos E2E 测试 + +[`nacos-group/nacos-e2e`](https://github.com/nacos-group/nacos-e2e) 项目是 Nacos 的端到端测试项目,用于验证 Nacos 在不同场景下的功能正确性。如果你想要向这个项目中新增测试用例,可以按照以下步骤进行: + +## 1. 准备工作 + ++ **Fork 仓库**:首先你需要在 GitHub 上 Fork `nacos-group/nacos-e2e` 仓库到你自己的账号下。 ++ **克隆仓库**:将 Fork 后的仓库克隆到本地。 + +```shell +git clone https://github.com/your-username/nacos-e2e.git +cd nacos-e2e +``` + ++**设置上游仓库**:为了能够拉取官方最新的更新,建议设置上游仓库。 + +```shell +git remote add upstream https://github.com/nacos-group/nacos-e2e.git +``` + +## 2. 理解现有结构 + +查看项目的目录结构,目前主要按客户端测试语言类型和CI部署Nacos的依赖区分,其中java按客户端版本范围和鉴权用例区分 + ++ cicd,build目录包含docker相关文件、构建脚本和Nacos部署的配置文件;helm目录包含Kubernetes 的包管理器,它简化了在 Kubernetes 集群上部署应用程序的过程 ++ cpp,用C++语言编写的用例,测试C++客户端 ++ csharp,用csharp语言编写的用例,测试csharp客户端 ++ golang,用golang语言编写的用例,测试go客户端 ++ java,用java语言编写的用例,测试java客户端,其中4个目录分别验证: + - auth,部署单例模式的Nacos,验证开启鉴权后的场景 + - nacos-1X,1.X客户端在1.3版本以上及2.0版本以下时的测试场景 + - nacos-1X-1.3down,1.X客户端在1.3及以下版本时的测试场景 + - nacos-2X,2.X客户在2.0及以上版本时的测试场景 ++ nodejs,用nodejs语言编写的用例,测试nodejs客户端 ++ python,用python语言编写的用例,测试python客户端 + +## 3. 新增测试用例 + +下面以在`java/nacos-2X`目录下新增一个配置中心相关用例举例: + ++ **选择合适的包**:根据你要测试的客户端语言和客户端版本,选择或创建一个合适的包(例如 `/nacos-e2e/java/nacos-2X/src/test/java/com/alibaba/nacos/config`)。 ++ **编写测试类**:在选定的包下创建一个新的测试类,例如 `YourFeatureTest.java`。 + - 每个目录下会继承一个测试基类,如此配置继承ConfigBase基类,此基类进行配置测试用到的常量声明以及在@BeforeAll @AfterAll 中定义在所有测试方法执行之前或之后运行的方法 + - ConfigBase基类继承BaseOperate,定义一些通用的操作,比如配置中心和服务中心初始化连接时用到的Properties + - BaseOperate继承ResourceInit,定义初始化时用到的变量(从环境变量或者本地文件中获取到的变量)、所有用例启动前服务端的数据写入,初始化一些必要数据(比如命名空间),获取当前服务端客户端的数据(服务端版本、客户端版本等) ++ **配置依赖**:如果需要引入新的依赖,请修改 `pom.xml` 或其他构建文件。 ++ **测试类规范**: + - @BeforeEach 注解的方法中对每个用例数据进行随机初始化和配置 + - @AfterEach 注解的方法中对每个用例写入数据进行删除等操作清理 + - @Test 注解的方法中进行用例逻辑编写,在@DisplayName 中描述当前用例测试的场景 + +示例代码: + +```java +package com.alibaba.nacos.config; + +import com.alibaba.nacos.util.RandomUtils; +import org.junit.jupiter.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class YourFeatureTest extends ConfigBase{ + private static final Logger log = LoggerFactory.getLogger(ConfigNormalTest.class); + private String dataId; + private String group; + private String content; + private Map cleanConfigMap = new HashMap<>(); + + @BeforeEach + public void setUp() throws Exception{ + dataId = "config.test." + RandomUtils.getStringWithCharacter(10); + group = DEFAULT_GROUP; + } + + @AfterEach + public void tearDown() throws Exception { + Iterator> iterator = cleanConfigMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + Boolean result = config.removeConfig(entry.getKey(), entry.getValue()); + if (result) { + iterator.remove(); + } + } + } + + @Test + @DisplayName("Publish normal character and sleep 3000ms to get config, expect get correct config content.") + public void testPublishAndGetConfig_normalCharacter() throws Exception{ + content = RandomUtils.getStringWithCharacter(20); + boolean result = config.publishConfig(dataId, group, content); + log.info("publishConfig dataId:{}, group:{}, result:{}", dataId, group, result); + Assertions.assertTrue(result, "publishConfig check fail"); + cleanConfigMap.put(dataId, group); + Thread.sleep(TIME_OUT); + + String value = config.getConfig(dataId, group, TIME_OUT); + log.info("getConfig dataId:{}, group:{}, value:{}", dataId, group, value); + Assertions.assertEquals(content, value, "getConfig value is not expect one"); + } + +} + +``` + +## 4. 运行测试 + +java目录下的用例,修改每个module的daily.conf文件中serverList变量为指定服务端IP:Port,或者在执行命令中传入,亦或本地编译器中执行 + ++ **用例执行命令:** + +```shell +mvn clean test -B -Dtest=YourFeatureTest -DserverList=127.0.0.1 +``` + +其他语言下的用例执行,可以参考每种语言的module下bin目录的run.sh文件,在语言环境具备情况下,安装必要的包和设置环境变量后执行,亦或安装相应的编译器本地执行 + +## 5. 提交更改 + ++ **提交代码**:将你的更改提交到本地仓库。 + +```shell +git add . +git commit -m "Add new e2e test for your feature" +``` + ++ **推送到远程仓库**:将更改推送到你的 GitHub 仓库。 + +```shell +git push origin main +``` + +## 6. 创建 Pull Request + ++ **创建 PR**:回到 GitHub 页面,点击 "New pull request" 按钮,选择你的分支与 `nacos-group/nacos-e2e` 的主分支进行比较,然后创建 Pull Request。 ++ **等待审查**:项目维护者会对你的 PR 进行审查,可能会提出一些修改意见。根据反馈进行相应的调整。 + +## 7. 合并 PR + ++ **合并**:一旦 PR 被批准,它会被合并到主分支中。 + +通过以上步骤,你就可以成功地向 `nacos-group/nacos-e2e` 项目中新增测试用例了。 + +## 8. Nacos主仓库CI能力 + +1. 成功地向 `nacos-group/nacos-e2e` 项目中新增测试用例后,用例最终在`alibaba/nacos` 仓库的actions中触发运行。 +2. CI通过使用阿里云ASK能力为每次PUSH和PR提供一个独立的Nacos环境进行E2E测试,部署使用nacos-group/nacos-e2e仓库中的helm chart(nacos-e2e/cicd),镜像为当次提交的代码。 +3. 由于安全性考虑,将PR和PUSH动作的CI分离为两个流水线。PR触发E2E测试后回写评论至PR中,可以关联跳转到对应的测试报告。 +4. 测试报告可视化,每次E2E测试的报告在CI当次执行页面上可视化展示,无需查看日志,方便排查。 +5. 支持兼容性测试,并发执行多OS和JDK版本的测试。 + diff --git a/src/content/docs/v3.0/en/manual/test/nacos-unit-test.md b/src/content/docs/v3.0/en/manual/test/nacos-unit-test.md new file mode 100644 index 00000000000..6871792e34d --- /dev/null +++ b/src/content/docs/v3.0/en/manual/test/nacos-unit-test.md @@ -0,0 +1,139 @@ +--- +title: Nacos Unit Tests +keywords: [Nacos,Tests,Unit Tests] +description: Describe the Nacos unit testing specifications and the steps to run unit tests, helping developers quickly understand and contribute test code. +sidebar: + order: 1 +--- + +# Nacos 单元测试 + +Nacos 单元测试主要指的是Nacos 核心仓库[`alibaba/nacos`](https://github.com/alibaba/nacos)中的最基本的单元测试,用于测试各个最小测试单元(一般是Class)的逻辑正确性及改动正确性。 + +该文档将介绍Nacos 单元测试的规范,以及运行单元测试的步骤,帮助开发者快速了解在修改功能代码后如何撰写并贡献对应的单元测试代码。 + +## 1. Nacos 单元测试准备工作 + +- 可参考[Nacos 贡献流程](../../contribution/contributing-flow.md)的前3个步骤,进行仓库克隆、和本地Git的设置。 +- 安装JDK1.8+ 以上版本 +- 安装Maven3.2.5 以上版本。 + +## 2. 新增Nacos 单元测试用例 + +Nacos 的单元测试基于`junit5`,`mockito`两个测试框架,详细使用方法请参考[JUnit5](https://junit.org/junit5/docs/current/user-guide/)和[Mockito](https://site.mockito.org/#features)。 + +新建的单元测试用例类需尽量与所测试类的模块、包名一致;若已经存在单元测试类,请尽量在原有测试类中添加测试用例,不要新建测试类,部分情况为避免用例互相干扰,可以新增单元测试类,但需要在新类名中进行说明和表达。 + +新增的单元测试类需要遵循如下规范: + +### 2.1. Nacos 单元测试规范 + +1. 单元测试类必须以`Test`结尾,前面的类名必须与测试类名一致;如:类名为`NacosNamingService`,则单元测试类名为`NacosNamingServiceTest`。 +2. 单元测试的用例必须以`test`开头,后接需要测试的方法名,以驼峰命名法命名测试用例,如:`testRegisterInstance`。 +3. 单元测试用例中必须存在断言。 +4. 单元测试中不允许依赖外部资源,如:数据库, 若有需要,请使用Mockito进行模拟。 +5. 单元测试的用例若遇到所测试方法存在多个分支和测试用例的情况,请创建多个用例,且通过`For`,`With`,`Without`等描述测试用例场景,如:`DefaultParamCheckerTest`的`testCheckParamInfoForNamespaceShowName`、`testCheckParamInfoForNamespaceId`等。 +6. 单元测试用例中如果存在设置 static 变量,请使用 `@AfterAll` 或 `@AfterEach` 注解在用例结束后进行回滚,避免影响其他测试用例。 +7. 单元测试用例中所需要的依赖尽量使用`@Mock`注解进行模拟,需要用到的前置设置和初始化,尽量在`@BeforeAll` 和 `@BeforeEach`注解中完成,避免大量重复初始化代码。 +8. 单元测试若需要测试异步方法,尽量使用mock的线程池进行,若无法使用mock线程池,可以尝试控制线程池的启动时间和间隔周期,避免单元测试运行时间过长。 +9. 单元测试代码同样需要遵循[Nacos代码规范](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md)。 + +### 2.2. Nacos 单元测试示例 + +```java + +public class YourClass { + + public boolean demo(String serviceName) { + if (null == serviceName) { + return false; + } + return doDemo(serviceName); + } + + private boolean doDemo(String serviceName) { + if (!ServiceManager.containService(serviceName)) { + SecurityManager.newService(serviceName); + return true; + } else { + throw new Exception(); + } + } +} + +public class YourClassTest { + + private YourClass yourClass; + + private String serviceName; + + @BeforeEach + public void setUp() throws Exception{ + // do initial for each test cases; + yourClass = new YourClass(); + serviceName = "test"; + } + + @AfterEach + public void tearDown() throws Exception { + // do clean data. + ServiceManager.cleanService(serviceName); + } + + @Test + public void testDemoForNullService() { + assertFalse(yourClass.demo(null)); + } + + @Test + public void testDemoForExistService() { + ServiceManager.newService(serviceName); + assertThrows(Exception.class, () -> yourClass.demo(serviceName)); + } + + @Test + public void testDemoForNotExistService() { + assertFalse(ServiceManager.containService(serviceName)); + assertTrue(yourClass.demo(serviceName)); + assertTrue(ServiceManager.containService(serviceName)); + assertNotNull(ServiceManager.getService(serviceName)); + } +} +``` + +## 3. 运行Nacos 单元测试 + +在Nacos 仓库根目录下执行`mvn -Prelease-nacos clean test -DtrimStackTrace=false -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn`命令即可运行单元测试。 + +测试运行完成后,终端中会显示测试结果,同时在各自模块下,会通过`jacoco`插件生成当前模块的测试覆盖率文件,如`api/target/site/jacoco/jacoco.xml`。 + +## 4. 提交Nacos 单元测试 + ++ **提交代码**:将你的更改提交到本地仓库。 + +```shell +git add . +git commit -m "Add new unit test for class/packages/modules" +``` + ++ **推送到远程仓库**:将更改推送到你的 GitHub 仓库。 + +```shell +git push origin $your_branch_name +``` + +## 5. 创建 Pull Request + ++ **创建 PR**:回到 GitHub 页面,点击 "New pull request" 按钮,选择你的分支与 `alibaba/nacos` 的主分支(一般为`develop`)进行比较,然后创建 Pull Request。 ++ **等待审查**:PR中会通过`Github Action`对您提交的单元测试及其他修改进行一系列的检查工作,同时项目维护者会对你的 PR 进行审查,可能会提出一些修改意见。根据运行结果和反馈进行相应的调整。 ++ **合并**:一旦 PR 被批准,它会被合并到主分支中。 + +通过以上步骤,你就可以成功地向 `alibaba/nacos` 项目中新增测试用例了。 + +## 6. 测试报告 + +在PR中以及PR被合并后的`Github Action`中,会对运行之后的单元测试报告,提交到[CodeCov](https://app.codecov.io/gh/alibaba/nacos/)上进行统计和分析,可以查看到测试覆盖率情况。 + +同时在PR中, 会有`Coverage`的简略报告,评论在PR中,原则上在测试覆盖率出现降低时,社区维护人员可能会选择不进行PR的合并,并在评论中提醒您补充单元测试。 + +> 当前部分模块的测试覆盖率还有待提高,欢迎社区同学贡献测试代码。 diff --git a/src/content/docs/v3.0/en/manual/user/addressing.mdx b/src/content/docs/v3.0/en/manual/user/addressing.mdx new file mode 100644 index 00000000000..422755e1962 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/addressing.mdx @@ -0,0 +1,240 @@ +--- +title: Nacos 客户端寻址 +keywords: [寻地, 客户端, server-addr, endpoint, 地址服务器, 服务端地址配置] +description: 本文介绍了各语言SDK如何配置寻找Nacos服务器的地址逻辑,以及多种不同的寻址方式的优先级。 +sidebar: + order: 10 +--- + +import {Tabs, TabItem} from '@astrojs/starlight/components'; + +# Nacos 客户端寻址 + +在使用Nacos时, Nacos客户端需要配置Nacos服务器的地址,以确保客户端能连接到Nacos服务器,从而实现服务发现和注册等功能。 + +本文档将介绍Nacos客户端的各种寻址逻辑,以及多种不同的寻址方式的优先级;对于部分支持拓展寻址的编程语言的客户端,本文档还会介绍如何实现自定义的寻址逻辑。 + +## 1. 客户端寻址基本逻辑 + +Nacos的所有客户端在寻址Nacos服务端时,虽然具体的实现有所差异,但基本都遵循如下逻辑: + +![寻址基本逻辑](/img/doc/manual/user/client-addressing.svg) + +1. 首先,Nacos客户端会根据初始化时,接受来自应用的配置,如`Properties`等信息; +2. 在Nacos客户端初始化过程中,会创建一个`ServerListManager(服务列表管理器)`,用于管理服务端的地址列表; +3. 根据传入的`Properties`配置信息,`ServerListManager`会选择一个对应的`ServerListProvider(服务列表提供者)`来实际获取服务端地址列表,默认情况下,Nacos客户端会提供基于`server-addr配置`的`PropertiesServerListProvider`,和`Endpoint 地址服务器`的`EndpointServerListProvider`; +4. 在`ServerListManager`完成`ServerListProvider`的选取、初始化以及从其中获取到服务端地址列表后,Nacos客户端才会创建用于通信的`GrpcClient`,并使用`ServerListManager`管理的服务端地址列表来创建连接,并请求对应的Nacos Server。 + +不同的`ServerListProvider`会导致客户端在寻找服务端地址列表时的行为有所不同,下文对不同`ServerListProvider`的使用条件、行为结果、优先级等特性进行说明: + +### 1.1. 基于配置的寻址逻辑 + +Nacos客户端默认提供了基于配置的`ServerListProvider`,即`PropertiesServerListProvider`,该`ServerListProvider`会根据`server-addr`配置,从配置中获取服务端地址列表,若配置中存在多个服务端地址,Nacos客户端将会使用`,`和`;`字符进行分割、并最终随机选择一个作为服务端地址。 + +若当前选择的服务端地址不可用,则Nacos客户端会按照轮询的方式,依次尝试使用其他服务端地址,当尝试所有的地址之后,会回到第一个服务端地址,继续轮询,直至找到可用的服务端地址或客户端中止。 + +这种寻址方式的最大优点是,Nacos客户端的配置非常简单,只需要配置`server-addr`即可,无需配置额外的服务端地址列表配置;同时也无需部署额外的组件;但是,这种寻址方式相对不灵活,无法动态的修改服务端地址列表,需要修改时只能重新创建一个新的Nacos客户端使用。 + +使用示例: + +> 注意:不同编程语言的配置方式可能存在差异,请根据实际情况修改。 + + + + + ```java + try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + ConfigService configService = NacosFactory.createConfigService(properties); + NamingService configService = NacosFactory.createNamingService(properties); + } catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ``` + + + + ```Go + sc := []constant.ServerConfig{ + { + IpAddr: `{serverAddr1}`, + Port: 8848, + }, + { + IpAddr: "{serverAddr2}", + Port: 8848, + } + } + + //create ClientConfig + cc := *constant.NewClientConfig() + + // create client + namingClient, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + configClient, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + ``` + + + 待补充 + + + +### 1.2. 基于Endpoint地址服务器的寻址逻辑 + +Nacos客户端还提供了基于地址服务器的寻址方式,即`EndpointServerListProvider`,该`ServerListProvider`会根据`endpoint`配置,从`endpoint`所指向的地址服务器中获取服务端地址列表; + +地址服务器可以是任意一个提供对应HTTP接口的服务端,例如一个`nginx`,`tomcat服务端`,`Nacos的address模块等`等等;对应接口需要按照格式返回Nacos服务端的地址列表,格式为每行一个地址,例如: + +``` +127.0.0.1:8848 +1.1.1.1 +``` + +若返回的地址没有端口,则默认端口为8848。 + +同样地,Nacos客户端会随机选择列表中的一个地址作为服务端地址,并使用该地址访问Nacos服务端;若该地址不可用,则Nacos客户端会按照轮询的方式,依次尝试使用其他服务端地址,当尝试所有的地址之后,会回到第一个服务端地址,继续轮询,直至找到可用的服务端地址或客户端中止。 + +该寻址方式的最大优点是可以动态的更新客户端所连接的服务端地址列表,无需重启客户端;但是,这种寻址方式需要额外部署一个地址服务器以提供对应的更新和获取地址列表的接口、同时可能需要配置的内容较多,适合Nacos部署规模较大的场景。 + +使用示例: + +> 注意:不同编程语言的配置方式可能存在差异,请根据实际情况修改。 + + + + + 更多地址服务器相关配置,请参考[Java SDK 配置参数-通用参数](./java-sdk/properties/#21-通用参数)。 + + ```java + try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String endpoint = "{endpoint}"; + Properties properties = new Properties(); + properties.put("endpoint", endpoint); + + ConfigService configService = NacosFactory.createConfigService(properties); + NamingService configService = NacosFactory.createNamingService(properties); + } catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ``` + + + + ```Go + cc := constant.ClientConfig{ + Endpoint: "{endpoint}:8080", + } + + // a more graceful way to create config client + client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + }, + ) + ``` + + + 待补充 + + + +### 1.3. 不同寻址逻辑的触发优先级 + +当Nacos客户端启动时, 同时配置了不同的寻址逻辑,Nacos客户端会按照以下优先级进行服务端地址的获取: + +1. 优先使用`EndpointServerListProvider`,尝试读取`endpoint`相关配置,若存在,则使用`EndpointServerListProvider`获取服务端地址列表,`endpoint`相关配置默认又遵循如下优先级; +- 1.1. 其中环境变量`ALIBABA_ALIWARE_ENDPOINT_URL`的优先级最高,如果存在,则使用`ALIBABA_ALIWARE_ENDPOINT_URL`作为地址服务器地址; +- 1.2. 其次是初始化时传入的Properties中`endpoint`配置优先,如果存在,则使用`endpoint`所配置的地址作为地址服务器地址; +- 1.3. 再优先是JVM参数中配置的`endpoint`配置优先,如果存在,则使用JVM中的`endpoint`所配置的地址作为地址服务器地址; +- 1.4. 最后是环境变量中的`endpoint`,如果存在,使用环境变量中`endpoint`中配置的地址作为地址服务器地址; +2. 若以上关于`EndpointServerListProvider`的配置均不存在,则使用`PropertiesServerListProvider` +- 1.1. 优先使用初始化时传入的Properties中`serverAddr`配置,如果存在,则使用`serverAddr`配置的地址作为Nacos服务端地址列表; +- 1.2. 其次是JVM参数中配置的`serverAddr`配置,如果存在,则使用JVM中的`serverAddr`配置的地址作为Nacos服务端地址列表; +- 1.3. 最后是环境变量中的`serverAddr`,如果存在,使用环境变量中`serverAddr`中配置的地址作为Nacos服务端地址列表; + +> 以上配置中的配置优先级顺序为默认情况下的优先级顺序, 不同的编程语言客户端的优先级可能存在差异,请根据实际情况修改;另外,如果设置了[Java SDK 读取配置的优先级顺序](./java-sdk/properties/#13-如何使用),除了`ALIBABA_ALIWARE_ENDPOINT_URL`以外,其余的配置优先级顺序将会按照设置的优先级顺序进行读取。 + +## 2. 自定义的寻址方式 + +> 目前仅Java SDK支持自定义的寻址方式,其他语言的SDK暂不支持。 + +Nacos的Java SDK通过SPI,可以自定义ServerListProvider,实现自定义的寻址方式: + +### 2.1. 引入依赖 + +```xml + + + com.alibaba.nacos + nacos-client + 2.5.0 + +``` + +### 2.2. 实现自定义ServerListProvider + +实现接口`com.alibaba.nacos.client.address.ServerListProvider` + +ServerListProvider的相应接口如果: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|---| +|init|NacosClientProperties, NacosRestTemplate|void|初始化时会调用此接口注入Nacos的配置,HTTP客户端等,以便于您读取初始化时传入的参数,以及通过HTTP接口访问获取其他信息等| +|getServerList|无|List<String>|`ServerListManager`会调用此接口,获取服务端地址列表| +|getServerName|无|String|获取`ServerListProvider`所对应的服务端名称,用于标记地址列表所对应的服务端名称| +|getNamespace|无|String|获取`ServerListProvider`所归属的命名空间,也即此Nacos客户端所归属的命名空间| +|getContextPath|无|String|获取`ServerListProvider`所对应的服务端上下文路径,当Nacos服务端配置了上下文路径时,需要此上下文路径,以便于Nacos客户端访问服务端| +|getOrder|无|int|获取`ServerListProvider`的优先级,数字越大优先级越高,`EndpointServerListProvider`的优先级为`500`,`PropertiesServerListProvider`的优先级为499| +|match|NacosClientProperties|boolean|判断当前`ServerListProvider`是否匹配当前Nacos客户端的配置,如果匹配,则Nacos客户端会调用`init`对此`ServerListProvider`进行初始化| +|isFixed|无|boolean|判断当前`ServerListProvider`是否所提供的地址列表是否为固定的,比如`EndpointServerListProvider`是可变的,`PropertiesServerListProvider`是固定的| +|getAddressSource|无|String|获取`ServerListProvider`的地址列表来源地址,比如`EndpointServerListProvider`返回是`Endpoint`的地址,`PropertiesServerListProvider`是``(空字符串)| +|shutdown|无|void|关闭`ServerListProvider`,关闭时会调用此接口,以便于您关闭相关资源,比如关闭HTTP客户端等| + +### 2.3. 使用自定义ServerListProvider + +添加对应的SPI配置文件`META-INF/services/com.alibaba.nacos.client.address.ServerListProvider`到您的`resources`目录中。 + +比如内容为: + +``` +org.example.MyServerListProvider +``` + +随后在初始化时传入的Properties中配置能让自定义ServerListProvider匹配到的对应参数,比如代码为: + +```java +public class MyServerListProvider implements ServerListProvider { + + public int getOrder() { + return Integet.MAX_VALUE; + } + + public boolean match(NacosClientProperties nacosClientProperties) { + return Boolean.parseBoolean(nacosClientProperties.getProperty("example.server.list.provider")); + } +} +``` + +配置内容应该为: + +```properties +example.server.list.provider=true +``` diff --git a/src/content/docs/v3.0/en/manual/user/auth.mdx b/src/content/docs/v3.0/en/manual/user/auth.mdx new file mode 100644 index 00000000000..8f794eef39c --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/auth.mdx @@ -0,0 +1,116 @@ +--- +title: 配置鉴权 +keywords: [Authorization, SDK] +description: 本文介绍了各语言SDK如何配置身份信息,访问Nacos的默认鉴权系统 +sidebar: + order: 5 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +> 注意 +> - Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。 +> - Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。 +> - 如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做进行[自定义插件开发](../../plugin/auth-plugin.md)。 + +# 配置鉴权 + +Nacos提供了一个简单的默认鉴权实现,当服务端通过[运维手册-权限校验](../admin/auth.mdx)开启鉴权后,客户端、SDK以及openAPI都需要配置身份信息才可以访问Nacos。 本文档介绍如何为各语言客户端及openAPI配置身份信息。 + +## 1. 客户端如何配置鉴权信息 + + + + + 在构建“Properties”类时,需传入用户名和密码。 + ```java + properties.put("username","${username}"); + properties.put("password","${password}"); + ``` + + #### 示例代码 + ```java + try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + // if need username and password to login + properties.put("username","${username}"); + properties.put("password","${password}"); + + ConfigService configService = NacosFactory.createConfigService(properties); + NamingService configService = NacosFactory.createNamingService(properties); + } catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ``` + + + + 在构建“ClientConfig”类时,需传入用户名和密码。 + ```Go + cc := *constant.NewClientConfig( + constant.WithUsername("${username}"), + constant.WithPassword("${password}"), + ) + ``` + + #### 示例代码 + ```Go + sc := []constant.ServerConfig{ + *constant.NewServerConfig("${serverAddr}", 8848, constant.WithContextPath("/nacos")), + } + + //create ClientConfig + cc := *constant.NewClientConfig( + constant.WithUsername("${username}"), + constant.WithPassword("${password}"), + ) + + // create client + namingClient, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + configClient, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + ``` + + + 待补充 + + + +## 2. OpenAPI如何配置鉴权信息 + +首先需要使用用户名和密码登陆nacos。 + +```powershell +curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos' +``` + +若用户名和密码正确,返回信息例如: + +``` +{"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyOTE2Nn0.2TogGhhr11_vLEjqKko1HJHUJEmsPuCxkur-CfNojDo","tokenTtl":18000,"globalAdmin":true} +``` + +接下来进行配置信息或服务信息时,应当使用该accessToken鉴权,在url后添加参数`accessToken=$accessToken`,其中$accessToken为登录时返回的token信息,例如 + +```powershell +curl -X GET '127.0.0.1:8848/nacos/v2/cs/config?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&dataId=nacos.example.1&group=nacos_group' +``` + +```powershell +curl -X POST 'http://127.0.0.1:8848/nacos/v2/ns/instance?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3' +``` diff --git a/src/content/docs/v3.0/en/manual/user/go-sdk/usage.md b/src/content/docs/v3.0/en/manual/user/go-sdk/usage.md new file mode 100644 index 00000000000..397e2757cce --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/go-sdk/usage.md @@ -0,0 +1,388 @@ +--- +title: Go SDK Usage +keywords: [Go,SDK,Usage] +description: This document introduces the usage of Nacos's Go SDK (nacos-sdk-go) +sidebar: + order: 5 +--- + +# Go SDK Usage + + +## 使用限制 +Go>=v1.15 + +Nacos>2.x + +## 安装 +使用`go get`安装SDK: +```sh +$ go get -u github.com/nacos-group/nacos-sdk-go/v2 +``` +## 快速使用 +* 初始化客户端配置ClientConfig + +```go +constant.ClientConfig{ + TimeoutMs uint64 // 请求Nacos服务端的超时时间,默认是10000ms + NamespaceId string // Nacos的命名空间Id + Endpoint string // 当使用地址服务器时,需要该配置. https://help.aliyun.com/document_detail/130146.html + RegionId string // Nacos&KMS的regionId,用于配置中心的鉴权 + AccessKey string // Nacos&KMS的AccessKey,用于配置中心的鉴权 + SecretKey string // Nacos&KMS的SecretKey,用于配置中心的鉴权 + OpenKMS bool // 是否开启kms,默认不开启,kms可以参考文档 https://help.aliyun.com/product/28933.html + // 同时DataId必须以"cipher-"作为前缀才会启动加解密逻辑 + CacheDir string // 缓存service信息的目录,默认是当前运行目录 + UpdateThreadNum int // 监听service变化的并发数,默认20 + NotLoadCacheAtStart bool // 在启动的时候不读取缓存在CacheDir的service信息 + UpdateCacheWhenEmpty bool // 当service返回的实例列表为空时,不更新缓存,用于推空保护 + Username string // Nacos服务端的API鉴权Username + Password string // Nacos服务端的API鉴权Password + LogDir string // 日志存储路径 + RotateTime string // 日志轮转周期,比如:30m, 1h, 24h, 默认是24h + MaxAge int64 // 日志最大文件数,默认3 + LogLevel string // 日志默认级别,值必须是:debug,info,warn,error,默认值是info +} +``` + +* ServerConfig + +```go +constant.ServerConfig{ + ContextPath string // Nacos的ContextPath,默认/nacos,在2.0中不需要设置 + IpAddr string // Nacos的服务地址 + Port uint64 // Nacos的服务端口 + Scheme string // Nacos的服务地址前缀,默认http,在2.0中不需要设置 + GrpcPort uint64 // Nacos的 grpc 服务端口, 默认为 服务端口+1000, 不是必填 +} +``` + +Note:我们可以配置多个ServerConfig,客户端会对这些服务端做轮询请求 + +### Create client + +```go +// 创建clientConfig +clientConfig := constant.ClientConfig{ + NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // 如果需要支持多namespace,我们可以创建多个client,它们有不同的NamespaceId。当namespace是public时,此处填空字符串。 + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + LogDir: "/tmp/nacos/log", + CacheDir: "/tmp/nacos/cache", + LogLevel: "debug", +} + +// 创建clientConfig的另一种方式 +clientConfig := *constant.NewClientConfig( + constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"), //当namespace是public时,此处填空字符串。 + constant.WithTimeoutMs(5000), + constant.WithNotLoadCacheAtStart(true), + constant.WithLogDir("/tmp/nacos/log"), + constant.WithCacheDir("/tmp/nacos/cache"), + constant.WithLogLevel("debug"), +) + +// 至少一个ServerConfig +serverConfigs := []constant.ServerConfig{ + { + IpAddr: "console1.nacos.io", + ContextPath: "/nacos", + Port: 80, + Scheme: "http", + }, + { + IpAddr: "console2.nacos.io", + ContextPath: "/nacos", + Port: 80, + Scheme: "http", + }, +} + +// 创建serverConfig的另一种方式 +serverConfigs := []constant.ServerConfig{ + *constant.NewServerConfig( + "console1.nacos.io", + 80, + constant.WithScheme("http"), + constant.WithContextPath("/nacos"), + ), + *constant.NewServerConfig( + "console2.nacos.io", + 80, + constant.WithScheme("http"), + constant.WithContextPath("/nacos"), + ), +} + +// 创建服务发现客户端 +_, _ := clients.CreateNamingClient(map[string]interface{}{ + "serverConfigs": serverConfigs, + "clientConfig": clientConfig, +}) + +// 创建动态配置客户端 +_, _ := clients.CreateConfigClient(map[string]interface{}{ + "serverConfigs": serverConfigs, + "clientConfig": clientConfig, +}) + +// 创建服务发现客户端的另一种方式 (推荐) +namingClient, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, +) + +// 创建动态配置客户端的另一种方式 (推荐) +configClient, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, +) +``` + +### Create client for Nacos +https://help.aliyun.com/document_detail/130146.html + +```go +cc := constant.ClientConfig{ + Endpoint: "nacos.aliyun.com:8080", + NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", + RegionId: "cn-shanghai", + AccessKey: "LTAI4G8KxxxxxxxxxxxxxbwZLBr", + SecretKey: "n5jTL9YxxxxxxxxxxxxaxmPLZV9", + OpenKMS: true, + TimeoutMs: 5000, + LogLevel: "debug", +} + +// a more graceful way to create config client +client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + }, +) +``` + + +### 服务发现 + +* 注册实例:RegisterInstance + +```go + +success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{ + Ip: "10.0.0.11", + Port: 8848, + ServiceName: "demo.go", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc":"shanghai"}, + ClusterName: "cluster-a", // 默认值DEFAULT + GroupName: "group-a", // 默认值DEFAULT_GROUP +}) + +``` + +* 注销实例:DeregisterInstance + +```go + +success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{ + Ip: "10.0.0.11", + Port: 8848, + ServiceName: "demo.go", + Ephemeral: true, + Cluster: "cluster-a", // 默认值DEFAULT + GroupName: "group-a", // 默认值DEFAULT_GROUP +}) + +``` + +* 获取服务信息:GetService + +```go + +services, err := namingClient.GetService(vo.GetServiceParam{ + ServiceName: "demo.go", + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + GroupName: "group-a", // 默认值DEFAULT_GROUP +}) + +``` + +* 获取所有的实例列表:SelectAllInstances + +```go +// SelectAllInstance可以返回全部实例列表,包括healthy=false,enable=false,weight<=0 +instances, err := namingClient.SelectAllInstances(vo.SelectAllInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT +}) + +``` + +* 获取实例列表 :SelectInstances + +```go +// SelectInstances 只返回满足这些条件的实例列表:healthy=${HealthyOnly},enable=true 和weight>0 +instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + HealthyOnly: true, +}) + +``` + +* 获取一个健康的实例(加权随机轮询):SelectOneHealthyInstance + +```go +// SelectOneHealthyInstance将会按加权随机轮询的负载均衡策略返回一个健康的实例 +// 实例必须满足的条件:health=true,enable=true and weight>0 +instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT +}) + +``` + +* 监听服务变化:Subscribe + +```go + +// Subscribe key=serviceName+groupName+cluster +// 注意:我们可以在相同的key添加多个SubscribeCallback. +err := namingClient.Subscribe(vo.SubscribeParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + SubscribeCallback: func(services []model.Instance, err error) { + log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services)) + }, +}) + +``` + +* 取消服务监听:Unsubscribe + +```go + +err := namingClient.Unsubscribe(vo.SubscribeParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + SubscribeCallback: func(services []model.Instance, err error) { + log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services)) + }, +}) + +``` + +* 获取服务名列表:GetAllServicesInfo +```go + +serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f", + PageNo: 1, + PageSize: 10, + }), + +``` + +### 动态配置 + +* 发布配置:PublishConfig + +```go + +success, err := configClient.PublishConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group", + Content: "hello world!222222"}) + +``` + +* 删除配置:DeleteConfig + +```go + +success, err = configClient.DeleteConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group"}) + +``` + +* 获取配置:GetConfig + +```go + +content, err := configClient.GetConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group"}) + +``` + +* 监听配置变化:ListenConfig + +```go + +err := configClient.ListenConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group", + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data) + }, +}) + +``` +* 取消配置监听:CancelListenConfig + +```go + +err := configClient.CancelListenConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group", +}) + +``` + +* 搜索配置: SearchConfig +```go +configPage,err := configClient.SearchConfig(vo.SearchConfigParam{ + Search: "blur", + DataId: "", + Group: "", + PageNo: 1, + PageSize: 10, +}) +``` +## 例子 +我们能从示例中学习如何使用Nacos go客户端 +* [动态配置示例](./example/config) +* [服务发现示例](./example/service) + +## 文档 +Nacos open-api相关信息可以查看文档 [Nacos open-api wepsite](https://nacos.io/en-us/docs/open-api.html). + +Nacos产品了解可以查看 [Nacos website](https://nacos.io/en-us/docs/what-is-nacos.html). + +## 贡献代码 +我们非常欢迎大家为Nacos-sdk-go贡献代码. 贡献前请查看[CONTRIBUTING.md](./CONTRIBUTING.md) + +## 联系我们 +* 加入Nacos-sdk-go钉钉群(23191211). +* [Gitter](https://gitter.im/alibaba/nacos): Nacos即时聊天工具. +* [Twitter](https://twitter.com/nacos2): 在Twitter上关注Nacos的最新动态. +* [Weibo](https://weibo.com/u/6574374908): 在微博上关注Nacos的最新动态. +* [Nacos SegmentFault](https://segmentfault.com/t/nacos): SegmentFault可以获得最新的推送和帮助. +* Email Group: + * users-nacos@googlegroups.com: Nacos用户讨论组. + * dev-nacos@googlegroups.com: Nacos开发者讨论组 (APIs, feature design, etc). + * commits-nacos@googlegroups.com: Nacos commit提醒. \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/user/java-sdk/failover.md b/src/content/docs/v3.0/en/manual/user/java-sdk/failover.md new file mode 100644 index 00000000000..5597fc916e0 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/java-sdk/failover.md @@ -0,0 +1,143 @@ +--- +title: Java SDK Failover +keywords: [Failover] +description: Java SDL Failover manual +sidebar: + order: 4 +--- + +# Java SDK 容灾 + +我们可以在客户端开启本地容灾,用来应对Nacos服务端出现问题时,保证客户端的数据和接口稳定性。 + +这里有两个使用场景: + +1. 在Nacos服务端发布的时候,我们主动把容灾打开,这样客户端只使用本地容灾数据,Nacos服务的数据抖动或者数据错误都不会影响客户端,我们在Nacos服务端升级完成并且数据验证没问题后再关闭容灾; +2. 在Nacos运行期间,突然出现接口不可用或者数据异常,我们可以快速的开启容灾,让客户端使用容灾数据,减小服务受影响的窗口,等Nacos服务端恢复后再关闭容灾; + +具体方案可以参考:https://github.com/alibaba/nacos/issues/11053 + +## 1. 流程简介 + +image + +如上图所示,客户端的查询请求都会先经过FailoverReactor,如果FailoverReactor有数据,则直接使用,从而忽略掉Nacos Server返回的数据;如果FailoverReactor里面没有数据,则走正常流程,从ServiceInfoHolder里读取缓存; + +## 2. 磁盘容灾 + +FailoverReactor里的数据可以使用不同的数据源,默认的数据源为磁盘。 + +### 2.1. 磁盘容灾文件目录 + +默认的磁盘容灾文件目录为: + +``` +{user.home}/nacos/naming/{namespace}/failover +``` + +这个目录可以定制,如果设置了-D参数: + +``` +-DJM.SNAPSHOT.PATH=/mypath +``` + +则容灾磁盘文件目录变为: + +``` +/mypath/nacos/naming/{namespace}/failover +``` + +### 2.2. 磁盘容灾开关 + +容灾开关存放在磁盘容灾文件目录下的一个文件里,具体文件名为: + +``` +00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 +``` + +文件里存放一个数字0或者1,0代表关闭容灾,1代表打开容灾 + +### 2.3. 磁盘容灾数据 + +容灾的数据分成多个文件,都是存放在磁盘容灾文件目录下,每一个文件存储一个单独的服务的容灾数据,每个文件的文件名格式如下: + +``` +{group.name}%40%40{service.name} +``` + +里面的内容为客户端的ServiceInfo类的JSON序列化字符串,例如: + +``` +{ + "name":"DEFAULT_GROUP@@test.2", + "groupName":"DEFAULT_GROUP", + "clusters":"", + "cacheMillis":10000, + "hosts":[ + { + "instanceId":"1.1.2.1#8888#DEFAULT#DEFAULT_GROUP@@test.2", + "ip":"1.1.2.1", + "port":8888, + "weight":1, + "healthy":true, + "enabled":true, + "ephemeral":true, + "clusterName":"DEFAULT", + "serviceName":"DEFAULT_GROUP@@test.2", + "metadata":{ + "k1":"v1" + }, + "instanceHeartBeatInterval":5000, + "instanceHeartBeatTimeOut":15000, + "ipDeleteTimeout":30000 + } + ], + "lastRefTime":1689835375819, + "checksum":"", + "allIPs":false, + "reachProtectionThreshold":false, + "valid":true +} +``` + +## 3. 扩展容灾数据源 + +磁盘容灾不需要外部依赖,逻辑比较简单,但是管理起来不太方便。因此我们也支持使用SPI来扩展容灾数据源,使用磁盘以外的存储。以下是扩展的步骤。 + +### 3.1. 开发自己的容灾数据源类 + +编写一个类,实现接口com.alibaba.nacos.client.naming.backups.FailoverDataSource: + +``` +public class MyFailoverDataSource implements FailoverDataSource { + + @Override + public FailoverSwitch getSwitch() { + // TODO write your own implementation. + return null; + } + + @Override + public Map getFailoverData() { + // TODO write your own implementation. For naming module, the map + // should contain failover data with service name as key and ServiceInfo as value + return null; + } +} +``` + +### 3.2. 配置容灾数据源类 + +在资源目录下新建文件: + +``` +{resource.root}/META-INF/services/com.alibaba.nacos.client.naming.backups.FailoverDataSource +``` + +{resource.root}的一个例子是src/main/resources。 + +文件内容为: + +``` +your.package.MyFailoverDataSource +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/user/java-sdk/properties.md b/src/content/docs/v3.0/en/manual/user/java-sdk/properties.md new file mode 100644 index 00000000000..ce9e58931b6 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/java-sdk/properties.md @@ -0,0 +1,223 @@ +--- +title: Java SDK Properties +keywords: [ Java,SDK,Properties ] +description: This document introduces the list of configuration parameters currently supported by the Nacos Java SDK (nacos-client), and provides a brief explanation of the principles behind how the Nacos Java SDK reads these configuration parameters. +sidebar: + order: 3 +--- + +# Java SDK 配置参数 + +## 1. Java SDK 读取参数配置 + +### 1.1. 介绍 + +Nacos Java SDK 通过 `NacosClientProperties`, 一个类似于 `Spring Environment`用来统一管理客户端的各种配置项。 + +### 1.2. 特点 + +- 统一管理 Properties、命令行参数、环境变量和默认值 +- 提供优先级搜索功能, 默认搜索顺序 `properties -> 命令行参数 -> 环境参数 -> 默认值`, 可通过调整优先级来调整搜索顺序, + 默认是 `properties` 优先 +- 配置隔离, 每个 `NacosClientProperties` 对象,除去全局性的配置互不影响. + +### 1.3. 如何使用 + +#### 1.3.1. 优先级 + +默认优先级是 `properties`, 可通过以下2种方式来调整: + +``` +- (命令行参数)-Dnacos.env.first=PROPERTIES|JVM|ENV +- (环境变量)NACOS_ENV_FIRST=PROPERTIES|JVM|ENV +``` + +默认情况相当于`-Dnacos.env.first=PROPERTIES`. + +以上2种方式都指定的情况下,客户端优先使用命令行参数的方式获取优先级参数,若是通过命令行参数的方式没有获取到优先级参数则使用环境变量的方式获取优先级参数.如果以上2种方式都未指定优先级参数默认优先级为`properties` + +默认优先级: +![default_order.png](/img/nacos_client_properties_default_order.png) + +优先级: PROPERTIES +![default_order.png](/img/nacos_client_properties_default_order.png) + +优先级: JVM +![jvm_order.png](/img/nacos_client_properties_jvm_order.png) + +优先级: ENV +![jvm_order.png](/img/nacos_client_properties_env_order.png) + +#### 1.3.2. 搜索 + +`NacosClientProperties` 会按照指定优先级进行搜索配置, 以默认优先级(`PROPERTIES`)为例, 如果要获取一个 key 为 +`key1`的值, 查找顺序如下: + +![search_order.png](/img/nacos_client_properties_search_order.png) + +`NacosClientProperties` 会按照上图顺序搜索,直到查询到为止. + +#### 1.3.3. 配置隔离 + +为了应对多注册中心,多配置中心的场景, `NacosClientProperties` 引入配置隔离的概念. 在 `NacosClientProperties` 中总共有4个取值源, +分别是: 用户自定义的properties、命令行参数、 环境变量和默认值, 其中 `命令行参数、 环境变量和默认值` +这3个是全局共享的无法做到隔离, 那么只剩下用户自定义的properties对象是可以进行隔离的, 每个 `NacosClientProperties` +对象中包含不同的 `Properties` 对象, 通过这种方法做到配置互不影响. + +> 注意: 全局共享的配置: 命令行参数、 环境参数和默认值 一旦初始化完毕,后续使用无法更改,使用 `setProperty` +> 方法,也无法修改. `setProperty` 只能修改`NacosClientProperties` 对象中包含的 `Properties` 对象中的值 + +#### 1.3.4. 配置派生 + +在配置隔离的概念之上又引入了配置派生的概念, 其目的是让配置能够继承.所有 `NacosClientProperties` +对象都是由 `NacosClientProperties.PROTOTYPE` 对象派生而来. 无法通过其他方式创建 `NacosClientProperties` 对象 + +```java +// global properties +NacosClientProperties.PROTOTYPE.setProperty("global-key1","global-value1"); + +// properties1 +NacosClientProperties properties1 = NacosClientProperties.PROTOTYPE.derive(); +properties1. + +setProperty("properties1-key1","properties1-value1"); + +// properties2 +NacosClientProperties properties2 = properties1.derive(); +properties2. + +setProperty("properties2-key1","properties2-value1"); +``` + +以上代码如下图所示: +![derive.png](/img/nacos_client_properties_derive.png) + +那么搜索会怎么搜索呢? 以默认优先级(PROPERTIES)为例: + +```java +// value == global-value1 +String value = properties2.getProperty("global-key1"); + +``` + +![derive_search.png](/img/nacos_client_properties_derive_search.png) + +#### 1.3.5. API + +| 方法名 | 入参内容 | 返回内容 | 描述 | +|---------------|-------------------------------|-----------------------|-------------------------------------------------------------------------------------| +| getProperty | key: String | String | 获取 key 对应的 value 值, 不存在返回 null | +| getProperty | key: String, default: String | String | 获取 key 对应的 value 值, 不存在返回默认值 | +| getBoolean | key: String | Boolean | 获取 key 对应的 Boolean 值, 不存在则返回 null | +| getBoolean | key: String, default: Boolean | Boolean | 获取 key 对应的 Boolean 值, 不存在返回默认值 | +| getInteger | key: String | Integer | 获取 key 对应的 Integer 值, 不存在返回 null | +| getInteger | key: String, default: Integer | Integer | 获取 key 对应的 Integer 值, 不存在返回默认值 | +| getLong | key: String | Long | 获取 key 对应的 Long 值, 不存在返回 null | +| getLong | key: String, default: Long | Long | 获取 key 对应的 Long 值, 不存在返回默认值 | +| setProperty | key: String, value: String | void | 设置 key-value 到 NacosClientProperties 对象中,已存的值会被覆盖 | +| addProperties | properties: Properties | void | 添加 Properties 到 NacosClientProperties 对象中,已存在到值会被覆盖 | +| containsKey | key: String | boolean | 判断是否包含指定 key 的值, 包含返回 true 否则 false | +| asProperties | void | Properties | 将 NacosClientProperties 对象转换为 Properties 对象 | +| derive | void | NacosClientProperties | 创建一个继承父 NacosClientProperties 所有配置的 NacosClientProperties 对象, 内部包含一个空 Properties | +| derive | Properties | NacosClientProperties | 创建一个继承父 NacosClientProperties 所有配置的 NacosClientProperties 对象, 内部包含指定的 Properties 对象 | + +## 2. Java SDK 配置参数列表 + +### 2.1. 通用参数 + +通用参数为初始化注册中心`NamingService`和配置中心`ConfigServie`时均生效的参数: + +| 参数名 | PropertyKeyConst的Key | 含义 | 可选值 | 默认值 | +|--------------------------------|-----------------------------------|--------------------------------------------------------------------------------------------------------------|---------------------------------------------------|--------------------------| +| serverAddr | SERVER_ADDR | Nacos Server的地址列表,即此JAVA SDK访问哪个Nacos Server | 任意域名或IP,多个地址通过英文逗号`,`分割,多个地址必须属于同一个Nacos Server集群 | 无 | +| contextPath | CONTEXT_PATH | Nacos Server OpenAPI 的 context path,对应Nacos Server 的`server.servlet.context-path` 参数 | 任意URL支持的path | nacos | +| endpoint | ENDPOINT | Nacos Server的地址获取接入点,Java SDK或通过该接入点查询Nacos Server的实际域名或IP地址 | 任意域名或IP | 无 | +| endpointPort | ENDPOINT_PORT | Nacos Server的地址获取接入点的端口,配合endpoint使用,即请求${endpoint}:${endpointPort}/nacos/serverlist | 0~65535 | 8080 | +| endpointContextPath | ENDPOINT_CONTEXT_PATH | Nacos Server的地址获取接入点的context path,配合endpoint使用,${endpoint}:${endpointPort}/${endpointContextPath}/serverlist | 任意URL支持的path | nacos | +| endPointClusterName | ENDPOINT_CLUSTER_NAME | Nacos Server在接入点中的集群名,配合endpoint使用,${endpoint}:${endpointPort}/${endpointContextPath}/${endPointClusterName} | 任意URL支持的path | serverlist | +| endpointQueryParams | ENDPOINT_QUERY_PARAMS | Nacos Server的地址获取接入点的请求参数,用于接入点服务扩展自定义逻辑,格式key=value | 任意URL参数,key=value | 无 | +| endpointRefreshIntervalSeconds | ENDPOINT_REFRESH_INTERVAL_SECONDS | Nacos Server定期从地址获取接入点重新获取地址列表的间隔时间,单位为秒 | 任意正整数 | 30 | +| namespace | NAMESPACE | 该 JAVA SDK 所归属的命名空间Id, 设置后该SDK只能访问该命名空间的资源(配置或服务) | 命名空间Id | 空字符串`` | +| username | USERNAME | 开启鉴权功能后,访问Nacos Server所使用的用户名 | 任意字符串 | 无 | +| password | PASSWORD | 开启鉴权功能后,访问Nacos Server所使用的用户名对应的密码 | 任意字符串 | 无 | +| accessKey | ACCESS_KEY | 使用阿里云RAM鉴权时需要使用的accessKey | 任意字符串 | 无 | +| secretKey | SECRET_KEY | 使用阿里云RAM鉴权时需要使用的secretKey | 任意字符串 | 无 | | +| ramRoleName | RAM_ROLE_NAME | 使用阿里云RAM鉴权时需要使用的ramRoleName | 任意字符串 | 无 | +| signatureRegionId | SIGNATURE_REGION_ID | 使用阿里云RAM鉴权时,需要使用的signatureRegionId | 任意字符串 | 无 | +| logAllProperties | LOG_ALL_PROPERTIES | 启动Java SDK时,是否打印全量参数,包含自定义properties、JVM和环境变量,主要用户调试和问题排查。 | boolean | false | +| ~~clusterName~~ | ~~CLUSTER_NAME~~ | 由于和服务实例的ClusterName名称相同,容易造成混淆,该参数已废弃,请使用`endPointClusterName` | 任意URL支持的path | serverlist | +| ~~isAdaptClusterNameUsage~~ | ~~IS_ADAPT_CLUSTER_NAME_USAGE~~ | 是否兼容通过`clusterName`的方式设置`endPointClusterName`,让升级兼容性更好 | boolean | false | +| ~~serverName~~ | ~~SERVER_NAME~~ | 该 JAVA SDK 的名称,目前仅在访问endpoint时使用,由于使用率低且命名不合理,将废弃 | 任意字符串 | 由serverAddr/endpoint自动拼接 | + +### 2.2. 配置中心相关参数 + +仅在初始化配置中心`ConfigServie`时生效: + +| 参数名 | PropertyKeyConst的Key | 含义 | 可选值 | 默认值 | +|----------------------------|--------------------------------|-------------------------------------------------------------------|------------|------------------------------------------------| +| clientWorkerMaxThreadCount | CLIENT_WORKER_MAX_THREAD_COUNT | 自动计算配置中心ConfigService进行配置监听时的最大线程池个数 | >=2 的int值 | CPU个数 | +| clientWorkerThreadCount | CLIENT_WORKER_THREAD_COUNT | 指定配置中心ConfigService进行配置监听时的线程池个数,优先级高于clientWorkerMaxThreadCount | >=2 的int值 | Max(2, Min(clientWorkerMaxThreadCount, CPU个数)) | +| enableRemoteSyncConfig | ENABLE_REMOTE_SYNC_CONFIG | 配置中心ConfigService进行配置监听时立刻对监听的配置进行和服务端的同步和通知,开启可能影响启动监听的速度 | boolean | false | +| configRequestTimeout | CONFIG_REQUEST_TIMEOUT | 指定配置中心ConfigService发起rpc请求超时时间, 默认不启用, 使用RpcClientConfig通用配置的超时时间 | >=0 的long值 | -1 | +| ~~configRetryTime~~ | ~~CONFIG_RETRY_TIME~~ | 旧版本配置中心使用长轮询重试间隔时间,已废弃 | 任意int | 2000 | +| ~~configLongPollTimeout~~ | ~~CONFIG_LONG_POLL_TIMEOUT~~ | 旧版本配置中心使用长轮询超时时间,已废弃 | 任意int | 30000 | +| ~~maxRetry~~ | ~~MAX_RETRY~~ | 旧版本配置中心使用的最大重试次数参数,已废弃 | 任意int | 3 | + +### 2.3. 注册中心相关参数 + +仅在初始化注册中心`NamingServie`时生效: + +| 参数名 | PropertyKeyConst的Key | 含义 | 可选值 | 默认值 | +|----------------------------------|--------------------------------------|-------------------------------------------------------------------|------------|-------------------------------------------------| +| namingLoadCacheAtStart | NAMING_LOAD_CACHE_AT_START | 注册中心NamingService在启动时读取本地磁盘缓存来初始化数据 | boolean | false | +| namingCacheRegistryDir | NAMING_CACHE_REGISTRY_DIR | 注册中心NamingService的本地磁盘缓存目录名拓展名,用于同一节点中区分多个NamingService实例 | 任意字符串 | 空字符串 | +| namingAsyncQuerySubscribeService | NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE | 注册中心NamingService开启异步查询订阅服务的功能,作为数据推送链路异常时的兜底辅助 | boolean | false | +| namingPollingMaxThreadCount | NAMING_POLLING_MAX_THREAD_COUNT | 自动计算注册中心NamingService异步查询订阅服务的最大线程个数 | >=1 的int值 | CPU个数 | +| namingPollingThreadCount | NAMING_POLLING_THREAD_COUNT | 指定注册中心NamingService异步查询订阅服务的线程个数,优先级高于namingPollingMaxThreadCount | >=1 的int值 | Max(2, Min(namingPollingMaxThreadCount, CPU个数)) | +| namingRequestDomainMaxRetryCount | NAMING_REQUEST_DOMAIN_RETRY_COUNT | 当初始化注册中心NamingService`serverAddr`仅有一个地址时,请求Nacos Server失败后的最大重试次数 | 任意int值 | 3 | +| namingPushEmptyProtection | NAMING_PUSH_EMPTY_PROTECTION | 注册中心NamingService开启推空保护功能,当订阅服务时发现服务地址列表为0时,忽略此地址列表 | boolean | false | +| redoDelayTime | REDO_DELAY_TIME | 注册中心NamingService与Nacos Server链接断开后,间隔多长时间检查并进行redo操作,单位毫秒 | 任意long值 | 3000 | +| redoDelayThreadCount | REDO_DELAY_THREAD_COUNT | 注册中心NamingService执行redo操作的线程数 | 任意int值 | 1 | +| namingRequestTimeout | NAMING_REQUEST_TIMEOUT | 注册中心NamingService发起rpc请求超时时间, 默认不启用, 使用RpcClientConfig通用配置的超时时间 | >=0 的long值 | -1 | +| ~~namingClientBeatThreadCount~~ | ~~NAMING_CLIENT_BEAT_THREAD_COUNT~~ | 注册中心NamingService旧版本使用的,用于发送所注册服务实例心跳的线程数,已废弃 | 任意int值 | 无 | + +### 2.4. 连接相关 + +Nacos Java SDK 连接Nacos Server时,可以设置一系列的参数,来提升针对网络抖动时的容错能力,这部分配置暂时**只能通过JVM参数( +-D)**进行设置,社区正在进行改造,支持其能够通过`NacosClientProperties`(即初始化Java SDK时传入的Properties)进行配置,尽请期待。 + +| 参数名 | 含义 | 可选值 | 默认值 | +|-----------------------------------------------------------------|------------------------------------------------------------------------------------|---------|---------------| +| nacos.server.grpc.port.offset | Nacos Server GRPC端口相对主端口的偏移量 | 任意int值 | 1000 | +| nacos.remote.client.grpc.name | 该Nacos Java SDK的GRPC连接的名字 | 任意字符串 | null | +| nacos.remote.client.grpc.connect.keep.alive | 该Nacos Java SDK的GRPC连接的Keep Alive | 任意Long值 | 5000 | +| nacos.remote.client.grpc.retry.times | 该Nacos Java SDK的GRPC连接发起请求时的最大重试次数 | 任意int值 | 3 | +| nacos.remote.client.grpc.timeout | 该Nacos Java SDK的GRPC连接发起请求时的请求超时时间 | 任意Long值 | 3000 | +| nacos.remote.client.grpc.pool.alive | 该Nacos Java SDK的GRPC连接所使用的线程池的线程Keep Alive时间,单位毫秒 | 任意Long值 | 10000 | +| nacos.remote.client.grpc.pool.core.size | 该Nacos Java SDK的GRPC连接所使用的线程池的最小大小 | 任意int值 | CPU个数*2 | +| nacos.remote.client.grpc.pool.max.size | 该Nacos Java SDK的GRPC连接所使用的线程池的最大大小 | 任意int值 | CPU个数*8 | +| nacos.remote.client.grpc.server.check.timeout | 该Nacos Java SDK的GRPC连接刚连接上服务端时,进行连接注册的超时时间 | 任意Long值 | 3000 | +| nacos.remote.client.grpc.queue.size | 该Nacos Java SDK的GRPC连接的请求队列长度 | 任意int值 | 10000 | +| nacos.remote.client.grpc.health.retry | 该Nacos Java SDK的GRPC连接的健康检查重试次数,达到这个次数健康检查失败的连接会被客户端强制关闭,进行重连 | 任意int值 | 3 | +| nacos.remote.client.grpc.health.timeout | 该Nacos Java SDK的GRPC连接的健康检查超时时间 | 任意Long值 | 3000 | +| nacos.remote.client.grpc.maxinbound.message.size | 该Nacos Java SDK的GRPC连接单次请求的Request的最大大小,单位byte | 任意int值 | 10M | +| nacos.remote.client.grpc.channel.keep.alive | 该Nacos Java SDK的GRPC连接对应的TCP Channel的Keep Alive时间,此时间应该大于`connect.keep.alive`,单位毫秒 | 任意int值 | 6 * 60 * 1000 | +| nacos.remote.client.grpc.channel.keep.alive.timeout | 该Nacos Java SDK的GRPC连接对应的TCP Channel的Keep Alive超时时间,单位毫秒 | 任意Long值 | 20 * 1000 | +| nacos.remote.client.grpc.channel.capability.negotiation.timeout | 该Nacos Java SDK的GRPC连接对应的TLS握手超时时间 | 任意Long值 | 5000 | + +### 2.5. 其他参数 + +Nacos Java SDK 中有部分参数对运行时期的影响较小,且需要全局一致,因此此类参数目前需要通过JVM参数(-D)或环境变量进行设置,一般使用时使用默认值即可,仅在遇到一些特殊场景时才需要设置。 + +| 参数名 | 含义 | 可选值 | 默认值 | +|---------------------------|--------------------------------------------------------------------------------------------------------------------|--------------------|-------------------| +| PER_TASK_CONFIG_SIZE | 每个`ConfigService`可监听的最大配置数 | 任意int | 3000 | +| JM.SNAPSHOT.PATH | Nacos Java SDK的本地快照根目录,根目录下会创建`naming`和`config`两个目录用于存放订阅的服务和配置的缓存信息 | 任意目录 | ${user.home} | +| JM.LOG.PATH | Nacos Java SDK的日志输出目录,正常情况下Nacos Java SDK的日志会输出到该目录下,部分特殊的场景和版本,可能会输出到业务日志中(如使用了log4j1.0的版本,或使用Spring Cloud重载了日志配置 | 任意目录 | ${user.home}/logs | +| nacos.server.port | 默认的Nacos Server**配置中心和鉴权login**的端口,当传入的`serverAddr`参数中不带有端口号时,使用此设置的端口连接Nacos Server,建议统一通过`serverAddr`参数设置端口 | 0~65535 | 8848 | +| nacos.naming.exposed.port | 默认的Nacos Server**注册中心**的端口,当传入的`serverAddr`参数中不带有端口号时,使用此设置的端口连接Nacos Server,建议统一通过`serverAddr`参数设置端口 | 0~65535 | 8848 | +| nacos.client.contextPath | 默认的Nacos Server contentPath,`contextPath`和`endpointContextPath` 未传入时使用 | 任意URL支持的path | nacos | +| nacos.env.first | Nacos JAVA SDK 的 `NacosClientProperties` 配置搜索顺序。详情见[1.3.1. 优先级](#131-优先级) | PROPERTIES/JVM/ENV | PROPERTIES | +| project.name | 该SDK所归属的应用名,可在服务订阅者列表和配置订阅者列表中使用,仅作为参考字段使用 | 任意字符串 | unknown | +| ~~NACOS.CONNECT.TIMEOUT~~ | 连接服务时的连接超时时间,旧版本Http使用,已废弃 | 任意int | 1000 | +| NACOS.READ.TIMEOUT | 连接服务时的读取超时时间,旧版本Http使用 | 任意int | 3000 | diff --git a/src/content/docs/v3.0/en/manual/user/java-sdk/usage.md b/src/content/docs/v3.0/en/manual/user/java-sdk/usage.md new file mode 100644 index 00000000000..c8b1c153334 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/java-sdk/usage.md @@ -0,0 +1,1068 @@ +--- +title: Java SDK Usage +keywords: [Java,SDK,Usage] +description: This document introduces the usage of Nacos's Java SDK (nacos-client), including how to configure the Nacos Client, how to use the Nacos Client, and how to utilize the Nacos Client's API. +sidebar: + order: 2 +--- + +# Java SDK 使用手册 + +## 1. 引用概述 + +Maven 坐标 +``` + + com.alibaba.nacos + nacos-client + ${version} + +``` + +> 注意:由于Nacos Java SDK在2.0版本后引入了gRPC,为了避免用户业务引入的gRPC版本不同导致冲突,使用了shaded技术将部分依赖直接封装进nacos-client中,导致nacos-client较大。 +> 如果用户未自行引入gRPC或确认版本无冲突,希望使用纯净版的nacos-client以减小依赖,可以使用classifier来指定使用纯净版。 + +```xml + + + 2.4.2 + + + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + pure + + + + ${project.groupId} + nacos-common + ${nacos.version} + + + ${project.groupId} + nacos-api + ${nacos.version} + + +``` + +## 2. 初始化SDK + +Nacos 初始化SDK仅需要使用 `NacosFactory` 类进行不同模块的创建即可: + +```java + +String serverAddr = "localhost:8848"; + +# 初始化配置中心的Nacos Java SDK +ConfigService configService = NacosFactory.createConfigService(serverAddr); + +# 初始化配置中心的Nacos Java SDK +NamingService namingService = NacosFactory.createNamingService(serverAddr); +``` + +如果初始化SDK时,还需要配置一些参数,可以使用 `Properties` 类进行配置: + +```java + +Properties properties = new Properties(); +# 指定Nacos-Server的地址 +properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); +# 指定Nacos-SDK的命名空间 +properties.setProperty(PropertyKeyConst.NAMESPACE, "${namespaceId}"); + +# 初始化配置中心的Nacos Java SDK +ConfigService configService = NacosFactory.createConfigService(properties); + +# 初始化配置中心的Nacos Java SDK +NamingService namingService = NacosFactory.createNamingService(properties); +``` + +更多初始化时所涉及的参数配置,请参考[Java SDK 配置参数](./properties.md)。 + +> 注意:一个Nacos Java SDK实例只能用于获取同一个命名空间下的配置和服务,如果要获取不同的命名空间下的配置或服务,需要创建不同的Nacos Java SDK实例。 + +## 3. 配置管理 API +### 3.1. 获取配置 +#### 描述 + +用于服务启动的时候从 Nacos 获取配置。 +```java +public String getConfig(String dataId, String group, long timeoutMs) throws NacosException +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写产品名:模块名(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| timeout | long | 读取配置超时时间,单位 ms,推荐值 3000。 | + + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值 | + + +#### 请求示例 + +```java +try { + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfig(dataId, group, 5000); + System.out.println(content); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.2. 监听配置 +#### 描述 + +如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。 + +```java +public void addListener(String dataId, String group, Listener listener) +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- |:-----------------------------------------------------------------------------------------------------------------------------------------------| +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写**产品名:模块名**(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| listener | Listener | 监听器,配置变更进入监听器的回调函数。 | + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值,初始化或者配置变更的时候通过回调函数返回该值。 | + + +#### 请求示例 + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +String content = configService.getConfig(dataId, group, 5000); +System.out.println(content); +configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("recieve1:" + configInfo); + } + @Override + public Executor getExecutor() { + return null; + } +}); + +// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码 +while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } +} +``` + +### 3.3. 删除监听 +#### 描述 + +取消监听配置,取消监听后配置不会再推送。 + +```java +public void removeListener(String dataId, String group, Listener listener) +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组 | +| listener | ConfigChangeListenerAdapter | 监听器,配置变更进入监听器的回调函数。 | + + +#### 使用示例 + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +configService.removeListener(dataId, group, yourListener); +``` + +### 3.4. 发布配置 +#### 描述 + +用于通过程序自动发布 Nacos 配置,以便通过自动化手段降低运维成本。 + +注意:创建和修改配置时使用的同一个发布接口,当配置不存在时会创建配置,当配置已存在时会更新配置。 + +```java +public boolean publishConfig(String dataId, String group, String content) throws NacosException; + +public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException; + +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 `package.class`(如 `com.taobao.tc.refund.log.level`)的命名规则保证全局唯一性。建议根据配置的业务含义来定义 class 部分。全部字符均为小写。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 256 字节。 | +| group | string | 配置分组,建议填写`产品名:模块名`(如 Nacos`:Test`)来保证唯一性。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 128 字节。 | +| content | string | 配置内容,不超过 100K 字节。 | +| type | string | @Since 1.4.1. 配置类型,见 `com.alibaba.nacos.api.config.ConfigType`,默认为TEXT | + + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否发布成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isPublishOk = configService.publishConfig(dataId, group, "content"); + System.out.println(isPublishOk); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.5. 删除配置 +#### 描述 + +用于通过程序自动删除 Nacos 配置,以便通过自动化手段降低运维成本。 + +>注意: 当配置已存在时会删除该配置,当配置不存在时会直接返回成功消息。 + + +```java +public boolean removeConfig(String dataId, String group) throws NacosException + +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID | +| group | string | 配置分组 | + + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否删除成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isRemoveOk = configService.removeConfig(dataId, group); + System.out.println(isRemoveOk); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.6. 带监听器的获取配置 + +#### 描述 + +如果希望在程序首次启动获取配置时自行注册的Listener用于以后配置更新,建议您直接使用该接口。 + +> 该接口等价于先使用`getConfig`之后再使用`addListener`。 + +```java +String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException; +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写产品名:模块名(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| timeout | long | 读取配置超时时间,单位 ms,推荐值 3000。 | +| listener | Listener | 监听器,配置变更进入监听器的回调函数。 | + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值 | + + +#### 请求示例 + +```java +try { + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfigAndSignListener(dataId, group, 5000, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("recieve1:" + configInfo); + } + @Override + public Executor getExecutor() { + return null; + } + }); + System.out.println(content); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.7. 带Compare-And-Swap(CAS)的发布配置 + +#### 描述 + +直接使用`publishConfig`进行配置发布时,可能存在不同进程间并发的配置覆盖问题,此时可以使用带Compare-And-Swap(CAS)的发布配置API,来保证不会此类情形。 + +注意:创建和修改配置时使用的同一个发布接口,当配置不存在时会创建配置,当配置已存在时会更新配置。 + +```java +boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException; + +boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException; +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| dataId | string | 配置 ID,采用类似 `package.class`(如 `com.taobao.tc.refund.log.level`)的命名规则保证全局唯一性。建议根据配置的业务含义来定义 class 部分。全部字符均为小写。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 256 字节。 | +| group | string | 配置分组,建议填写`产品名:模块名`(如 Nacos`:Test`)来保证唯一性。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 128 字节。 | +| content | string | 配置内容,不超过 100K 字节。 | +| casMd5 | string | 前配置内容的md5 | +| type | string | 配置类型,见 `com.alibaba.nacos.api.config.ConfigType`,默认为TEXT | + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否发布成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + String oldContent = "oldContent"; + String oldContentMd5 = "63fb636909f1ebad67110e49117e6de4"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + # 首次发布,casMd5传入null。 + boolean isPublishOk = configService.publishConfigCas(dataId, group, oldContent, null); + System.out.println(isPublishOk); + # old Md5 正确,变成成功 + isPublishOk = configService.publishConfigCas(dataId, group, "newContent", oldContentMd5); + System.out.println(isPublishOk); + # old Md5 错误,变成失败 + isPublishOk = configService.publishConfigCas(dataId, group, "newContent2", oldContentMd5); + System.out.println(isPublishOk); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +## 4. 服务发现API +### 4.1. 注册实例 +#### 描述 +注册一个实例到服务。 + +> 由于同一个Nacos Client实例,仅能向一个服务注册一个实例;若同一个Nacos Client实例多次向同一个服务注册实例,后注册的实例将会覆盖先注册的实例。 +> 若有存在代理注册的场景,请使用[批量注册服务实例](#48-批量注册服务实例) + +```java +void registerInstance(String serviceName, String ip, int port) throws NacosException; + +void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException; + +void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; + +void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException; + +void registerInstance(String serviceName, Instance instance) throws NacosException; + +void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:-------|----------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| ip | 字符串 | 服务实例IP | 无,必填 | +| port | int | 服务实例port | 无,必填 | +| clusterName | 字符串 | 集群名 | DEFAULT | +| instance | 参见代码注释 | 实例属性 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); + +# 以下注册请求所造成的结果均一致, 注册分组名为`DEFAULT_GROUP`, 服务名为`nacos.test.service`的实例,实例的ip为`127.0.0.1`, port为`8848`, clusterName为`DEFAULT`. +naming.registerInstance("nacos.test.service", "127.0.0.1", 8848); +naming.registerInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848); +naming.registerInstance("nacos.test.service", "127.0.0.1", 8848, "DEFAULT"); +naming.registerInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848, "DEFAULT"); +Instance instance = new Instance(); +instance.setIp("127.0.0.1"); +instance.setPort(8848); +instance.setClusterName("DEFAULT"); +naming.registerInstance("nacos.test.service", instance); +naming.registerInstance("nacos.test.service", "DEFAULT_GROUP", instance); +``` + +### 4.2. 注销实例 +#### 描述 +删除服务下的一个实例。 + +> 若该服务是通过[批量注册服务实例](#48-批量注册服务实例)进行注册,使用注销实例进行注销时,将注销所有批量注册的实例。 +> 若仅希望注销部分批量注册的实例,请使用[批量注销服务实例](#49-批量注销服务实例) + +```java +void deregisterInstance(String serviceName, String ip, int port) throws NacosException; + +void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException; + +void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; + +void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException; + +void deregisterInstance(String serviceName, Instance instance) throws NacosException; + +void deregisterInstance(String serviceName, String groupName, Instance instance); +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:-------|----------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| ip | 字符串 | 服务实例IP | 无,必填 | +| port | int | 服务实例port | 无,必填 | +| clusterName | 字符串 | 集群名 | DEFAULT | +| instance | 参见代码注释 | 实例属性 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下注销请求所造成的结果均一致, 注销分组名为`DEFAULT_GROUP`, 服务名为`nacos.test.service`的实例,实例的ip为`127.0.0.1`, port为`8848`, clusterName为`DEFAULT`. +naming.deregisterInstance("nacos.test.service", "127.0.0.1", 8848); +naming.deregisterInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848); +naming.deregisterInstance("nacos.test.service", "127.0.0.1", 8848, "DEFAULT"); +naming.deregisterInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848, "DEFAULT"); +Instance instance = new Instance(); +instance.setIp("127.0.0.1"); +instance.setPort(8848); +instance.setClusterName("DEFAULT"); +naming.deregisterInstance("nacos.test.service", instance); +naming.deregisterInstance("nacos.test.service", "DEFAULT_GROUP", instance); +``` + +### 4.3. 获取全部实例 +#### 描述 +获取服务下的所有实例。 +```java +List getAllInstances(String serviceName) throws NacosException; + +List getAllInstances(String serviceName, String groupName) throws NacosException; + +List getAllInstances(String serviceName, boolean subscribe) throws NacosException; + +List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException; + +List getAllInstances(String serviceName, List clusters) throws NacosException; + +List getAllInstances(String serviceName, String groupName, List clusters) throws NacosException; + +List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException; + +List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------|------------------------------------------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| subscribe | Boolean | 是否订阅服务,为true时将会订阅该服务,同时查询优先通过内存缓存;为false时将直接查询Nacos Server | true | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | + +#### 返回参数 +List<Instance> 实例列表。 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下查询请求所造成的结果均一致. +System.out.println(naming.getAllInstances("nacos.test.service")); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP")); +System.out.println(naming.getAllInstances("nacos.test.service", true)); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP", true)); +System.out.println(naming.getAllInstances("nacos.test.service", new ArrayList<>())); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>())); +System.out.println(naming.getAllInstances("nacos.test.service", new ArrayList<>(), true)); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true)); +``` + +### 4.4. 获取健康或不健康实例列表 +#### 描述 +根据条件获取过滤后的实例列表。 +```java +List selectInstances(String serviceName, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException; + +List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException; + +List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, String groupName, List clusters, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException; + +List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------|------------------------------------------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| subscribe | Boolean | 是否订阅服务,为true时将会订阅该服务,同时查询优先通过内存缓存;为false时将直接查询Nacos Server | true | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| healthy | boolean | 是否健康,为true时仅会返回健康的实例列表,反之则返回不健康的实例列表。 | true | + +#### 返回参数 +List<Instance> 实例列表。 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下查询请求所造成的结果均一致. +System.out.println(naming.selectInstances("nacos.test.service", true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", true)); +System.out.println(naming.selectInstances("nacos.test.service", true, true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", true, true)); +System.out.println(naming.selectInstances("nacos.test.service", new ArrayList<>(), true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true)); +System.out.println(naming.selectInstances("nacos.test.service", new ArrayList<>(), true, true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true, true)); +``` + +### 4.5. 获取一个健康实例 +#### 描述 +根据负载均衡算法随机获取一个健康实例。 +```java +Instance selectOneHealthyInstance(String serviceName) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------|------------------------------------------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| subscribe | Boolean | 是否订阅服务,为true时将会订阅该服务,同时查询优先通过内存缓存;为false时将直接查询Nacos Server | true | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | + + +#### 返回参数 +Instance 实例。 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下查询请求所造成的结果均一致. +System.out.println(naming.selectOneHealthyInstance("nacos.test.service")); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP")); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", true)); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP", true)); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", new ArrayList<>())); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>())); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", new ArrayList<>(), true)); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true)); +``` + +### 4.6. 监听服务 +#### 描述 +监听服务下的实例列表变化。 +```java +void subscribe(String serviceName, EventListener listener) throws NacosException; + +void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException; + +void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException; + +void subscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| listener | EventListener | 回调listener | 无,必填 | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}; +naming.subscribe("nacos.test.service", serviceListener); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", serviceListener); +naming.subscribe("nacos.test.service", new ArrayList<>(), serviceListener); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), serviceListener); +``` + +#### 使用自定义线程池进行异步监听 + +Nacos 支持使用自定义线程池进行异步监听回调,只需要将`EventListener`更换为`AbstractEventListener`,并实现`Executor getExecutor()`方法来返回自定义的线程池,Nacos Client将在服务发生变更时使用该线程池进行异步回调。 + +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +ExecutorService executorService = Executors.newFixedThreadPool(1); +EventListener serviceListener = new AbstractEventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } + } + + @Override + public Executor getExecutor() { + return executorService; + } +}; +naming.subscribe("nacos.test.service", serviceListener); +``` + +#### 监听服务变化的差值 + +Nacos 从2.4.0版本你开始,支持监听服务变化的差值,即和之前相比,有哪些实例被新增,移除和修改,只需要将`EventListener`更换为`AbstractNamingChangeListener`,实现`onChange`方法即可。`onChange`中会传入`NamingChangeEvent`,其中`InstancesDiff`记录了此次通知和之前相比的实例变化。 + +同时为了防止差值的错误和异常,`NamingChangeEvent`仍然可以通过`getInstances`方法获取最终的服务实例列表。 + +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +ExecutorService executorService = Executors.newFixedThreadPool(1); +EventListener serviceListener = new AbstractNamingChangeListener() { + @Override + public void onChange(NamingChangeEvent event) { + if (event.isAdded()) { + System.out.println(event.getAddedInstances()); + } + if (event.isRemoved()) { + System.out.println(event.getRemovedInstances()); + } + if (event.isModified()) { + System.out.println(event.getModifiedInstances()); + } + } + + @Override + public Executor getExecutor() { + return executorService; + } +}; +naming.subscribe("nacos.test.service", serviceListener); +``` + +### 4.7. 取消监听服务 +#### 描述 +取消监听服务下的实例列表变化。 +```java +void unsubscribe(String serviceName, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| listener | EventListener | 回调listener | 无,必填 | + +> 注意:取消监听服务时,需要使用进行订阅时的`listener`进行取消监听,否则可能造成取消监听失败。 + +#### 返回参数 +无 + +#### 请求示例 +```java + +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> {}; +naming.subscribe("nacos.test.service", serviceListener); +naming.unsubscribe("nacos.test.service", serviceListener); +naming.unsubscribe("nacos.test.service", "DEFAULT_GROUP", serviceListener); +naming.unsubscribe("nacos.test.service", new ArrayList<>(), serviceListener); +naming.unsubscribe("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), serviceListener); +``` + +### 4.8. 批量注册服务实例 + +#### 描述 + +注册一系列实例到指定服务。 + +> 由于同一个Nacos Client实例,仅能向一个服务注册一个实例;若同一个Nacos Client实例多次向同一个服务注册实例,后注册的实例将会覆盖先注册的实例。 +> 考虑到社区存在代理注册的场景:如Nacos-Sync, Proxy-Registry等,需要在一个客户端中注册同一个服务的不同实例,社区新增了批量注册服务实例的功能。 + +```java +void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|--------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| instances | Instance的List | 服务实例列表 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); + +Instance instance1 = new Instance(); +instance1.setIp("127.0.0.1"); +instance1.setPort(8848); +instance1.setClusterName("DEFAULT"); + +Instance instance2 = new Instance(); +instance2.setIp("127.0.0.1"); +instance2.setPort(9848); +instance2.setClusterName("DEFAULT"); + +List instances = new ArrayList<>(2); +instances.add(instance1); +instances.add(instance2); + +naming.batchRegisterInstance("nacos.test.service", "DEFAULT_GROUP", instances); +``` + +### 4.9. 批量注销服务实例 + +#### 描述 + +从指定服务中注销一系列实例。 + +> 针对使用了批量注册服务实例的用户设计,允许用户选择一部分或全部批量注册的实例进行注销。 + +```java +void batchDeregisterInstance(String serviceName, String groupName, List instances) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|--------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| instances | Instance的List | 服务实例列表 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); + +Instance instance1 = new Instance(); +instance1.setIp("127.0.0.1"); +instance1.setPort(8848); +instance1.setClusterName("DEFAULT"); + +Instance instance2 = new Instance(); +instance2.setIp("127.0.0.1"); +instance2.setPort(9848); +instance2.setClusterName("DEFAULT"); + +List instances = new ArrayList<>(2); +instances.add(instance1); +instances.add(instance2); + +naming.batchRegisterInstance("nacos.test.service", "DEFAULT_GROUP", instances); +naming.batchDeregisterInstance("nacos.test.service", "DEFAULT_GROUP", instances); +``` + +### 4.10. 带选择器的监听服务 + +#### 描述 + +使用自定义逻辑的选择器,监听服务下的实例列表变化,当服务列表发生变化时,会使用自定义的选择器进行过滤,当过滤后的数据仍然有变化时,才会进行回调通知。 + +```java +void subscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; + +void subscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:---------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| selector | NamingSelector | 自定义的数据选择器 | 无,必填 | +| listener | EventListener | 回调listener | 无,必填 | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}; +# 只会选择订阅ip为`127.0`开头的实例。 +NamingSelector selector = NamingSelectorFactory.newIpSelector("127.0.*"); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", selector, serviceListener); + +``` + +#### 预设提供的数据选择器 + +Nacos Client 提供了预设的多种数据选择器以供默认场景下使用: +1. Cluster选择器,`NamingSelectorFactory.newClusterSelector(Collection clusters)`, 当订阅服务时传入了`clusters`参数,Nacos Client将自动使用该数据选择器。 +2. Ip选择器,`NamingSelectorFactory.newClusterSelector(String ipRegex)`,当实例的ip满足传入的ipRegex时,才会被通知回调。 +3. 元数据选择器,`NamingSelectorFactory.newMetadataSelector(Map metadata)`,当实例的元数据包含**所有**传入选择器的metadata时,才会被通知回调。 +4. 任意元数据选择器,`NamingSelectorFactory.newMetadataSelector(Map metadata, false)`,当实例的元数据包含**任意一对**传入选择器的metadata时,才会被通知回调。 + +#### 开发自定义数据选择器 + +多数情况下, 开发自定义数据选择器只需要创建`DefaultNamingSelector`即可,在构建时传入一个`Predicate filter`作为单个实例是否满足您过滤条件的结果,类似Java中stream的filter方法,如此您仅需要考虑单个实例的过滤条件即可。 + +若是`DefaultNamingSelector`无法满足需求,您需要实现`NamingSelector`接口,根据传入的`NamingContext`进行复杂的逻辑校验,最后输出`NamingResult`给Nacos Client。 + +### 4.11. 取消带选择器的监听服务 + +#### 描述 + +使用自定义逻辑的选择器进行监听服务下的实例列表变化,那么在取消监听时需要使用`取消带选择器的监听服务`的API才能正确取消监听。 + +> 注意:取消监听时需要传入监听时使用的selector和listener,否则可能导致取消监听失败。 + +```java +void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:---------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| selector | NamingSelector | 自定义的数据选择器 | 无,必填 | +| listener | EventListener | 回调listener | 无,必填 | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}; +# 只会选择订阅ip为`127.0`开头的实例。 +NamingSelector selector = NamingSelectorFactory.newIpSelector("127.0.*"); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", selector, serviceListener); +naming.unsubscribe("nacos.test.service", "DEFAULT_GROUP", selector, serviceListener); + +``` + +### 4.12. 分页获取服务列表 + +#### 描述 + +通过分页的方式获取当前客户端所在命名空间的服务列表 + +```java +ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException; + +ListView getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException; +``` + +> 注意,使用`AbstractSelector`的`getServicesOfServer`方法已废弃,请勿继续使用。 + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:----------|:----|------------|---------------| +| pageNo | int | 分页序号 | 无,必填 | +| pageSize | int | 分页中每页的服务个数 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | + +#### 返回参数 +服务名列表: ListView + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 等价于`naming.getServicesOfServer(1, 10, "DEFAULT_GROUP");` +ListView result = naming.getServicesOfServer(1, 10); +System.out.println(result.getCount()); +System.out.println(result.getData()); +``` + +### 4.13. 获取当前客户端所监听的服务列表 + +#### 描述 + +获取当前客户端所的所有服务列表 + +```java +List getSubscribeServices() throws NacosException; +``` + +#### 请求参数 + +无 + +#### 返回参数 +服务列表: List + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.getSubscribeServices()); +``` + +## 5. Java SDK的生命周期 + +Nacos的Java SDK 生命周期从创建时开始,到调用`shutdown()`方法时结束,期间对应创建的线程池、连接等均会始终保留,及时连接断开,也会不断重试重新建立连接。 + +因此在使用时需要注意应用中创建的Nacos Java SDK的实例个数,避免造成线程池和连接的泄漏,在更换Nacos Java SDK实例时,切记调用`shutdown()`方法,同时在应用中应尽量复用同一个Nacos Java SDK实例,避免频繁的初始化实例。 diff --git a/src/content/docs/v3.0/en/manual/user/open-api.md b/src/content/docs/v3.0/en/manual/user/open-api.md new file mode 100644 index 00000000000..6934f995d72 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/open-api.md @@ -0,0 +1,1919 @@ +--- +title: Open API 手册 +keywords: [Open API,手册] +description: Open API 手册 +sidebar: + order: 7 +--- + +# Open API 手册 + +Nacos 2.X 版本兼容 Nacos1.X 版本的OpenAPI, 请参考文档[Nacos1.X OpenAPI](https://nacos.io/docs/v1/open-api)使用。 + +## 1. 文档规定 + +### 1.1. API 统一返回体格式 + +2.0版本Open API,所有接口请求的响应均为`json`类型的返回体,返回体具有相同的格式 + +```json +{ + "code": 0, + "message": "success", + "data": {} +} +``` + +返回体中各字段的含义如下表所示 + +| 名称 | 类型 | 描述 | +|:---------:|:--------:|--------------------------------------------------------| +| `code ` | `int` | 错误码,`0`代表执行成功,非`0`代表执行失败的某一种情况 | +| `message` | `String` | 错误码提示信息,执行成功为"`success`" | +| `data` | 任意类型 | 返回数据,执行失败时为详细出错信息 | + +> 由于执行成功的情况下code字段与message字段相同,后续在介绍接口的返回结果时,只介绍返回数据的data字段 + +### 1.2. API 错误码汇总 + +API接口返回体中的错误码及对应提示信息汇总见下表 + +| 错误码 | 提示信息 | 含义 | +|---------|------------------------------|----------------------------| +| `0` | `success` | 成功执行 | +| `10000` | `parameter missing` | 参数缺失 | +| `10001` | `access denied` | 访问拒绝 | +| `10002` | `data access error` | 数据访问错误 | +| `20001` | `'tenant' parameter error` | `tenant`参数错误 | +| `20002` | `parameter validate error` | 参数验证错误 | +| `20003` | `MediaType Error` | 请求的`MediaType`错误 | +| `20004` | `resource not found` | 资源未找到 | +| `20005` | `resource conflict` | 资源访问冲突 | +| `20006` | `config listener is null` | 监听配置为空 | +| `20007` | `config listener error` | 监听配置错误 | +| `20008` | `invalid dataId` | 无效的`dataId`(鉴权失败) | +| `20009` | `parameter mismatch` | 请求参数不匹配 | +| `21000` | `service name error` | `serviceName`服务名错误 | +| `21001` | `weight error` | `weight`权重参数错误 | +| `21002` | `instance metadata error` | 实例`metadata`元数据错误 | +| `21003` | `instance not found` | `instance`实例不存在 | +| `21004` | `instance error` | `instance`实例信息错误 | +| `21005` | `service metadata error` | 服务`metadata`元数据错误 | +| `21006` | `selector error` | 访问策略`selector`错误 | +| `21007` | `service already exist` | 服务已存在 | +| `21008` | `service not exist` | 服务不存在 | +| `21009` | `service delete failure` | 存在服务实例,服务删除失败 | +| `21010` | `healthy param miss` | `healthy`参数缺失 | +| `21011` | `health check still running` | 健康检查仍在运行 | +| `22000` | `illegal namespace` | 命名空间`namespace`不合法 | +| `22001` | `namespace not exist` | 命名空间不存在 | +| `22002` | `namespace already exist` | 命名空间已存在 | +| `23000` | `illegal state` | 状态`state`不合法 | +| `23001` | `node info error` | 节点信息错误 | +| `23002` | `node down failure` | 节点离线操作出错 | +| ... | ... | ... | +| 30000 | `server error` | 其他内部错误 | + +## 2. 配置管理 + +### 2.1. 获取配置 + +#### 接口描述 + +获取指定配置 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/config` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `tag` | `String` | 否 | 标签 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| `data` | `String` | 配置内容 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "contentTest" + } + ``` + +### 2.2. 发布配置 + +#### 接口描述 + +发布指定配置 + +> 当配置已存在时,则对配置进行更新 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/cs/config` + +#### 请求Body + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置组名 | +| `dataId` | `String` | **是** | 配置名 | +| `content` | `String` | **是** | 配置内容 | +| `tag` | `String` | 否 | 标签 | +| `appName` | `String` | 否 | 应用名 | +| `srcUser` | `String` | 否 | 源用户 | +| `configTags` | `String` | 否 | 配置标签列表,可多个,逗号分隔 | +| `desc` | `String` | 否 | 配置描述 | +| `use` | `String` | 否 | - | +| `effect` | `String` | 否 | - | +| `type` | `String` | 否 | 配置类型 | +| `schema` | `String` | 否 | - | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'dataId=nacos.example' \ + -d 'group=DEFAULT_GROUP' \ + -d 'namespaceId=public' \ + -d 'content=contentTest' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/cs/config' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 2.3. 删除配置 + +#### 接口描述 + +删除指定配置 + +#### 请求方式 + +`DELETE` + +#### 请求URL + +`/nacos/v2/cs/config` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `tag` | `String` | 否 | 标签 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 2.3. 查询配置历史列表 + +#### 接口描述 + +获取指定配置的历史版本列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history/list` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `pageNo` | `int` | 否 | 当前页,默认为`1` | +| `pageSize` | `int` | 否 | 页条目数,默认为`100`,最大为`500` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------|------------|----------------------------------------------------------| +| `data` | `Object` | 分页查询结果 | +| `data.totalCount` | `int` | 总数 | +| `data.pageNumber` | `int` | 当前页 | +| `data.pagesAvailable` | `int` | 总页数 | +| `data.pageItems` | `Object[]` | 历史配置项列表,参见[历史配置项信息](#ConfigHistoryInfo) | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/list?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "totalCount": 1, + "pageNumber": 1, + "pagesAvailable": 1, + "pageItems": [ + { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + ] + } + } + ``` + +### 2.3. 查询具体版本的历史配置 + +#### 接口描述 + +获取指定版本的历史配置 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `nid` | `long` | **是** | 历史配置id | + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|----------|------------| +| `data` | `Object` | 历史配置项 | +| `data.id` | `String` | 配置`id` | +| `data.lastId` | `int` | | +| `data.dataId` | `String` | 配置名 | +| `data.group` | `String` | 配置分组 | +| `data.tenant` | `String` | 租户信息(命名空间) | +| `data.appName` | `String` | 应用名 | +| `data.md5` | `String` | 配置内容的md5值 | +| `data.content` | `String` | 配置内容 | +| `data.srcIp` | `String` | 源ip | +| `data.srcUser` | `String` | 源用户 | +| `data.opType` | `String` | 操作类型 | +| `data.createdTime` | `String` | 创建时间 | +| `data.lastModifiedTime` | `String` | 上次修改时间 | +| `data.encryptedDataKey` | `String` | | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=&nid=203' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +### 2.6. 查询配置上一版本信息 + +#### 接口描述 + +获取指定配置的上一版本 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history/previous` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `id` | `long` | **是** | 配置id | + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|--------|----------|------------------------------------------------------| +| `data` | `Object` | 历史配置项,参见[历史配置项信息](#ConfigHistoryInfo) | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/previous?id=309135486247505920&dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +## 3. 服务发现 + +:::note +Nacos v2的openAPI中,已移除关于`发送实例心跳`的相关API,原因是Nacos2.0对于非持久化实例,已经使用长链接作为存活的基准,而不是通过传统`心跳续约`的方式进行。因此对于使用Nacos2.0的客户端用户,以及针对Nacos2.0进行服务开发的用户,其实已经不再需要使用v2的心跳openAPI。 + +Nacos v2的`注册实例`的OpenAPI,更多的是给予持久化服务实例进行注册;非持久化实例的注册,建议采用Nacos2.0客户端进行,获取更高的性能及更灵敏的变化感知能力。 + +对于仍然在使用Nacos1.X客户端的用户,以及基于Nacos v1 `发送实例心跳`的openAPI的用户,依旧可以继续使用。 +::: + +### 3.1. 注册实例 + +#### 接口描述 + +注册一个实例 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.2. 注销实例 + +#### 接口描述 + +注销指定实例 + +#### 请求方式 + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.3. 更新实例 + +#### 接口描述 + +修改实例信息 + +> 通过该接口更新的元数据拥有更高的优先级,且具有记忆能力;会在对应实例删除后,依旧存在一段时间,如果在此期间实例重新注册,该元数据依旧生效;您可以通过`nacos.naming.clean.expired-metadata.expired-time`及`nacos.naming.clean.expired-metadata.interval`对记忆时间进行修改 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.4. 查询实例详情 + +#### 接口描述 + +查询某个具体实例的详情信息 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------|-----------|--------------| +| `data` | `Object` | 实例详情信息 | +| `data.serviceName` | `String` | 服务名 | +| `data.ip` | `String` | `IP`地址 | +| `data.port` | `int` | 端口号 | +| `data.clusterName` | `String` | 集群名称 | +| `data.weight` | `double` | 实例权重 | +| `data.healthy` | `boolean` | 是否健康 | +| `data.instanceId` | `String` | 实例`id` | +| `data.metadata` | `map` | 实例元数据 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance?namespaceId=public&groupName=&serviceName=test_service&ip=127.0.0.1&port=8080' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "serviceName": "DEFAULT_GROUP@@test_service", + "ip": "127.0.0.1", + "port": 8080, + "clusterName": "DEFAULT", + "weight": 1.0, + "healthy": true, + "instanceId": null, + "metadata": { + "value": "1" + } + } + } + ``` + +### 3.5. 查询指定服务的实例列表 + +#### 接口描述 + +查询指定服务下的实例详情信息列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/instance/list` + +#### 请求头 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------------|----------|----------|----------------------| +| `User-Agent` | `String` | 否 | 用户代理,默认为空 | +| `Client-Version` | `String` | 否 | 客户端版本,默认为空 | + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|----------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `ip` | `String` | 否 | `IP`地址,默认为空,表示不限制`IP`地址 | +| `port` | `int` | 否 | 端口号,默认为`0`,表示不限制端口号 | +| `healthyOnly` | `boolean` | 否 | 是否只获取健康实例,默认为`false` | +| `app` | `String` | 否 | 应用名,默认为空 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|----------------------------------------|------------|-----------| +| `data` | | 指定服务的实例列表 | +| `data.name` | `String` | 分组名@@服务名 | +| `data.groupName` | `String` | 分组名 | +| `data.clusters` | `String` | 集群名 | +| `data.cacheMillis` | `int` | 缓存时间 | +| `data.hosts` | `Object[]` | 实例列表 | +| `data.hosts.ip` | `String` | 实例`IP` | +| `data.hosts.port` | `int` | 实例端口号 | +| `data.hosts.weight` | `double` | 实例权重 | +| `data.hosts.healthy` | `boolean` | 实例是否健康 | +| `data.hosts.enabled` | `boolean` | 实例是否可用 | +| `data.hosts.ephemeral` | `boolean` | 是否为临时实例 | +| `data.hosts.clusterName` | `String` | 实例所在的集群名称 | +| `data.hosts.serviceName` | `String` | 服务名 | +| `data.hosts.metadata` | `map` | 实例元数据 | +| `data.hosts.instanceHeartBeatTimeOut` | `int` | 实例心跳超时时间 | +| `data.hosts.ipDeleteTimeout` | `int` | 实例删除超时时间 | +| `data.hosts.instanceHeartBeatInterval` | `int` | 实例心跳间隔 | +| `data.lastRefTime` | `int` | 上次刷新时间 | +| `data.checksum` | `int` | 校验码 | +| `data.allIPs` | `boolean` | | +| `data.reachProtectionThreshold` | `boolean` | 是否到达保护阈值 | +| `data.valid` | `boolean` | 是否有效 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance/list?serviceName=test_service&ip=127.0.0.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "name": "DEFAULT_GROUP@@test_service", + "groupName": "DEFAULT_GROUP", + "clusters": "", + "cacheMillis": 10000, + "hosts": [ + { + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "enabled": true, + "ephemeral": true, + "clusterName": "DEFAULT", + "serviceName": "DEFAULT_GROUP@@test_service", + "metadata": { + "value": "1" + }, + "instanceHeartBeatTimeOut": 15000, + "ipDeleteTimeout": 30000, + "instanceHeartBeatInterval": 5000 + } + ], + "lastRefTime": 1662554390814, + "checksum": "", + "allIPs": false, + "reachProtectionThreshold": false, + "valid": true + } + } + ``` + +### 3.6. 批量更新实例元数据 + +#### 接口描述 + +批量更新实例的元数据, + +> 对应元数据的键不存在时,则添加对应元数据 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance/metadata/batch` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `consistencyType` | `String` | 否 | 持久化类型,默认为空 | +| `instances` | `JSON格式String` | 否 | 需要更新的实例列表,默认为空 | +| `metadata` | `JSON格式String` | **是** | 实例元数据 | + +#### 参数说明 + +> - `consistencyType`: 实例的持久化类型,当为‘`persist`’,表示对持久化实例的元数据进行更新;否则表示对临时实例的元数据进行更新 +> - `instances`: 待更新的实例列表,`json`数组,通过`ip+port+ephemeral+cluster`定位到某一实例,为空则表示更新指定服务下所有实例的元数据 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.7. 批量删除实例元数据 + +#### 接口描述 + +批量删除实例的元数据, + +> 对应元数据的键不存在时,则不做操作 + +#### 请求方式 + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance/metadata/batch` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `consistencyType` | `String` | 否 | 持久化类型,默认为空 | +| `instances` | `JSON格式String` | 否 | 需要更新的实例列表,默认为空 | +| `metadata` | `JSON格式String` | **是** | 实例元数据 | + +#### 参数说明 + +> - `consistencyType`: 实例的持久化类型,当为‘`persist`’,表示对持久化实例的元数据进行删除;否则表示对临时实例的元数据进行 +> - `instances`: 待更新的实例列表,`json`数组,通过`ip+port+ephemeral+cluster`定位到某一实例,为空则表示更新指定服务下所有实例的元数据 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.8. 创建服务 + +#### 接口描述 + +创建一个服务 + +> 服务已存在时会创建失败 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `metadata` | `JSON格式String` | 否 | 服务元数据,默认为空 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例,默认为`false` | +| `protectThreshold` | `float` | 否 | 保护阈值,默认为`0` | +| `selector` | `JSON格式String` | 否 | 访问策略,默认为空 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ephemeral=true' \ + -d 'metadata={"k1":"v1"}' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.9. 删除服务 + +#### 接口描述 + +删除指定服务 + +> 服务不存在时会报错,且服务还存在实例时会删除失败 + +#### 请求方式 + +`DELETE` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.10. 修改服务 + +#### 接口描述 + +更新指定服务 + +> 服务不存在时会报错 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `metadata` | `JSON格式String` | 否 | 服务元数据,默认为空 | +| `protectThreshold` | `float` | 否 | 保护阈值,默认为`0` | +| `selector` | `JSON格式String` | 否 | 访问策略,默认为空 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'metadata={"k1":"v2"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.11. 查询服务详情 + +#### 接口描述 + +查询某个具体服务的详情信息 + +> 服务不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|---------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|-----------|----------------| +| `data` | | 服务信息 | +| `data.namespace` | `String` | 命名空间 | +| `data.groupName` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.clusterMap` | `map` | 集群信息 | +| `data.metadata` | `map` | 服务元数据 | +| `data.protectThreshold` | `float` | 保护阈值 | +| `data.selector` | `Object` | 访问策略 | +| `data.ephemeral` | `Boolean` | 是否为临时实例 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "public", + "serviceName": "nacos.test.1", + "groupName": "DEFAULT_GROUP", + "clusterMap": {}, + "metadata": {}, + "protectThreshold": 0, + "selector": { + "type": "none", + "contextType": "NONE" + }, + "ephemeral": false + } + } + ``` + +### 3.12. 查询服务列表 + +#### 接口描述 + +查询符合条件的服务列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/service/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|-----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `selector` | `JSON格式String` | **是** | 访问策略 | +| `pageNo` | `int` | 否 | 当前页,默认为`1` | +| `pageSize` | `int` | 否 | 页条目数,默认为`20`,最大为`500` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|------------|------------------| +| `data` | | 服务列表信息 | +| `data.count` | `String` | 服务数目 | +| `data.services` | `String[]` | 分页后的服务列表 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "count": 2, + "services": [ + "nacos.test.1", + "nacos.test.2" + ] + } + } + ``` + +### 3.13. 更新实例健康状态 + +#### 接口描述 + +更新实例的健康状态 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/health/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名,默认为`DEFAULT` | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `healthy` | `boolean` | **是** | 是否健康 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|--------------------| +| `data` | `String` | “`ok`”表示执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ip=127.0.0.1' \ + -d 'port=8080' \ + -d 'healthy=false' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/health/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "ok" + } + ``` + +### 3.14. 查询客户端列表(新) + +#### 接口描述 + +查询当前所有的客户端列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/list` + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------|------------|----------------| +| `data` | `String[]` | 客户端`id`列表 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + "10.128.164.35:9956#true", + "1664358687402_127.0.0.1_2300", + "1664358642902_127.0.0.1_2229", + "192.168.139.1:49825#true", + "10.128.164.35:9954#true", + "192.168.139.1:53556#true" + ] + } + ``` + +> 对于不同版本的nacos client,建立客户端的方式不同。 +> +> 对于`1.x`版本,每个实例会建立两个基于`ip+port`的客户端,分别对应实例注册与服务订阅,`clientId`格式为 `ip:port#ephemeral` +> +> 对于`2.x`版本的`nacos client`, 每个实例会建立一个`RPC`连接,对应一个基于`RPC`连接的客户端,兼具注册与订阅功能,`clientId` +> 格式为`time_ip_port` + + + +### 3.15. 查询客户端信息(新) + +#### 接口描述 + +查询指定客户端的详细信息 + +> 客户端不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|------------------------|-----------|----------------| +| `data` | `Object` | 客户端信息 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ephemeral` | `boolean` | 是否为临时实例 | +| `data.lastUpdatedTime` | `int` | 上次更新时间 | +| `data.clientType` | `String` | 客户端类型 | +| `data.clientIp` | `String` | 客户端`IP` | +| `data.clientPort` | `String` | 客户端端口 | +| `data.connectType` | `String` | 连接类型 | +| `data.appName` | `String` | 应用名 | +| `data.Version` | `String` | 客户端版本 | + +> 只有当`clientType`为`connection`时,会显示`connectType`,`appName`和`appName`字段 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "clientId": "1664527081276_127.0.0.1_4400", + "ephemeral": true, + "lastUpdatedTime": 1664527081642, + "clientType": "connection", + "connectType": "GRPC", + "appName": "-", + "version": "Nacos-Java-Client:v2.1.0", + "clientIp": "10.128.164.35", + "clientPort": "4400" + } + } + ``` + +### 3.16. 查询客户端的注册信息(新) + +#### 接口描述 + +查询指定客户端的注册信息 + +> 客户端不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/publish/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------------------|------------|----------------------| +| `data` | `Object[]` | 客户端注册的服务列表 | +| `data.namespace` | `String` | 命名空间 | +| `data.group` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.registeredInstance` | `Object` | 该服务下注册的实例 | +| `data.registeredInstance.ip` | `String` | `IP`地址 | +| `data.registeredInstance.port` | `int` | 端口号 | +| `data.registeredInstance.cluster` | `String` | 集群名 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/publish/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "registeredInstance": { + "ip": "10.128.164.35", + "port": 9950, + "cluster": "DEFAULT" + } + } + ] + } + ``` + +### 3.17. 查询客户端的订阅信息(新) + +#### 接口描述 + +查询指定客户端的订阅信息 + +> 客户端不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/subscribe/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------------|------------|----------------------| +| `data` | `Object[]` | 客户端订阅的服务列表 | +| `data.namespace` | `String` | 命名空间 | +| `data.group` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.subscriberInfo` | `Object` | 订阅信息 | +| `data.subscriberInfo.app` | `String` | 应用 | +| `data.subscriberInfo.agent` | `String` | 客户端信息 | +| `data.subscriberInfo.addr` | `String` | 地址 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/subscribe/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "subscriberInfo": { + "app": "unknown", + "agent": "Nacos-Java-Client:v2.1.0", + "addr": "10.128.164.35" + } + } + ] + } + ``` + +### 3.18. 查询注册指定服务的客户端信息(新) + +#### 接口描述 + +查询注册指定服务的客户端信息 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/service/publisher/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | +| `ip` | `String` | 否 | `IP`地址,默认为空,不限制`IP`地址 | +| `port` | `int` | 否 | 端口号,默认为空,表示不限制端口号 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|----------|------------| +| `data` | | 客户端列表 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ip` | `String` | 客户端`IP` | +| `data.port` | `int` | 客户端端口 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/publisher/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527081276_127.0.0.1_4400", + "ip": "10.128.164.35", + "port": 9950 + }, + { + "clientId": "10.128.164.35:9954#true", + "ip": "10.128.164.35", + "port": 9954 + } + ] + } + ``` + +### 3.19. 查询订阅指定服务的客户端信息(新) + +#### 接口描述 + +查询订阅指定服务的客户端信息 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/service/subscriber/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|------------------------------------| +| `namespaceId` | String | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | String | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | String | **是** | 服务名 | +| `ephemeral` | boolean | 否 | 是否为临时实例 | +| `ip` | String | 否 | `IP`地址,默认为空,不限制`IP`地址 | +| `port` | int | 否 | 端口号,默认为空,表示不限制端口号 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|----------|------------| +| `data` | | 客户端列表 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ip` | `String` | 客户端`IP` | +| `data.port` | `int` | 客户端端口 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/subscriber/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527125645_127.0.0.1_4443", + "ip": "10.128.164.35", + "port": 0 + }, + { + "clientId": "172.24.144.1:54126#true", + "ip": "172.24.144.1", + "port": 54126 + } + ] + } + ``` + +## 4. 命名空间 + +### 4.1. 查询命名空间列表 + +#### 接口描述 + +查询当前所有的命名空间 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/console/namespace/list` + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------------|------------|----------------------| +| `data` | `Object[]` | 命名空间列表 | +| `data.namespace` | `String` | 命名空间`ID` | +| `data.namespaceShowName` | `String` | 命名空间名称 | +| `data.namespaceDesc` | `String` | 命名空间描述 | +| `data.quota` | `int` | 命名空间的容量 | +| `data.configCount` | `int` | 命名空间下的配置数量 | +| `data.type` | `int` | 命名空间类型 | + +> 命名空间分为3种类型,`0 `- 全局命名空间 `1` - 默认私有命名空间 `2 `- 自定义命名空间 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "", + "namespaceShowName": "public", + "namespaceDesc": null, + "quota": 200, + "configCount": 1, + "type": 0 + } + ] + } + ``` + +### 4.2. 查询具体命名空间 + +#### 接口描述 + +查询具体命名空间的信息 + +> 命名空间不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------------|----------|----------------------| +| `data` | `Object` | 命名空间信息 | +| `data.namespace` | `String` | 命名空间`ID` | +| `data.namespaceShowName` | `String` | 命名空间名称 | +| `data.namespaceDesc` | `String` | 命名空间描述 | +| `data.quota` | `int` | 命名空间的容量 | +| `data.configCount` | `int` | 命名空间下的配置数量 | +| `data.type` | `int` | 命名空间类型 | + +> 命名空间分为3种类型,`0` - 全局命名空间 `1` - 默认私有命名空间 `2` - 自定义命名空间 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace?namespaceId=test_namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "test_namespace", + "namespaceShowName": "test", + "namespaceDesc": null, + "quota": 200, + "configCount": 0, + "type": 2 + } + } + ``` + +### 4.3. 创建命名空间 + +#### 接口描述 + +创建一个命名空间 + +> 命名空间已存在时会报错 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | +| `namespaceName` | `String` | **是** | 命名空间名称 | +| `namespaceDesc` | `String` | 否 | 命名空间描述 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 4.4. 编辑命名空间 + +#### 接口描述 + +编辑命名空间信息 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | +| `namespaceName` | `String` | **是** | 命名空间名称 | +| `namespaceDesc` | `String` | 否 | 命名空间描述 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test.nacos' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 4.5. 删除命名空间 + +#### 接口描述 + +删除指定命名空间 + +#### 请求方式 + +`DELETE` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` diff --git a/src/content/docs/v3.0/en/manual/user/overview/other-language.md b/src/content/docs/v3.0/en/manual/user/overview/other-language.md new file mode 100644 index 00000000000..ad2eb365c5d --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/overview/other-language.md @@ -0,0 +1,22 @@ +--- +title: Nacos SDK OverView +keywords: [其他语言,SDK] +description: 其他语言的SDK +sidebar: + order: 1 +--- + +# Nacos SDK OverView + +Nacos官方目前提供并维护了Java、Golang、Python三个版本的客户端,同时我们将主要依靠社区的贡献来发展多语言客户端。在未来,我们将向Nacos社区用户推荐那些最被广泛使用的以及支持最好的客户端作为Nacos相应语言的官方版本。 + +| Language | code repo | pkg repo | +| ---- | ---- | ---- | +| Java | [https://github.com/alibaba/nacos](https://github.com/alibaba/nacos)| [https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client](https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client) +| Golang | [https://github.com/nacos-group/nacos-sdk-go](https://github.com/nacos-group/nacos-sdk-go) | github.com/nacos-group/nacos-sdk-go/v2| +| Python | [https://github.com/nacos-group/nacos-sdk-python](https://github.com/nacos-group/nacos-sdk-python) |[https://pypi.org/project/nacos-sdk-python/](https://pypi.org/project/nacos-sdk-python/)| +| C++ | [https://github.com/nacos-group/nacos-sdk-cpp](https://github.com/nacos-group/nacos-sdk-cpp)|/| +| NodeJS|[https://github.com/nacos-group/nacos-sdk-nodejs](https://github.com/nacos-group/nacos-sdk-nodejs)|[https://www.npmjs.com/package/nacos](https://www.npmjs.com/package/nacos)| +| C#| [https://github.com/nacos-group/nacos-sdk-csharp](https://github.com/nacos-group/nacos-sdk-csharp)|[https://www.nuget.org/packages/nacos-sdk-csharp](https://www.nuget.org/packages/nacos-sdk-csharp)| +|Rust|[https://github.com/nacos-group/nacos-sdk-rust](https://github.com/nacos-group/nacos-sdk-rust)|[https://crates.io/crates/nacos-sdk/versions](https://crates.io/crates/nacos-sdk/versions) + diff --git a/src/content/docs/v3.0/en/manual/user/parameters-check.md b/src/content/docs/v3.0/en/manual/user/parameters-check.md new file mode 100644 index 00000000000..ff8e25675dc --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/parameters-check.md @@ -0,0 +1,235 @@ +--- +title: 参数校验 +keywords: [参数校验,使用规则] +description: 参数校验 +sidebar: + order: 9 +--- + +# 参数校验 + +2.3.0版本之前的Nacos的参数校验逻辑分散,由各类请求的处理方法单独进行校验,难以更改维护,经常出现参数校验的遗漏,参数校验的规则也没有明确统一;这使得用户使用时经常会因为一些特殊字符导致功能不符合预期或出现漏洞,甚至导致大量推送,导致带宽打满,内存占用过多,导致应用出现故障。 + +在2.3.0之后的版本中,Nacos明确了参数校验规则,在服务端实现了统一的参数校验逻辑并添加了参数校验层,根据校验规则对客户端向服务端发送的请求进行校验。 + +用户可以选择开启参数校验功能,开启后Nacos将会对客户端向服务端发送的请求中的部分参数进行参数校验,确保参数的合法性,避免由于错误使用,导致的不符合预期以及性能问题。 + +## 1. 参数校验开关 + +服务端的参数校验功能**默认开启**,用户可以通过设置`${nacos.home}/conf`目录下的`application.properties`文件中的`nacos.core.param.check.enabled`值选择开启或者关闭服务端参数校验功能。 + +`nacos.core.param.check.enabled=true`时开启Nacos服务端参数校验,`false`关闭服务端参数校验 + +## 2. 参数校验规则 + +开启参数校验后OpenAPI文档 和 SDK文档中的所有接口中的相关参数都会接受格式校验,现对相关参数以及校验规则进行说明: + +|参数描述|最大字符长度|校验规则| +|-----|-----|-----| +|命名空间名称|256|禁止`@#$%^&*`,对应正则表达式:`[^@#$%^&*]+$`| +|命名空间ID|64|只允许字母数字下划线以及"-"字符,对应正则表达式:`^[\w-]+`| +|配置名称|256|只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$`| +|服务名称|512|禁止中文和`@@`且禁止以`@`开头,禁止空白字符,对应正则表达式`^(?!@).((?!@@)[^\u4E00-\u9FA5])*$`| +|分组名称|128|只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$`| +|集群名称|64|只允许数字字母和`-_`,对应正则表达式`^[0-9a-zA-Z-_]+$`| +|IP地址|128|禁止中文字符和空白字符,对应正则表达式为`^[^\u4E00-\u9FA5]*$`| +|端口号|-|取值范围为`0~65535`| +|实例元数据|1024|字段名加字段值的总长度小于1024个字符| + +### 2.1. namespaceShowName + +#### 参数描述 + +命名空间名称 + +#### 校验规则 + +字符长度最大为256,禁止`@#$%^&*`,对应正则表达式:`[^@#$%^&*]+$` + +#### OpenAPI示例 + +- [创建命名空间](./open-api.md#43-创建命名空间) + +#### 校验失败报错信息 + +- 超出长度:`Param 'namespaceShowName' is illegal, the param length should not exceed 256.` +- 非法字符:`Param 'namespaceShowName' is illegal, illegal characters should not appear in the param.` + +### 2.2. namespaceId/tenant/namespace + +#### 参数描述 + +命名空间ID(租户空间) + +#### 校验规则 + +字符长度最大为64,只允许字母数字下划线以及"-"字符,对应正则表达式:`^[\w-]+` + +#### OpenAPI示例 + +- [获取配置](./open-api.md#21-获取配置) +- [注册实例](./open-api.md#31-注册实例) + +#### 校验失败报错信息 + +- 超出长度:`Param 'namespaceId/tenant' is illegal, the param length should not exceed 64.` +- 非法字符:`Param 'namespaceId/tenant' is illegal, illegal characters should not appear in the param.` + +### 2.3. dataId + +#### 参数描述 + +配置名称 + +#### 校验规则 + +字符长度最大为256,只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$` + +#### OpenAPI示例 + +[发布配置](./open-api.md#22-发布配置) + +#### Java SDK示例 + +监听配置:`public void addListener(String dataId, String group, Listener listener) ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'dataId' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'dataId' is illegal, illegal characters should not appear in the param.` + +### 2.4. service/serviceName + +#### 参数描述 + +服务名称 + +#### 校验规则 + +字符长度最大为512,禁止中文和`@@`且禁止以`@`开头,对应正则表达式`^(?!@).((?!@@)[^\u4E00-\u9FA5])*$` + +#### OpenAPI示例 + +[注册实例](./open-api.md#31-注册实例) + +#### Java SDK示例 + +注册实例:`void registerInstance(String serviceName, String ip, int port) throws NacosException; ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'serviceName' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'serviceName' is illegal, illegal characters should not appear in the param.` + +### 2.5. group/groupName + +#### 参数描述 + +分组名称 + +#### 校验规则 + +字符长度最大为128,只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$` + +#### OpenAPI示例 + +[查询实例列表](./open-api.md#35-查询指定服务的实例列表) + +#### Java SDK示例 + +删除配置:`public boolean removeConfig(String dataId, String group) throws NacosException ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'group' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'group' is illegal, illegal characters should not appear in the param.` + +### 2.6. cluster/clusterName + +#### 参数描述 + +集群名称 + +#### 校验规则 + +字符长度最大为64,只允许数字字母和`-_`,对应正则表达式`^[0-9a-zA-Z-_]+$` + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +获取全部实例:`List getAllInstances(String serviceName, List clusters) throws NacosException;` + +#### 校验失败报错信息 + +- 超出长度:`Param 'cluster' is illegal, the param length should not exceed 64.` +- 非法字符:`Param 'cluster' is illegal, illegal characters should not appear in the param.` + +### 2.7. ip + +#### 参数描述 + +IP地址 + +#### 校验规则 + +字符长度最大为128,禁止中文字符,对应正则表达式为`^[^\u4E00-\u9FA5]*$` + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +注销实例:`void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;` + +#### 校验失败报错信息 + +- 超出长度:`Param 'ip' is illegal, the param length should not exceed 128.` +- 非法字符:`Param 'ip' is illegal, illegal characters should not appear in the param.` + +### 2.8. port + +#### 参数描述 + +端口号 + +#### 校验规则 + +取值范围为0~65535 + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +注销实例:`void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;` + +#### 校验失败报错信息 + +端口取值超出范围:`Param 'port' is illegal, the value should be between 0 and 65535` + +### 2.9. metadata + +#### 参数描述 + +实例元数据 + +#### 校验规则 + +字段名加字段值的总长度小于1024个字符 + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +注册实例:`void registerInstance(String serviceName, Instance instance) throws NacosException;` + +#### 校验失败报错信息 + +实例总长度超出范围:`Param 'Metadata' is illegal, the param length should not exceed %d.` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/manual/user/python-sdk/usage.md b/src/content/docs/v3.0/en/manual/user/python-sdk/usage.md new file mode 100644 index 00000000000..8e3440a7658 --- /dev/null +++ b/src/content/docs/v3.0/en/manual/user/python-sdk/usage.md @@ -0,0 +1,239 @@ +--- +title: Python SDK Usage +keywords: [Python,SDK,Usage] +description: This document introduces the usage of Nacos's Python SDK (nacos-sdk-python) +sidebar: + order: 6 +--- + +# Python SDK Usage + + +### Supported Python version: + +- Python 2.7 +- Python 3.6+ + +### Supported Nacos version +- Nacos 0.8.0+ +- Nacos 1.x +- Nacos 2.x with http protocol + + +## Installation +```shell +pip install nacos-sdk-python +``` + +## Getting Started +```python +import nacos + +# Both HTTP/HTTPS protocols are supported, if not set protocol prefix default is HTTP, and HTTPS with no ssl check(verify=False) +# "192.168.3.4:8848" or "https://192.168.3.4:443" or "http://192.168.3.4:8848,192.168.3.5:8848" or "https://192.168.3.4:443,https://192.168.3.5:443" +SERVER_ADDRESSES = "server addresses split by comma" +NAMESPACE = "namespace id" + +# no auth mode +client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE) +# auth mode +#client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE, ak="{ak}", sk="{sk}") + +# get config +data_id = "config.nacos" +group = "group" +print(client.get_config(data_id, group)) +``` + +## Configuration +``` +client = NacosClient(server_addresses, namespace=your_ns, ak=your_ak, sk=your_sk) +``` + +* *server_addresses* - **required** - Nacos server address, comma separated if more than 1. +* *namespace* - Namespace. | default: `None` +* *ak* - The accessKey to authenticate. | default: null +* *sk* - The secretKey to authentication. | default: null +* *log_level* - Log level. | default: null +* *log_rotation_backup_count* - The number of log files to keep. | default: `7` + +#### Extra Options +Extra option can be set by `set_options`, as following: + +``` +client.set_options({key}={value}) +# client.set_options(proxies={"http":"192.168.3.50:809"}) +``` + +Configurable options are: + +* *default_timeout* - Default timeout for get config from server in seconds. +* *pulling_timeout* - Long polling timeout in seconds. +* *pulling_config_size* - Max config items number listened by one polling process. +* *callback_thread_num* - Concurrency for invoking callback. +* *failover_base* - Dir to store failover config files. +* *snapshot_base* - Dir to store snapshot config files. +* *no_snapshot* - To disable default snapshot behavior, this can be overridden by param *no_snapshot* in *get* method. +* *proxies* - Dict proxy mapping, some environments require proxy access, so you can set this parameter, this way http requests go through the proxy. + +## API Reference + +### Get Config +>`NacosClient.get_config(data_id, group, timeout, no_snapshot)` + +* `param` *data_id* Data id. +* `param` *group* Group, use `DEFAULT_GROUP` if no group specified. +* `param` *timeout* Timeout for requesting server in seconds. +* `param` *no_snapshot* Whether to use local snapshot while server is unavailable. +* `return` +W +Get value of one config item following priority: + +* Step 1 - Get from local failover dir(default: `${cwd}/nacos-data/data`). + * Failover dir can be manually copied from snapshot dir(default: `${cwd}/nacos-data/snapshot`) in advance. + * This helps to suppress the effect of known server failure. + +* Step 2 - Get from one server until value is got or all servers tried. + * Content will be save to snapshot dir after got from server. + +* Step 3 - Get from snapshot dir. + +### Add Watchers +>`NacosClient.add_config_watchers(data_id, group, cb_list)` + +* `param` *data_id* Data id. +* `param` *group* Group, use `DEFAULT_GROUP` if no group specified. +* `param` *cb_list* List of callback functions to add. +* `return` + +Add watchers to a specified config item. +* Once changes or deletion of the item happened, callback functions will be invoked. +* If the item is already exists in server, callback functions will be invoked for once. +* Multiple callbacks on one item is allowed and all callback functions are invoked concurrently by `threading.Thread`. +* Callback functions are invoked from current process. + +### Remove Watcher +>`NacosClient.remove_config_watcher(data_id, group, cb, remove_all)` + +* `param` *data_id* Data id. +* `param` *group* Group, use "DEFAULT_GROUP" if no group specified. +* `param` *cb* Callback function to delete. +* `param` *remove_all* Whether to remove all occurrence of the callback or just once. +* `return` + +Remove watcher from specified key. + +### Publish Config +>`NacosClient.publish_config(data_id, group, content, timeout)` + +* `param` *data_id* Data id. +* `param` *group* Group, use "DEFAULT_GROUP" if no group specified. +* `param` *content* Config value. +* `param` *timeout* Timeout for requesting server in seconds. +* `return` True if success or an exception will be raised. + +Publish one data item to Nacos. +* If the data key is not exist, create one first. +* If the data key is exist, update to the content specified. +* Content can not be set to None, if there is need to delete config item, use function **remove** instead. + +### Remove Config +>`NacosClient.remove_config(data_id, group, timeout)` +* `param` *data_id* Data id. +* `param` *group* Group, use "DEFAULT_GROUP" if no group specified. +* `param` *timeout* Timeout for requesting server in seconds. +* `return` True if success or an exception will be raised. + +Remove one data item from Nacos. + +### Register Instance +>`NacosClient.add_naming_instance(service_name, ip, port, cluster_name, weight, metadata, enable, healthy,ephemeral,group_name,heartbeat_interval)` +* `param` *service_name* **required** Service name to register to. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster to register to. +* `param` *weight* A float number for load balancing weight. +* `param` *metadata* Extra info in JSON string format or dict format +* `param` *enable* A bool value to determine whether instance is enabled or not. +* `param` *healthy* A bool value to determine whether instance is healthy or not. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `param` *heartbeat_interval* Auto daemon heartbeat interval in seconds. +* `return` True if success or an exception will be raised. + +### Deregister Instance +>`NacosClient.remove_naming_instance(service_name, ip, port, cluster_name)` +* `param` *service_name* **required** Service name to deregister from. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster to deregister from. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `return` True if success or an exception will be raised. + +### Modify Instance +>`NacosClient.modify_naming_instance(service_name, ip, port, cluster_name, weight, metadata, enable)` +* `param` *service_name* **required** Service name. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster name. +* `param` *weight* A float number for load balancing weight. +* `param` *metadata* Extra info in JSON string format or dict format. +* `param` *enable* A bool value to determine whether instance is enabled or not. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `return` True if success or an exception will be raised. + +### Query Instances +>`NacosClient.list_naming_instance(service_name, clusters, namespace_id, group_name, healthy_only)` +* `param` *service_name* **required** Service name to query. +* `param` *clusters* Cluster names separated by comma. +* `param` *namespace_id* Customized group name, default `blank`. +* `param` *group_name* Customized group name , default `DEFAULT_GROUP`. +* `param` *healthy_only* A bool value for querying healthy instances or not. +* `return` Instance info list if success or an exception will be raised. + +### Query Instance Detail +>`NacosClient.get_naming_instance(service_name, ip, port, cluster_name)` +* `param` *service_name* **required** Service name. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster name. +* `return` Instance info if success or an exception will be raised. + +### Send Instance Beat +>`NacosClient.send_heartbeat(service_name, ip, port, cluster_name, weight, metadata)` +* `param` *service_name* **required** Service name. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster to register to. +* `param` *weight* A float number for load balancing weight. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `param` *metadata* Extra info in JSON string format or dict format. +* `return` A JSON object include server recommended beat interval if success or an exception will be raised. + +### Subscribe Service Instances Changed +>`NacosClient.subscribe(listener_fn, listener_interval=7, *args, **kwargs)` +* `param` *listener_fn* **required** Customized listener function. +* `param` *listener_interval* Listen interval , default 7 second. +* `param` *service_name* **required** Service name which subscribes. +* `param` *clusters* Cluster names separated by comma. +* `param` *namespace_id* Customized group name, default `blank`. +* `param` *group_name* Customized group name , default `DEFAULT_GROUP`. +* `param` *healthy_only* A bool value for querying healthy instances or not. +* `return` + +### Unsubscribe Service Instances Changed +>`NacosClient.unsubscribe(service_name, listener_name)` +* `param` *service_name* **required** Service name to subscribed. +* `param` *listener_name* listener_name which is customized. +* `return` + +### Stop All Service Subscribe +>`NacosClient.stop_subscribe()` +* `return` + +## Debugging Mode +Debugging mode if useful for getting more detailed log on console. + +Debugging mode can be set by: +``` +client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE, username=USERNAME, password=PASSWORD,log_level="DEBUG") +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/overview.md b/src/content/docs/v3.0/en/overview.md new file mode 100644 index 00000000000..3bf10392def --- /dev/null +++ b/src/content/docs/v3.0/en/overview.md @@ -0,0 +1,138 @@ +--- +title: Nacos 概览 +keywords: [nacos] +description: 什么是 Nacos? +--- + +# Nacos 概览 + +欢迎来到 Nacos 的世界! + +## 什么是Nacos + +Nacos `/nɑ:kəʊs/` 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 + +Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 + +Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以**“服务”**为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。 + +Nacos 支持几乎所有主流类型的**“服务”**的发现、配置和管理: + +- [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) +- [gRPC](https://grpc.io/docs/guides/concepts.html#service-definition) +- [Dubbo RPC Service](https://dubbo.apache.org) +- [Spring Cloud RESTful Service](https://spring.io/projects/spring-cloud) + +### 产品功能 + +* **服务发现和服务健康监测** + + Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 [原生SDK](./guide/user/sdk.md)、[OpenAPI](./guide/user/open-api.md)、或一个[独立的Agent](./guide/user/other-language.md)注册 Service 后,服务消费者可以使用[DNS TODO](./ecology/use-nacos-with-coredns.md) 或[HTTP&API](./guide/user/open-api.md)查找和发现服务。 + + Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。 + +* **动态配置服务** + + 动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。 + + 动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。 + + 配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。 + + Nacos 提供了一个简洁易用的UI ([控制台样例 Demo](http://console.nacos.io/nacos/index.html)) 帮助您管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。 + +* **动态 DNS 服务** + + 动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。 + + Nacos 提供了一些简单的 [DNS APIs TODO](./ecology/use-nacos-with-coredns.md) 帮助您管理服务的关联域名和可用的 IP:PORT 列表. + +* **服务及其元数据管理** + + Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。 + +### 产品优势 + +- **易于使用** + + Nacos经历几万人使用反馈优化,提供统一的服务发现和配置管理功能,通过直观的 Web 界面和简洁的 API,为开发和运维人员在云原生环境中带来了便捷的服务注册、发现和配置更新操作。 + +- **特性丰富** + + Nacos提供了包括服务发现、配置管理、动态 DNS 服务、服务元数据管理、流量管理、服务监控、服务治理等在内的一系列特性,帮助您在云原生时代,更轻松的构建、交付和管理微服务。 + +- **极致性能** + + Nacos经过阿里双十一超快伸缩场景的锤炼,提供高性能的服务注册和发现能力,以及低延迟的配置更新响应,确保在大规模分布式系统中的高效率和稳定运行。 + +- **超大容量** + + Nacos诞生自阿里的百万实例规模,造就支持海量服务和配置的管理,能够满足大型分布式系统对高并发和高可用性的需求。 + +- **稳定可用** + + Nacos 通过自研的同步协议,配合生态中应用广泛的Raft协议,确保了服务的高可用性和数据的稳定性,保证阿里双十一系统的高可用稳定运行。 + +- **开放生态** + + Nacos拥有活跃的开源社区、广泛的生态整合和持续的创新发展,不仅大量兼容了Spring Cloud、Dubbo等大受欢迎的开源框架、还提供了丰富的插件化能力,帮助用户在云原生时代,提供可定制满足自身特殊需求的独有云原生微服务系统。 + +## 设计理念 + +> 我们相信一切都是服务,每个服务节点被构想为一个星球,每个服务都是一个星系。Nacos 致力于帮助建立这些服务之间的**连接**,助力每个面向星辰的梦想能够透过云层,飞在云上,更好的链接整片星空。 + +Nacos希望帮助用户在云原生时代,在私有云、混合云或者公有云等所有云环境中,更好的构建、交付、管理自己的微服务平台,更快的复用和组合业务服务,更快的交付商业创新的价值,从而为用户赢得市场。正是基于这一愿景,Nacos的设计理念被定位为`易于使用`、`面向标准`、`高可用`和`方便扩展`。 + +![设计理念简图](/img/doc/overview/design-philosophy.svg) + +#### 易于使用 + +易于使用是 Nacos 的一个核心理念,它通过提供用户友好的 Web 界面和简洁的 API 来简化服务的注册、发现和配置管理过程。开发者可以轻松集成 Nacos 到他们的应用中,无需投入大量时间在复杂的设置和学习上。 + +#### 面向标准 + +Nacos 采用面向标准的设计理念,遵循云原生应用开发的最佳实践和标准协议,以确保其服务发现和配置管理功能与广泛的技术栈和平台无缝对接。 + +#### 高可用 + +为了满足企业级应用对高可用的需求,Nacos 实现了集群模式,确保在节点发生故障时,服务的发现和配置管理功能不会受影响。集群模式也意味着 Nacos 可以通过增加节点来水平扩展,提升系统的整体性能和承载能力。 + +![Nacos高可用架构图](/img/doc/overview/availability-structure.svg) + +#### 方便扩展 + +Nacos 还注重易于扩展,它采用了模块化的设计使得各个组件都可以独立地进行扩展或替换。这也为社区贡献者提供了方便,使他们能够针对特定的需求开发新的功能或者改善现有功能,进一步推动 Nacos 的生态发展。 + +![Nacos插件架构图](/img/doc/overview/plugin-structure.svg) + +通过上述设计理念的实现,Nacos 为用户提供了一个强大而灵活的平台,以支持不断变化的业务需求,加速业务创新和数字转型,最终帮助用户在竞争激烈的市场中占据有利地位。 + +## 部署模式 + +Nacos 提供了两种两种部署运行模式:`单机模式`和`集群模式` + +![Nacos部署模式图](/img/doc/overview/deploy-structure.svg) + +### 单机模式 + +单机模式又称`单例模式`, 拥有所有Nacos的功能及特性,具有极易部署、快速启动等优点。但无法与其他节点组成集群,无法在节点或网络故障时提供高可用能力。单机模式同样可以使用内置Derby数据库(默认)和外置数据库进行存储。 + +单机模式主要适合于工程师于本地搭建或于测试环境中搭建Nacos环境,主要用于开发调试及测试使用;也能够兼顾部分对稳定性和可用性要求不高的业务场景。 + +### 集群模式 + +集群模式通过自研一致性协议Distro以及Raft协议,将多个Nacos节点构建成了高可用的Nacos集群。数据将在集群中各个节点进行同步,保证数据的一致性。集群模式具有高可用、高扩展、高并发等优点,确保在故障发生时不影响业务的运行。集群模式**默认**采用外置数据库进行存储,但也可以通过内置数据库进行存储。 + +该模式主要适合于生产环境,也是社区最为推荐的部署模式。 + +## 生态组件 + +![Nacos生态图](/img/doc/overview/ecology-structure.png) + +## 路线规划 + +![NacosRoadMap](/img/doc/overview/roadmap.svg) + +## 参与社区 + +Nacos 主要通过Github 进行社区的协作,欢迎社区中所有的用户和开发者加入到Nacos的开发中来。详情请参考[如何共建](contribution/contributing.md)。 \ No newline at end of file diff --git a/src/content/docs/v3.0/en/plugin/address-plugin.md b/src/content/docs/v3.0/en/plugin/address-plugin.md new file mode 100644 index 00000000000..47db604d127 --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/address-plugin.md @@ -0,0 +1,96 @@ +--- +title: Addressing Plugin +keywords: [Addressing, Plugin] +description: This article describes how to develop and use Nacos' addressing plugin. +sidebar: + order: 2 + hidden: true +--- + +# Addressing Plugin + +Since version 2.3.0, Nacos support to inject addressing plugins through [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html), and select a plugin implementation in the configuration file `application.properties ` as the actual addressing service. This document will describe how to implement an addressing plugin and how to make it work. + +> Attention: +> At present, the addressing plugin is still in the beta stage, and its API and interface definitions maybe modified with version upgrades. Please pay attention to the applicable version of your plugin. + +## Overview Of Addressing Plugin + +At present, there are three addressing modes for Nacos cluster addressing: stand-alone addressing, profile addressing and address server addressing. Through the addressing plugin, users can write their own addressing logic. + +## Develop Nacos Server Addressing Plugin + +To develop a Nacos server-side addressing plugin, developer first need to depend on the relevant API of the address plugin. + +```xml + + com.alibaba.nacos + nacos-address-plugin + ${project.version} + +``` + +`${project.version}` is the version of Nacos for your development plugin. + +Then implement interface`com.alibaba.nacos.plugin.address.spi.AddressPlugin`, and put your implementation into services of SPI. + +The methods of interface in following: + +|method name|parameters|returns|description| +|-----|-----|-----|---| +|start|void|String|Start the addressing function of the plugin.| +|getServerList|void|List<String>|Returns the addresses of all Nacos cluster nodes. The address format is`IP: Port`.| +|getPluginName|void|String|The name of the plugin. When the name is the same, the plugin loaded later will overwrite the plugin loaded first.| +|registerListener|Consumer<List<String>>|AddressPlugin|Register the listener and call the listener when the cluster address changes| +|shutdown|void|void|Shutdown plugin| + +This interface is defined by `com.alibaba.nacos.plugin.address.spi.AbstractAddressPlugin`.The abstract class implements`getServerList`, `registerListener` and `shutdown` methods by default, Users can inherit AbstractAddressPlugin to implement other methods when actually writing plugins. AbstractAddressPlugin has a List<String>member variable named serverList, that is, the cluster address collection. The user needs to maintain this variable. +When users need to configure plugin related parameters in the configuration file, they need to configure keys starting with `address.plugin` in the property configuration file. In this case, the corresponding parameters can be obtained through the `com.alibaba.nacos.plugin.address.common.AddressProperties` singleton class +```properties +address.plugin.$ {key} = ${val} +``` +After configuration, users can write plugins through the +```java +AddressProperties.getProperty(${key}) +``` +To get the parameters. + +### Use Server Plugin + +After the plugin finished, it needs to be packaged into jar/zip and places in the classpath of the nacos server. If you don't know how to add plugins into the classpath, please place plugins under `${nacos-server.path}/plugins` directly. + +After Adding plugins into classpath, also need to modify some configuration in `${nacos-server.path}/conf/application.properties`. + +```properties +### The plugin name nacos using,should be same as the return value of `com.alibaba.nacos.plugin.address.spi.AddressPlugin#getPluginName` +nacos.core.member.lookup.type=${addressPluginName} +``` + +Restart nacos cluster, and after any request, some logs can be saw in `${nacos-server.path}/logs/nacos-cluster.log`: + +```text +[AddressPluginManager] Load AddressPlugin(xxxx) PluginName(xxx) successfully. +``` + +### Use the default Nacos addressing plugin + +In order to be compatible with the addressing of the old version, when the user does not use the custom plug-in, the configuration is the same as the original, or the configuration item `nacos.core.member.lookup.type=[file, address server]`. + +## Client Plugin + +### Use Custom Plugins +The implementation of custom plugins is the same as that of the server. When users need to use custom plugins, they inherit `com.alibaba.nacos.plugin.address.spi.AbstractAddressPlugin` or implement `com.alibaba.nacos.plugin.address.spi.AddressPlugin`, package the developed client plug-in into jar/zip, and put it into your application's classpath to automatically take effect. When initializing `NacosConfigService` or `NacosNamingService`, the key passed in the `Properties` object is `addressPluginName`, and val is the parameter returned by the plugin `getPluginName`. +for example: +```java + Properties properties = new Properties(); + properties.put("addressPluginName", ${addressPluginName}); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfig(dataId, group, 5000); +``` + +### Use the default Nacos addressing plugin +The Java client plug-in of Nacos is adapted to the old version. If the customized plug-in is not applicable, the use of the client is the same as before. + +### Plugin for other programming language + +TODO diff --git a/src/content/docs/v3.0/en/plugin/auth-plugin.md b/src/content/docs/v3.0/en/plugin/auth-plugin.md new file mode 100644 index 00000000000..e44f7bdaef2 --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/auth-plugin.md @@ -0,0 +1,185 @@ +--- +title: Authorization Plugin +keywords: [Authorization, Plugin] +description: This article describes how to develop and use Nacos' authentication plugin. +sidebar: + order: 1 +--- + +# Authorization Plugin + +Since version 2.1.0, Nacos support to inject authentication plugins through [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html), and select a plugin implementation in the configuration file `application.properties ` as the actual authentication service. This document will describe how to implement an authentication plugin and how to make it work. + +> Attention: +> At present, the authentication plugin is still in the beta stage, and its API and interface definitions maybe modified with version upgrades. Please pay attention to the applicable version of your plugin. + +## Concepts in Authentication Plugins + +Authentication, the common expression is to verify whether **who** can perform **some operation** on **something**. So when Nacos designs the authentication plugin, the authentication information abstracted as three main concepts: `identity context`, `resource` and `action type`. + +### IdentityContext + +IdentityContext is the abstraction of the request originator in the Nacos authentication plugin. Due to different plugin implementations, the identity context may be different, for example, username and password are one type of identity information, and accessToken is another type of identity information. Therefore, the IdentityContext does not limit the specific size and key. The plugin implementation can customize any size and keywords. Nacos will automatically obtain the identity keywords defined by the plugin implementation and their corresponding value from the request and inject them into IdentityContext which will be used in plugins. + +IdentityContext must include: + +|Field Name|Description| +|-----|---| +|remote_ip|source ip of request| + +### Resource + +Resource is the abstraction of the object operated by the request in the Nacos authentication plugin. It is mainly defined by Nacos, which can be a configuration, a service, or a group. + +Resource mainly consists of the following: + +|Field Name|Description| +|-----|---| +|namespaceId|Namespace ID of the requested resource, some interfaces may not have this value| +|group| The group name of the requested resource, some interfaces may not have this value| +|name | The resource name of the requested resource, such as the service name or the configuration dataId, some interfaces may be defined special values, such as `nacos/admin`| +|type | The type of the requested resource, which may be an enumeration value in `SignType`, which mainly represents the module related to the resource | +|properties| The extended configuration of the requested resource, which does not belong to the above-mentioned resource-related information, will be placed in properties, such as the Request name of the Grpc request or the tags on the `@Secured` annotation, etc. | + +### Action + +Action is the abstraction of the request operation in the Nacos authentication plugin, mainly include the read operation `R` and write operation `W`. For details, see the `ActionTypes` enumeration. + +## Server Plugin + +To develop a Nacos server-side authentication plugin, developer first need to depend on the relevant API of the authentication plugin. + +```xml + + com.alibaba.nacos + nacos-auth-plugin + ${project.version} + +``` + +`${project.version}` is the version of Nacos for your development plugin. + +Then implement interface `com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService`, and put your implementation into services of SPI. + +The methods of interface in following: + +|method name|parameters|returns|description| +|-----|-----|-----|---| +|getAuthServiceName|void|String|The name of the plugin. When the name is the same, the plugin loaded later will overwrite the plugin loaded first.| +|identityNames|void|Collection<String>|The identity context keywords of the plugin. Nacos will obtain the parameters with these keywords as the key from the request and inject them into the IdentityContext.| +|enableAuth|ActionTypes,SignType|boolean|Called before `validateIdentity` and `validateAuthority`, the plugin can decide whether to authenticate this type of operation or this type of module.| +|validateIdentity|IdentityContext, Resource|boolean|Validate identity, called before `validateAuthority`| +|validateAuthority|IdentityContext, Permission|boolean|Validate permissions, called when `validateIdentity` returns `true`| + +### Load Server Plugin + +After the plugin finished, it needs to be packaged into jar/zip and places in the classpath of the nacos server. If you don't know how to add plugins into the classpath, please place plugins under `${nacos-server.path}/plugins` directly. + +After Adding plugins into classpath, also need to modify some configuration in `${nacos-server.path}/conf/application.properties`. + +```properties +### The plugin name nacos using,should be same as the return value of `com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService#getAuthServiceName` +nacos.core.auth.system.type=${authServiceName} + +### open authorization +nacos.core.auth.enabled=true +``` + +Restart nacos cluster, and after any request, some logs can be saw in `${nacos-server.path}/logs/core-auth.log`: + +```text +[AuthPluginManager] Load AuthPluginService(xxxx) AuthServiceName(xxx) successfully. +``` + +### Use the default Nacos authentication plugin + +Nacos provides a simple authentication plugin. It is a weak authentication system to prevent business misuse, not a strong authentication system to prevent malicious attacks. The usage detail see [Admin Manual-Authentication](../manual/admin/auth.mdx). + +## Client Plugin + +The authentication plugin for Nacos Client is to inject authentication-related identity context into the request so that each request can be recognized by the server authentication plugin. + +The Java client of Nacos comes with two implementations by default: + +- A default implementation using `username`,`password` and `accessToken`; +- An Aliyun implementation using `accessKey` and `secretKey`. + +### Default implementation + +When `username`, `password` are included in the properties passed into a nacos client instance, the nacos client will use the simple authentication plugin to inject identity context; +e.g.: +```java +Properties properties = new Properties(); +properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); +properties.setProperty(PropertyKeyConst.USERNAME, "nacos"); +properties.setProperty(PropertyKeyConst.PASSWORD, "nacos"); +NamingFactory.createNamingService(properties); +ConfigFactory.createConfigService(properties); +``` + +The plugin will login through `username` and `password` asynchronously, and obtain the `accessToken` after the login is successful. Finally, the plugin will inject the `accessToken` into all requests, which make the server plugins can validate identity and permission according to `accessToken`. + +### Aliyun implementation + +When `accessKey` and `secretKey` are included in the properties, the nacos client will use the aliyun authentication plugin to inject identity context. + +e.g.: + ```java + Properties properties = new Properties(); + properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); + properties.setProperty(PropertyKeyConst.ACCESS_KEY, "nacos"); + properties.setProperty(PropertyKeyConst.SECRET_KEY, "nacos"); + NamingFactory.createNamingService(properties); + ConfigFactory.createConfigService(properties); + ``` + +The plugin will generate signatures by `accessKey`, `secretKey` and the request resource, and inject into the request. + +The identity context may be different for the different request resource: + +|Type|Identity keys|description| +|-----|-----|-----| +|NamingService|ak|accessKey| +|NamingService|signature|naming signature| +|NamingService|data|signature datum, include timestamp| +|ConfigService|Spas-AccessKey|accessKey| +|ConfigService|Spas-Signature|config signature| +|ConfigService|Timestamp|request timestamp| +|ConfigService|Spas-SecurityToken|Temporary token (used when Alibaba Cloud STS function is enabled)| + +Developers can validate authentication and authorization in the server plugin based on the above information. + +### Custom Plugin + +Considering that the developer's authentication plugin may have custom identity keywords, the Java client of Nacos can also use the SPI to inject the plugin implementation. + +To develop a Nacos client authentication plugin, developers first need to depend on the relevant API of the authentication plugin. + +```xml + + com.alibaba.nacos + nacos-auth-plugin + ${project.version} + +``` + +`${project.version}` is the version of Nacos for your development plugin. + +Then implement interface `com.alibaba.nacos.plugin.auth.spi.client.ClientAuthService`, and put your implementation into services of SPI. + +The methods of interface in following: + +|method name|parameters|returns|description| +|-----|-----|-----|---| +|setServerList|List<String>,Nacos server address list|void|Called during initialization, to inject the Nacos service list, which is convenient for plugins to access the nacos server, such as calling the login interface, etc.| +|setNacosRestTemplate|NacosRestTemplate,http client for Nacos|void|Called during initialization, to inject Nacos' http client, which is convenient for plugins to access the nacos server, such as calling the login interface, etc.| +|login|Properties,properties of initialization|boolean|mainly performs the conversion of identity context, such as `username`, `password` is converted to `accessToken`| +|getLoginIdentityContext|Resource|IdentityContext|Get the identity context converted by the login interface, and the client will inject all the content of the returned object into the request| + +Developers can choose to inherit `com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService`, which implements `setServerList` and `setNacosRestTemplate`. + +Then package the developed client plugin into jar/zip and put it into the classpath of your application and take effect automatically. + +### Plugin for other programming language + +TODO diff --git a/src/content/docs/v3.0/en/plugin/config-change-plugin.md b/src/content/docs/v3.0/en/plugin/config-change-plugin.md new file mode 100644 index 00000000000..18452f0c615 --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/config-change-plugin.md @@ -0,0 +1,141 @@ +--- +title: Config Change Plugin +keywords: [Config Change,Config audit,Config format check,webhook] +description: Nacos support Config Change Plugin. +sidebar: + order: 8 +--- + +> Translated by AI. +# Config Change Plugin + +Community has long been hoping for Nacos Configuration Center to provide notifications to specific systems when configurations change. These notifications are used for recording, warning, and auditing purposes. Before version 2.3.0, the only way to achieve this was by simulating Nacos client subscription to the configurations. This approach involved subscribing to changes in core configurations and executing functionalities such as sending records and warnings upon receiving change notifications. + +However, this implementation had a few significant issues. Firstly, individual configurations needed to be added one by one, making it difficult to capture all configuration changes. Secondly, functionalities could only be executed after configuration changes, and there was no capability for performing pre-change operations such as format validation or whitelist validation. + +To address these limitations, starting from Nacos version 2.3.0, Nacos introduced support for injecting configuration change plugins through the [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html). This allows users to define custom plugins to execute specific logic before and after configuration changes. Examples of such logic include format validation, whitelist validation, and webhook integrations. + +These enhancements provide more flexibility and control for users to implement their own custom logic before and after configuration changes in Nacos Configuration Center. + +## Concepts in Config Change Plugin + +Nacos's configuration change plugin design takes inspiration from the aspect-oriented programming (AOP) paradigm. It treats configuration change operations, such as adding, updating, and deleting, as the `pointcuts` and weaves the plugins `before` and `after` these pointcuts. + +### ConfigChangePointCutTypes + +Nacos has categorized the configuration change operations based on their behaviors and sources. These configuration change operations are defined as several `ConfigChangePointCutTypes` in `com.alibaba.nacos.plugin.config.constants.ConfigChangePointCutTypes`. The specific details are as follows: + +|PointCut Name|Description|Start version| +|-----|-----|-----| +|PUBLISH_BY_HTTP|Configuration is published through the HTTP interface, including creating and modifying configurations.|2.3.0| +|PUBLISH_BY_RPC|Configuration is published through the gRPC interface, including creating and modifying configurations.|2.3.0| +|REMOVE_BY_HTTP|Configuration is removed through the HTTP interface.|2.3.0| +|REMOVE_BY_RPC|Configuration is removed through the gRPC interface.|2.3.0| +|IMPORT_BY_HTTP|Configuration is imported through the HTTP interface.|2.3.0| +|REMOVE_BATCH_HTTP|Configurations are batch removed through the HTTP interface.|2.3.0| + +### ConfigChangeExecuteTypes + +In Nacos, the configuration change plugins need to be executed before or after the `ConfigChangePointCutTypes` by selecting the `ConfigChangeExecuteTypes`. These execution types are defined in `com.alibaba.nacos.plugin.config.constants.ConfigChangeExecuteTypes`. The specific details are as follows: + +|Execute Type|Description|Start version| +|-----|-----|-----| +|EXECUTE_BEFORE_TYPE|Plugin execute **Before** `ConfigChangePointCutTypes`|2.3.0| +|EXECUTE_AFTER_TYPE|Plugin execute **After** `ConfigChangePointCutTypes`|2.3.0| + +## Plugin Development + +To develop a config change plugin for the Nacos server, you first need to depend on the relevant API of the config change plugin. + +```xml + + com.alibaba.nacos + nacos-config-plugin + ${project.version} + +``` + +`${project.version}` is the Nacos version, at least `2.3.0`. + +Then implement `com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService` interface, which include following method: + +|Method name|Parameters|Return|Description| +|-----|-----|-----|-----| +|getServiceType|void|String|Returns the name of the plugin, which is used to differentiate between different types of plugin implementations.| +|getOrder|void|int|Returns the execution order of the plugin. The configuration change plugin is designed with a chain plugin pattern, where multiple plugins are executed in order. The smaller the return value of getOrder, the earlier the plugin is executed.| +|executeType|void|ConfigChangeExecuteTypes|Returns `ConfigChangeExecuteTypes` implemented by the plugin.| +|pointcutMethodNames|void|ConfigChangePointCutTypes[]|Returns `ConfigChangePointCutTypes` where the plugin is implemented.| +|execute|ConfigChangeRequest,ConfigChangeResponse|void|Executes the actual logic of the plugin.| + +`ConfigChangeRequest` and `ConfigChangeResponse` refer to the contents passed in during the execution of logic and the resulting execution outcome, respectively. + +`ConfigChangeRequest` mainly include follow contents: + +|Field name|Type|Description| +|-----|-----|-----| +|requestType|ConfigChangePointCutTypes|The pointcut types of this configuration change| +|requestArgs|HashMap|The actual parameters of this configuration change, mainly including `namespace`, `group`, `dataId`, `content`, etc., with different parameters for different pointcut types| + +`ConfigChangeResponse` mainly include follow contents: + +|Field name|Type|Description| +|-----|-----|-----| +|responseType|ConfigChangePointCutTypes|The pointcut types of this configuration change| +|isSuccess|boolean|Indicates whether the execution is successful. When the return value is `false`, the configuration change will be intercepted and the failure result will be returned directly| +|retVal|Object|Return content, a reserved field that is currently not used| +|msg|String|Execution result information, obtained when `isSuccess` is `false`, used to return information to the client| +|args|Object[]|The execution parameters of the configuration change, effective for the `EXECUTE_BEFORE_TYPE` plugin type. It can be used to modify the content of the actual executed configuration change, such as changing certain values in content to other values| + +### Load Plugin + +After the plugin finished, it needs to be packaged into jar/zip and places in the classpath of the nacos server. If you don't know how to add plugins into the classpath, please place plugins under `${nacos-server.path}/plugins` directly. + +After Adding plugins into classpath, also need to modify some configuration in `${nacos-server.path}/conf/application.properties`. + +```properties +### The name of the config change plugin enabled in Nacos, corresponding to the return value of com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService's getServiceType method. +nacos.core.config.plugin.${configChangePluginName}.enabled=true +``` + +After restarting the Nacos cluster and completing the startup, you can see the following logs in `${nacos-server.path}/logs/plugin-control.log`: + +```text +[ConfigChangePluginManager] Load ${className}(${classFullName}) ConfigChangeServiceName(${configChangePluginName}) successfully. +``` + +### Custom Plugin properties + +Some plugins may want to set certain parameters through a configuration file. Custom plugins can modify the following configurations in `${nacos-server.path}/conf/application.properties` to achieve this: + +```properties +### The name of the config change plugin enabled in Nacos, corresponding to the return value of com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService's getServiceType method. +nacos.core.config.plugin.${configChangePluginName}.${propertyKey}=${propertyValue} +``` + +In `ConfigChangeRequest`, getting properties by following way: + +```Java +final Properties properties = (Properties) configChangeRequest.getArg(ConfigChangeConstants.PLUGIN_PROPERTIES); +final String ${propertyKey} = properties.getProperty("${propertyKey}"); +``` + +## Plugin Demo + +In the [nacos-group/nacos-plugin](https://github.com/nacos-group/nacos-plugin) repository, there is a demo implementation of a configuration change plugin. This demo plugin implements validation of the configuration content format, validation of the whitelist for importing configuration names, and a webhook callback after the change. To use this plugin, you need to package it as a JAR/ZIP file and place it in the classpath of the Nacos server. Additionally, add the following configuration in `${nacos-server.path}/conf/application.properties`: + +```properties +# webhook +#nacos.core.config.plugin.webhook.enabled=true +# It is recommended to use EB https://help.aliyun.com/document_detail/413974.html +#nacos.core.config.plugin.webhook.url=http://${webhookIp}:${webhookPort}/${webhookUri}?token=*** +# The content push max capacity ,byte +#nacos.core.config.plugin.webhook.contentMaxCapacity=102400 + +# whitelist +#nacos.core.config.plugin.whitelist.enabled=true +# The import file suffixs +#nacos.core.config.plugin.whitelist.suffixs=xml,text,properties,yaml,html + +# fileformatcheck,which validate the import file of type and content +#nacos.core.config.plugin.fileformatcheck.enabled=true +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/en/plugin/config-encryption-plugin.md b/src/content/docs/v3.0/en/plugin/config-encryption-plugin.md new file mode 100644 index 00000000000..927202b452e --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/config-encryption-plugin.md @@ -0,0 +1,82 @@ +--- +title: CONFIGURE ENCRYPTION +keywords: [AES,ENCRYPTION,CONFIGURE ENCRYPTION] +description: CONFIGURE ENCRYPTION +sidebar: + order: 3 +--- + +> To ensure the security of users' sensitive configuration data, Nacos provides a new feature of configuration encryption. The risk of user usage is reduced, and the configuration is no longer required to be encrypted separately. + +# Preconditions + +**Version:** + +The old version is temporarily incompatible. Currently, it is only based on the 2.x version. The recommended version is > 2.0.4. + +**Embedded database startup:** + +The database table config_info, config_info_beta, his_config_info needs to add a new field `encrypted_data_key` to store the key used for encryption of each configuration item. This field has been added to the sql of the new version of the default table creation. + +If you have used the single-machine mode of the embedded database before, you need to delete the nacos/data folder, and the table will be recreated after restarting. + +**MySQL start:** + +The database table config_info, config_info_beta, his_config_info needs to add a new field `encrypted_data_key` to store the key used for encryption of each configuration item. This field has been added to the sql of the new version of the default table creation. + +> For the currently built Nacos, use the following sql to add fields to the corresponding table: +> +> ``ALTER TABLE table_name ADD COLUMN `encrypted_data_key` text NOT NULL COMMENT '秘钥'`` + +# Plug-in implementation + +![](https://tva1.sinaimg.cn/large/008i3skNly1gvsu112vnnj314b0u0764.jpg) + +The operations of encryption and decryption are abstracted through the SPI mechanism, and Nacos provides the implementation of `AES` by default. Users can also customize the implementation of encryption and decryption. The specific implementation is in the [nacos-plugin](https://github.com/nacos-group/nacos-plugin) repository. + +When the Nacos server starts, all dependent encryption and decryption algorithms will be loaded, and then by publishing the prefix of the configured `dataId` to match whether encryption and decryption are required and the encryption and decryption algorithms used. + +The configuration published by the client will be encrypted and decrypted by the filter on the client side, that is, the configuration is in cipher text during the transmission process. The configuration published by the console will be processed on the server side. + +# How to use + +The Nacos encryption and decryption plug-in is pluggable, and it does not affect the operation of the core functions of Nacos. If you want to use the configuration encryption and decryption functions of Naocs, you need to refer to the implementation of the encryption algorithm separately. Both the client and the server use the AES encryption and decryption algorithm by adding the following dependencies. The server is recommended to add it to the config module. + +``` + + com.alibaba.nacos + nacos-aes-encryption-plugin + ${nacos-aes-encryption-plugin.version} + +``` +${nacos-aes-encryption-plugin.version} Get the latest version of the plugin。 + +> The plugin doesn't upload to Maven Central Repository, you need to compile it by yourslfe + +# How to compile + +You need to compile `nacos` and install to your local repository,before all the things. + +1. `git clone git@github.com:alibaba/nacos.git` +2. `cd nacos && mvn -B clean package install -Dmaven.test.skip=true` + +> if during this time occur an error that maven can't resolve `${revision}`, you may need to update maven version to latest. + +3. `git clone git@github.com:nacos-group/nacos-plugin.git` +4. `mvn install` + +Done, enjoy it! + +Suggestion: upload to your company repository if you can + +# Create encryption configuration + +- Open the Nacos console and click New Configuration. + + ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h0cxaklw10j21g20u0ac8.jpg) +- The configuration prefix uses cipher-[encryption algorithm name]-dataId to identify that the configuration needs to be encrypted, and the system will automatically identify and encrypt it. For example use the AES algorithm to decrypt the configuration: cipher-aes-application-dev.yml. + + ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h0cxs40s2tj21b40u0whw.jpg) +- Click Save to view the database + + ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h0cxwhdc77j21xm0bumz2.jpg) \ No newline at end of file diff --git a/src/content/docs/v3.0/en/plugin/custom-environment-plugin.md b/src/content/docs/v3.0/en/plugin/custom-environment-plugin.md new file mode 100644 index 00000000000..18f53d19046 --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/custom-environment-plugin.md @@ -0,0 +1,58 @@ +--- +title: CustomEnvironment +keywords: [CustomEnvironment,CustomConfiguration,DatabasePasswordencryption] +description: Nacos Custom environment variable plugin, can customize the extension server configuration and other functions, such as database password encryption. +sidebar: + order: 6 +--- + +# CustomEnvironmentPlugins + +Since version 2.2.0, Nacos support to inject custom environment plugins through [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html),to customize the configuration of nacos in the plugin and do it the way you expect (such as database password encryption). +This document will describe how to implement a custom environment plugin and how to make it work. +> Attention: +> At present, the track tracing plugin is still in the beta stage, and its API and interface definitions maybe modified with version upgrades. Please pay attention to the applicable version of your plugin. + +## Plugin Development + +To develop a Nacos custom environment plugin, developer first need to depend on the relevant API of the custom environment plugin. + +```xml + + com.alibaba.nacos + nacos-custom-environment-plugin + ${project.version} + +``` + +`${project.version}` is the version of Nacos for your development plugin. + +Then implement interface `com.alibaba.nacos.plugin.environment.spi.CustomEnvironmentPluginService`, and put your implementation into services of SPI. + +The methods of interface in following: + +| method name | parameters | returns | description | +|-------------|-----------------------|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| pluginName | `void` | `String` | The name of the plugin. | +| propertyKey | `void` | `Set` | Specifies the name of the configuration item that you want to customize for the server. | +| order | `void` | `Integer` | The higher the number, the higher the priority of the plugin. Multiple plugins customize the same configuration item at the same time. The plugin with a higher priority overwrites the plugin with a lower priority. | +| customValue | `Map` | `Map` | The entry parameter is the value of the configuration item corresponding to propertyKey, and the exit parameter is the value of the self-defined configuration item. | + +In [nacos-group/nacos-plugin](https://github.com/nacos-group/nacos-plugin),providing a demo implementation for custom environment plugin. This demo implements the database password Base64 decryption, +So,you can set the ciphertext database password instead of the plaintext password in the `application.properties` configuration file. + +# How to use +After the plugin finished, it needs to be packaged into jar/zip and places in the classpath of the nacos server. If you don't know how to add plugins into the classpath, please place plugins under `${nacos-server.path}/plugins` directly. + +After Adding plugins into classpath, also need to modify some configuration in `${nacos-server.path}/conf/application.properties`. + +```properties +### open custom environment +nacos.custom.environment.enabled=true +``` + +Restart nacos cluster, some logs can be saw in `${nacos-server.path}/logs/core-auth.log`: + +```text +[CustomEnvironmentPluginManager] Load customEnvironmentPluginService(xxx) customEnvironmentPluginName(xxx) successfully.. +``` diff --git a/src/content/docs/v3.0/en/plugin/datasource-plugin.md b/src/content/docs/v3.0/en/plugin/datasource-plugin.md new file mode 100644 index 00000000000..3b69bdd0790 --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/datasource-plugin.md @@ -0,0 +1,9 @@ +--- +title: Multiple data sources +keywords: [MySQL,Derby,Multiple data sources] +description: Multiple data sources +sidebar: + order: 4 +--- + +To be determined \ No newline at end of file diff --git a/src/content/docs/v3.0/en/plugin/trace-plugin.md b/src/content/docs/v3.0/en/plugin/trace-plugin.md new file mode 100644 index 00000000000..4d8e25adfae --- /dev/null +++ b/src/content/docs/v3.0/en/plugin/trace-plugin.md @@ -0,0 +1,201 @@ +--- +title: Tracing +keywords: [Track tracing, push tracing, modifying tracing] +description: Nacos supports Track tracing plugin, can help developers and maintainers find out problems quickly by extending plugin like push tracing. +sidebar: + order: 5 +--- + +# Track tracing plugin + +Since version 2.2.0, Nacos support to inject track tracing plugins through [SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html), to subscribe and process trace events in the plugin with the way you want (such as logging, writing to storage, etc.). +This document will describe how to implement a track tracing plugin and how to make it work. + +> Attention: +> At present, the track tracing plugin is still in the beta stage, and its API and interface definitions maybe modified with version upgrades. Please pay attention to the applicable version of your plugin. +> +> The track tracing of Nacos is different from the tracking in general sense. It is mainly used to trace and record some Nacos related operations, such as service registration, de-registration, push, status change, etc. +> It is not used to trace the access and request between micro-services. If you need to monitor the access and request between services, please use the corresponding tracing projects. + +## Concepts in Track tracing Plugin + +### TraceEvent + +Nacos embeds points in the important operations, and defines a series of trace events named 'TraceEvent'. After combined multiple 'TraceEvent's for the same resource (such as services, configurations, etc.), the trace of the resource will be gotten. + +The TraceEvent will include following: + +|Field Name|Description| +|-----|---| +|type|Type of event, defined by sub-events| +|eventTime|Time of the event occurs| +|namespaceId|Corresponding resource namespace ID of the event| +|group| Corresponding resource group name of the event| +|name | Corresponding resource name of the event,such as service name or dataId for config| + +Currently, the sub event types defined in Nacos include: + +|Event Name|Description|Details| +|-----|---|---| +|RegisterInstanceTraceEvent|The event for service instance registration, mainly occurs when the service provider is registered|[Detail](#1.1)| +|DeregisterInstanceTraceEvent|The event for service instance de-registration, mainly occurs when the service provider is de-registered|[Detail](#1.2)| +|RegisterServiceTraceEvent|The event for service registration, different from `RegisterInstanceTraceEvent`, mainly occurs when create empty services|[Detail](#1.3)| +|DeregisterServiceTraceEvent|The event for service de-registration, different from `DeregisterInstanceTraceEvent`, mainly occurs when remove empty services|[Detail](#1.4)| +|SubscribeServiceTraceEvent|The event for service subscription, mainly occurs when the service is subscribed|[Detail](#1.5)| +|UnsubscribeServiceTraceEvent|The event for service unsubscription, mainly occurs when the service is unsubscribed|[Detail](#1.6)| +|PushServiceTraceEvent|The event for service pushing, mainly occurs when the service is pushed to subscribed|[Detail](#1.7)| +|HealthStateChangeTraceEvent|The event for service instance health state changing, mainly occurs when an instance's health state changes due to a heartbeat/health check|[Detail](#1.8)| + +## Plugin Development + +To develop a Nacos track tracing plugin, developer first need to depend on the relevant API of the track tracing plugin. + +```xml + + com.alibaba.nacos + nacos-trace-plugin + ${project.version} + +``` + +`${project.version}` is the version of Nacos for your development plugin. + +Then implement interface `com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber`, and put your implementation into services of SPI. + +The methods of interface in following: + +|method name|parameters|returns|description| +|-----|-----|-----|---| +|getName|void|String|he name of the plugin. When the name is the same, the plugin loaded later will overwrite the plugin loaded first.| +|subscribeTypes|void|List>|The expected the event type of the subscription for this plugin. If returns an empty list, plugin will not subscribe any event.| +|onEvent|TraceEvent|void|The logic for handle events. The type of events will defined by `subscribeTypes`.| +|executor|void|Executor|When return not `null`, Nacos will use the `Executor` to call `onEvent`, otherwise use event distribution thread to call `onEvent`.| + +> Attention: +> It is recommend that you use a dependent Executor for plugin implementations, such as blocked IO operations in plugin implementations, which will block onEvent called to other events when there are IO exceptions, causing backlogs. + +In [nacos-group/nacos-plugin](https://github.com/nacos-group/nacos-plugin),providing a demo implementation for track tracing plugin. This demo subscribes `RegisterInstanceTraceEvent` and `DeregisterInstanceTraceEvent` and print result information into logs. + +## Degradation of Track Tracking Plugin + +Because the Track Tracking Plugin is for the monitoring category, and will not affect Nacos data. So when the Track Tracking Plugin has problems, it should not affect the Nacos primary works. + +It is recommend that you use a dependent Executor for plugin implementations, such as blocked IO operations in plugin implementations, which will block onEvent called to other events when there are IO exceptions, causing backlogs. + +If the backlog occurs unfortunately, subsequent events will be automatically discarded when the event queue of the Track Tracking Plugin reaches the upper limit to ensure overall system stability. + +You can see the words `Trace Event Publish failed, event : {}, publish queue size : {}` in `nacos.log` when the discard occurred + +## Appendix: Sub-trace Event Details + +

RegisterInstanceTraceEvent

+ +> Since 2.2.0. + +type: `REGISTER_INSTANCE_TRACE_EVENT` + +Extra Content: + +|Field Name|Description| +|-----|---| +|clientIp|The source IP of registering service instance request, probably null.| +|rpc|Whether the source request is gRPC, `true` when request is gRPC, `false` is HTTP.| +|instanceIp|The IP or Host of service instance registered| +|instancePort|The Port of service instance registered| + +

DeregisterInstanceTraceEvent

+ +> Since 2.2.0. + +type: `DEREGISTER_INSTANCE_TRACE_EVENT` + +Extra Content: + +|Field Name|Description| +|-----|---| +|clientIp|The source IP of de-registering service instance request, probably null.| +|reason|The reason of de-registering, details see [DeregisterInstanceReason](#1.2.1)| +|rpc|Whether the source request is gRPC, `true` when request is gRPC, `false` is HTTP.| +|instanceIp|The IP or Host of service instance de-registered| +|instancePort|The Port of service instance de-registered| + +

DeregisterInstanceReason

+ +|Reason|Description| +|-----|---| +|REQUEST|De-registration comes from client requests, in other word, user initiated de-registration.| +|NATIVE_DISCONNECTED|De-registration comes from client disconnected| +|SYNCED_DISCONNECTED|De-registration comes from client disconnected in other server node, and synced from other server node.| +|HEARTBEAT_EXPIRE|De-registration comes from heartbeat timeout for 1.X version client.| + +

RegisterServiceTraceEvent

+ +> Since 2.2.0. + +type: `REGISTER_SERVICE_TRACE_EVENT` + +Extra Content: None + +

DeregisterServiceTraceEvent

+ +> Since 2.2.0. + +type: `DEREGISTER_SERVICE_TRACE_EVENT` + +Extra Content: None + +

SubscribeServiceTraceEvent

+ +> Since 2.2.0. + +type: `SUBSCRIBE_SERVICE_TRACE_EVENT` + +Extra Content: + +|Field Name|Description| +|-----|---| +|clientIp|The IP of subscriber| + +

UnsubscribeServiceTraceEvent

+ +> Since 2.2.0. + +type: `UNSUBSCRIBE_SERVICE_TRACE_EVENT` + +Extra Content: + +|Field Name|Description| +|-----|---| +|clientIp|The IP of subscriber| + +

PushServiceTraceEvent

+ +> Since 2.2.0. + +type: `PUSH_SERVICE_TRACE_EVENT` + +Extra Content: + +|Field Name|Description| +|-----|---| +|clientIp|The IP of subscriber| +|instanceSize|The size of service instance for this push| +|pushCostTimeForAll|The full cost for this push, means that the cost from start pushing to end pushing, including the wait time in combined queue and the time for executing.| +|pushCostTimeForNetWork|The network cost for this push, means that the cost from executing to end pushing, only including the network cost.| +|serviceLevelAgreementTime|The actual cost for this push, means the cost from services changeing to end pushing. It's a reference value not accuracy.| + +

HealthStateChangeTraceEvent

+ +> Since 2.2.0. + +type: `HEALTH_STATE_CHANGE_TRACE_EVENT` + +Extra Content: + +|Field Name|Description| +|-----|---| +|instanceIp|The IP or Host of service instance changed| +|instancePort|The Port of service instance changed| +|isHealthy|The change result is healthy or not| +|healthCheckType|The type of health check| +|healthStateChangeReason|The reason of healthy changed| diff --git a/src/content/docs/v3.0/en/quickstart/quick-start-docker.mdx b/src/content/docs/v3.0/en/quickstart/quick-start-docker.mdx new file mode 100644 index 00000000000..fc30a2fb9e4 --- /dev/null +++ b/src/content/docs/v3.0/en/quickstart/quick-start-docker.mdx @@ -0,0 +1,83 @@ +--- +title: Nacos Docker 快速开始 +keywords: [Nacos,Docker] +description: Nacos Docker 快速开始 +sidebar: + order: 2 +--- + +# Nacos Docker 快速开始 + +这个快速开始手册是帮忙您快速在通过Nacos的Docker镜像,在Docker容器中部署并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**单机模式**及**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**集群模式**并**开启鉴权**,以避免存在**稳定性和安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 1. 环境准备 + +使用此快速开始方法进行Nacos安装及部署,需要安装[Docker](https://www.docker.com/)和[Docker Compose](https://docs.docker.com/compose/)。 + +## 2. 下载 nacos-docker 项目 + +```powershell +git clone https://github.com/nacos-group/nacos-docker.git +cd nacos-docker +``` + +## 3. 执行 docker-compose 命令启动Nacos + +> 首次执行命令时,会自动下载所需的相关Docker镜像,需要等待的时长取决于网络速度。您也可以提前下载好相关镜像,以缩短执行部署命令的等待时间。 + +```powershell +docker-compose -f example/standalone-derby.yaml up +``` + +## 4. 验证Nacos服务是否启动成功 + +通过`docker logs -f $container_id`命令,查看Nacos服务启动日志,如果看到如下日志,说明服务启动成功。 + +``` +Nacos started successfully in xxxx mode. use xxxx storage +``` + +可以通过下列服务,快速检验Nacos的功能。 + +### 4.1. 服务注册 + + ```powershell + curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080' + ``` + +### 4.2. 服务发现 + + ```powershell + curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName' + ``` + +### 4.3. 发布配置 + + ```powershell + curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld" + ``` + +### 4.4. 获取配置 + + ```powershell + curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test" + ``` + +### 4.5. Nacos控制台页面 + + link:http://127.0.0.1:8848/nacos/ + +## Nacos + Grafana + Prometheus +参考:[Nacos监控指南](../guide/admin/monitor-guide.md) + +**Note**: grafana创建一个新数据源时,数据源地址必须是 **http://prometheus:9090** + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Docker](https://github.com/nacos-group/nacos-docker) diff --git a/src/content/docs/v3.0/en/quickstart/quick-start-kubernetes.mdx b/src/content/docs/v3.0/en/quickstart/quick-start-kubernetes.mdx new file mode 100644 index 00000000000..a8b0f689af5 --- /dev/null +++ b/src/content/docs/v3.0/en/quickstart/quick-start-kubernetes.mdx @@ -0,0 +1,81 @@ +--- +title: Nacos Kubernetes 快速开始 +keywords: [nacos,kubernetes] +description: 本项目包含一个可构建的Nacos Docker Image,旨在利用 StatefulSets 在 Kubernetes上部署 Nacos。 +sidebar: + order: 3 +--- + +# Nacos Kubernetes 快速开始 + +这个快速开始手册是帮忙您快速在通过Nacos的Docker镜像,在Kubernetes中部署并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**开启鉴权**,以避免存在**安全性**的风险。 +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始为方便快速在kubernetes环境中部署Nacos,连接数据库时使用了默认的数据库配置,建议仅在测试中使用,在实际生产环境部署时,请**务必**修改部署的数据库配置信息,以避免存在**安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 1. 环境准备 + +使用此快速开始方法进行Nacos安装及部署,需要安装[Kubernetes](https://kubernetes.io/)。 + +## 2. 下载 nacos-k8s 项目 + +```shell +git clone https://github.com/nacos-group/nacos-k8s.git +cd nacos-k8s +``` + +## 3. 快速启动 + +> 使用此方式快速启动,请注意这是没有使用持久化卷的,可能存在数据丢失风险: + +```shell +cd nacos-k8s +chmod +x quick-startup.sh +./quick-startup.sh +``` + +## 4. 验证Nacos服务是否启动成功 + +通过`kubectl logs -f $pod_name`命令,查看Nacos服务启动日志,如果看到如下日志,说明服务启动成功。 + +``` +Nacos started successfully in xxxx mode. use xxxx storage +``` + +可以通过下列服务,快速检验Nacos的功能。 + +### 4.1. 服务注册 + + ```powershell + curl -X POST 'http://${cluster-ip}:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080' + ``` + +### 4.2. 服务发现 + + ```powershell + curl -X GET 'http://${cluster-ip}:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName' + ``` + +### 4.3. 发布配置 + + ```powershell + curl -X POST "http://${cluster-ip}:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld" + ``` + +### 4.4. 获取配置 + + ```powershell + curl -X GET "http://${cluster-ip}:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test" + ``` + +### 4.5. Nacos控制台页面 + +link:`http://${cluster-ip}:8848/nacos/` + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Docker](https://github.com/nacos-group/nacos-k8s) \ No newline at end of file diff --git a/src/content/docs/v3.0/en/quickstart/quick-start-native.mdx b/src/content/docs/v3.0/en/quickstart/quick-start-native.mdx new file mode 100644 index 00000000000..f6f759a4573 --- /dev/null +++ b/src/content/docs/v3.0/en/quickstart/quick-start-native.mdx @@ -0,0 +1,69 @@ +--- +title: Nacos Native Quick Start +keywords: [Nacos,Quick Start] +description: This quick start guide is designed to help you quickly download, install, and use Nacos on your computer. +sidebar: + order: 1 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Nacos Quick Start + +This quick start guide is designed to help you quickly download, install, and use Nacos on your computer. + +:::note +- The quick start guide is intended to assist you in quickly setting up, deploying, and getting started with Nacos. The Nacos service set up via this guide operates in **standalone mode** without **authentication enabled**, and is recommended for testing purposes only. For production environments, it's advised to deploy Nacos in **cluster mode** with **authentication enabled** to mitigate **stability and security risks**. +- Nacos is intended to be used as an internal data center (IDC) application component, not a publicly facing product. It is highly recommended to deploy Nacos within an isolated internal network. **It is strongly discouraged** to deploy Nacos in public network environments. +::: + +## System Requirements +Nacos Native is a version of Nacos built using the GraalVM, and it no longer depends on a separate JDK installation. Nacos Native currently provides executable files for Linux (GNU) and macOS. The current version of Nacos Native corresponds to Java Nacos version 2.4.0. Executable binaries can be downloaded from the corresponding release page. It is recommended to run Nacos Native on a complete 64-bit operating system that includes the `libstdc++` library. + +## Download and Extract the Package +After downloading the program package from the appropriate page, extract it and verify the files: + +1. On Linux operating systems, the extracted files should include the `nacos-server` binary, the `libinstrument.so` symlink, and other related configurations. +2. On macOS, the extracted files should include the `nacos-server` binary and other related configurations. + +If a Nacos Native version for other operating systems is not available, you can manually compile the source code on that operating system to obtain the Nacos Native binary package. + +## Starting the Server +It is recommended to run Nacos Native on a machine with at least 1 CPU core, 2 GB of RAM, and 60 GB of storage. On Linux/Unix/macOS, you can start Nacos Native using the following command: + +```shell +# standalone means running in single-node mode, not cluster mode +sh startup.sh -m standalone +``` + +If you want to start Nacos Native in cluster mode, make sure to have a `cluster.conf` file in the `conf` directory. This process is the same as with Java Nacos. After starting Nacos Native in cluster mode, it will automatically scan and retrieve the cluster IP addresses. + +## Stopping the Server +To stop the Nacos Native server on Linux/Unix/macOS, use the following command: + +```shell +sh shutdown.sh +``` + +## Viewing Logs +For Linux/Unix operating systems, Nacos Native's runtime logs are recorded in the `/root/nacos/logs` directory. The log structure is consistent with that of Java Nacos. + +## Building Native Nacos +Nacos Native provides build configurations for different operating systems. You can quickly build the Nacos Native executable for a specific OS using Maven. Follow the instructions below to build Nacos Native: + +```shell +# Build Native Nacos for Windows x64 +mvn clean install -DskipTests=true -Pnative -Pnative-win64 +# Build Native Nacos for macOS x86_64 +mvn clean install -DskipTests=true -Pnative -Pnative-osx-x86_64 +# Build Native Nacos for Linux x64 +mvn clean install -DskipTests=true -Pnative -Pnative-linux64 +``` + +The `-Pnative` configuration provides the basic native build settings, while `-Pnative-` specifies the operating system type. This configuration ensures that the RocksDB library is correctly packaged into Nacos Native. It's important to note that while Nacos Native offers a `musl`-based build configuration, GraalVM itself still requires compilation on a GNU-supported operating system, so using `musl` for compilation is not recommended. + +After a successful build, the relevant files will be published in the `/console/target` directory. You can refer to the "Download and Extract the Package" section to verify the completeness of the build and retrieve the compiled files. + +## Related Projects + +* [Nacos](https://github.com/alibaba/nacos) diff --git a/src/content/docs/v3.0/en/quickstart/quick-start.mdx b/src/content/docs/v3.0/en/quickstart/quick-start.mdx new file mode 100644 index 00000000000..a30749577cf --- /dev/null +++ b/src/content/docs/v3.0/en/quickstart/quick-start.mdx @@ -0,0 +1,117 @@ +--- +title: Nacos 快速开始 +keywords: [Nacos,快速开始] +description: 这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 +sidebar: + order: 1 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Nacos 快速开始 + +这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**单机模式**及**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**集群模式**并**开启鉴权**,以避免存在**稳定性和安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 0. 版本选择 + +您可以通过Nacos的[版本下载页面](/download/nacos-server/)、[release notes](https://github.com/alibaba/nacos/releases) 及 [发布声明](/news/release/)中找到每个版本支持的功能的介绍,当前推荐的稳定版本为`2.4.3`. + +## 1. 预备环境准备 + +Nacos 依赖 [Java](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/) 环境来运行,请确保是在以下版本环境中安装使用: + +1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。 +2. 64 bit JDK 1.8+;[下载](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) & [配置](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/)。 + +## 2. 下载安装包 + +你可以通过Nacos官网网站及Github两种方式来获取 **Nacos 发行包** 。 + + + + 进入Nacos官网[版本下载页面](/download/nacos-server/),选择 [稳定版本](/download/nacos-server/#稳定版本), 然后点击`二进制包下载`列中的`${nacos.version}.zip`进行下载。 + + > 注意:有时大量用户同时进行下载时,可能会遇到下载限流失败的情况,若出现下载限流失败,请稍等后重试,或采用`从 Github 下载方式`。 + + + 进入Nacos Github 的 [最新稳定版本](https://github.com/alibaba/nacos/releases) ,选择需要下载的Nacos版本,在`Assets`中点击下载 `nacos-server-$version.zip` 包。 + + + +## 3. 解压缩Nacos 发行包 + +```bash + unzip nacos-server-$version.zip + # 或者 tar -xvf nacos-server-$version.tar.gz + cd nacos/bin +``` + +## 4.启动服务器 + +* 注:Nacos的运行建议至少在2C4G 60G的机器配置下运行。 + + + + 启动命令(standalone代表着单机模式运行,非集群模式): + + `sh startup.sh -m standalone` + + 如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行: + + + 启动命令(standalone代表着单机模式运行,非集群模式): + + `startup.cmd -m standalone` + + + +## 5.验证Nacos服务是否启动成功 + +进入`${nacos.home}/logs/` 目录下, 使用`tail -f start.out` 查看日志,如果看到如下日志,说明服务启动成功。 + +``` +Nacos started successfully in stand alone mode. use embedded storage +``` + +可以通过下列服务,快速检验Nacos的功能。 + +### 5.1. 服务注册 + +`curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'` + +### 5.2. 服务发现 + +`curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'` + +### 5.3. 发布配置 + +`curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"` + +### 5.4. 获取配置 + +`curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"` + +### 5.5. Nacos控制台页面 + +打开任意浏览器,输入地址:`http://127.0.0.1:8848/nacos`,即可进入Nacos控制台页面。 + +## 6.关闭服务器 + +### 6.1. Linux/Unix/Mac + +`sh shutdown.sh` + +### 6.2. Windows + +`shutdown.cmd` + +或者双击shutdown.cmd运行文件。 + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) diff --git a/src/content/docs/v3.0/en/upgrading/200-compatibility.md b/src/content/docs/v3.0/en/upgrading/200-compatibility.md new file mode 100644 index 00000000000..794cfe20c37 --- /dev/null +++ b/src/content/docs/v3.0/en/upgrading/200-compatibility.md @@ -0,0 +1,208 @@ +--- +title: Nacos 2.0.0 compatibility +keywords: [Nacos,2.0.0] +description: Nacos 2.0.0 compatibility +sidebar: + order: 1 +--- + +> Document optimizing... + +# Nacos2.0 code storage location + +The Nacos code is currently stored in the feature_support_grpc_core branch. You need to switch to this branch and start again. The startup method is the same as Nacos 1.x, and Welcome to contribute. + +# Nacos 2.0.0 compatibility + +After discussion and development in the community, the core features of Nacos 2.0.0 based on the long connection have been developed completely. + +2.0.0 has released and welcome to use. + +2.0.0 support to smoothly upgrade and downgrade with Nacos1.X server, please refer to [Nacos2.0 upgrade document](./200-upgrading.md) for details. + +## Benchmark for Nacos 2.0.0 + +Detail see: [Nacos2.0 Naming Benchmark](../guide/admin/nacos2-naming-benchmark.md) and [Nacos2.0 Config Benchmark](../guide/admin/nacos2-config-benchmark.md) . + +## Deployment + +Compared with 1.X, Nacos2.0 adds gRPC communication mode, so there are 2 ports need to be added. The new ports are generated with a certain offset based on the configured main port (server.port). + +|port|offset from main port|description| +|--|--|--| +|9848|1000|The client gRPC requests the server port, which is used by the client to initiate a connection and request to the server| +|9849|1001|Server-side gRPC requests server-side port, used for synchronization between services, etc.| + +**When using VIP/nginx requests, you need to configure it as TCP forwarding, instead of http2 forwarding, otherwise the connection will be disconnected by nginx.** + +![nacos2_port_exposure.png](/img/nacos2_port_exposure.png) + + +The client has the same offset logic. The users configures the main port (default 8848) as in the use of 1.X, and calculates the corresponding gRPC port (default 9848) through the same offset. + +Therefore, if there is a port forwarding or firewall between the client and the server, the port forwarding configuration and firewall configuration need to be adjusted accordingly. + +Other detail refer to [Deployment Guide](../guide/admin/deployment.md) and replace the version to 2.1.1. + +## Compatibility + +The server of Nacos2.0 is fully compatible with 1.X clients. The Nacos2.0 client is not compatible with the Nacos1.X server because it uses gRPC. Please do not use the client of version 2.0 or higher to connect to the Nacos1.X server. + +## Features completion and adaptation for the old client + +### Configuration Management + +#### JAVA SDK + +- Completely compatible with all interfaces of 1.X client; +- Completely implement all interfaces of 2.X client. + +#### Other SDK + +- Completely compatible + +#### openAPI + +- Fully compatible with all openAPIs related to the configuration management. + +### Service Discovery + +#### JAVA SDK + +- Completely compatible with all interfaces of 1.X client; +- Completely implement all interfaces of 2.X client. + +#### Other SDK + +- Completely compatible + +#### openAPI + +- Register instance (Supported) +- Deregister instance (Supported) +- Modify instance (Supported) +- Query instances (Supported) +- Query instance detail (Supported) +- Send instance beat (Supported) +- Create service (Supported) +- Delete service (Supported) +- Update service (Supported) +- Query service (Supported) +- Query service list (Supported) +- Query system switches (Supported) +- Update system switch (Supported) +- Query system metrics (Supported) +- Query server list (Supported) +- Query the leader of current cluster (Deprecated) +- Update instance health status (Supported) +- Batch update instance metadata(Supported) +- Batch delete instance metadata(Supported) + +### Console + +- Completely compatible with `Config Management` +- Completely compatible with `Authority Control` +- Completely compatible with `Namespace` +- Completely compatible with `Cluster Managerment` +- Completely compatible with `Service Managerment` + +## Ecological compatibility + +### Spring Cloud Alibaba + +Use Nacos 2.0 connection features by specifying nacos-client version. + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2.1.5.RELEASE + + + com.alibaba.nacos + nacos-client + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2.1.5.RELEASE + + + com.alibaba.nacos + nacos-client + + + + + com.alibaba.nacos + nacos-client + 2.1.1 + +``` + +### Dubbo + +The Nacos2.0 version of the client is re-adapted to Dubbo2.7.X. And the Dubbo community is making changes to the new version, no longer relying on reflection, please see [Dubbo#7291](https://github.com/apache/dubbo/issues/7291). + +### Nacos Spring Boot + +Nacos spring boot had released a new version to adapt to the 2.0.0 client, please upgrade to newest version. + +## Usage + +### SDK and Console + +The usage of Nacos 2.0.0 version is exactly the same as that of Nacos 1.X version. For the client interface, please refer to [JAVA SDK](../guide/user/sdk.md). + +### Server + +The usage of the Nacos 2.0.0 server is not much different from the old version. Here only to describe the new configuration parameters in the new version. + +|Parameters|Default|Description| +|--|--|--| +|nacos.naming.clean.empty-service.interval|60000(ms)| The interval time of Nacos auto clean empty service. This parameter will replace `nacos.naming.empty-service.clean.period-time-ms` in old version.| +|nacos.naming.clean.empty-service.expired-time|60000(ms)| The expired time for Nacos judge whether an empty service is expired. When an empty service has no updated for setting time, it will be removed.| +|nacos.naming.clean.expired-metadata.interval|5000(ms)|The interval time of Nacos auto clean expired metadata.| +|nacos.naming.clean.expired-metadata.expired-time|60000(ms)| The expired time for Nacos judge whether a metadata is expired. When services or instances removed and after setting time, the metadata of removed services or instances will be deleted.| + +## FAQ + +### Whether support the old client? + +Configuration Management can support all clients after 1.0, and Service Management can support all client after 1.2. + +So recommending use Nacos client after 1.2.0. + +But Nacos 1.X client can't use new connection features, so recommending to use 2.0.0 client strongly. + +### Error `code:503,msg:server is DOWN now, please try again later!` during using. + +After version 1.4, Nacos use SOFA-Jraft to replace old raft implementation by nacos-self. Jraft will election leader with raft protocol and save the cluster metadata. If cluster restart with ip changed, it might cause Jraft can election leader successfully so that nacos can't start up. + +The solution is removed the `data` directory under nacos directory and restart. + +Or use `-Dnacos.server.ip=${domain}` jvm parameters to start nacos and set domain list in `nacos/conf/cluster.conf` to avoid the ip change effect. + +### `com.alibaba.nacos.consistency.entity` can't be found in source codes + +This package will be auto-generated by `protobuf`, so if you want to read source code or do some develop, you can use `mvn compile` to generate them. If you are using IDEA, you can also use IDEA's protobuf plugin. + +### Error `Connection is unregistered.` or `Client not connected, current status:STARTING` during startup client. + +The reason is that the client gRPC cannot establish a connection with the server. Please use `telnet ${nacos.server.address}:${nacos.server.grpc.port}` to test the network and to check the server port is correct. + +If there is no problem with the server, check whether the configuration is wrong. The configured ports of the server and client should be the same. + +If there is no problem with the configuration, check whether there is a firewall or VIP port forwarding problem. The gRPC ports of Nacos2.0 are calculated by the offset of the main port, so the port forwarding also needs to meet the offset. + +### Nacos2.0 adds ports 9848 and 9849 for GRPC communication. Do I need to configure additional settings in application.properties? + +No, these two ports are calculated by 8848 + 1000 and 8848 + 1001 in Nacos2.0 internally. No additional configuration by the user is required in the configuration file. But if you are using docker or there is a port forwarding method to start, you need to configure these two ports. + +###I want to start nacos2.0, and uses nginx proxy, how to deal with the port 9848, should it be exposed through nginx? + +If there is a firewall or nginx port forwarding problem, you need to configure the corresponding port exposure. For example, in nginx, on the basis of the already exposed 8848(x), an additional 9848(x+1000) needs to be exposed. + +### To be added... diff --git a/src/content/docs/v3.0/en/upgrading/200-upgrading.md b/src/content/docs/v3.0/en/upgrading/200-upgrading.md new file mode 100644 index 00000000000..388d559b7df --- /dev/null +++ b/src/content/docs/v3.0/en/upgrading/200-upgrading.md @@ -0,0 +1,9 @@ +--- +title: Nacos 2.0 upgrading doc +keywords: [Nacos,2.0,upgrading] +description: Nacos 2.0 upgrading doc +sidebar: + order: 2 +--- + +To be determined \ No newline at end of file diff --git a/src/content/docs/v3.0/en/what-is-nacos.md b/src/content/docs/v3.0/en/what-is-nacos.md new file mode 100644 index 00000000000..ee6621d90b1 --- /dev/null +++ b/src/content/docs/v3.0/en/what-is-nacos.md @@ -0,0 +1,96 @@ +--- +title: Nacos Configuration Center profile +keywords: [nacos] +description: What is Nacos +--- + +# What is Nacos + +> This document will be deprecated soon. It is recommended to refer to [Nacos Overview](./overview.md). + +## Overview + +Welcome to Nacos! + +Nacos `/nɑ:kəʊs/` is the acronym for 'Dynamic Naming and Configuration Service',an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications。 + +Nacos is committed to help you discover, configure, and manage your microservices. It provides a set of simple and useful features enabling you to realize dynamic service discovery, service configuration, service metadata and traffic management. + +Nacos makes it easier and faster to construct, deliver and manage your microservices platform. It is the infrastructure that supports a service-centered modern application architecture with a microservices or cloud-native approach. + +## What is Nacos? + +Service is a first-class citizen in Nacos. Nacos supports discovering, configuring, and managing almost all types of services: + +[Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) + +[gRPC](https://grpc.io/docs/guides/concepts.html#service-definition) & [Dubbo RPC Service](https://dubbo.apache.org) + +[Spring Cloud RESTful Service](https://spring.io/projects/spring-cloud) + +Key features of Nacos: + +* **Service Discovery And Service Health Check** + + Nacos supports both DNS-based and RPC-based (Dubbo/gRPC) service discovery. After a service provider registers a service with [native](./guide/user/sdk.md), [OpenAPI](./guide/user/open-api.md), or [a dedicated agent](./guide/user/other-language.md), a consumer can discover the service with either [DNS_TODO](./what-is-nacos.md) or [HTTP](./guide/user/open-api.md). + + Nacos provides real-time health check to prevent services from sending requests to unhealthy hosts or service instances. Nacos supports both transport layer (PING or TCP) health check and application layer (such as HTTP, Redis, MySQL, and user-defined protocol) health check. For the health check of complex clouds and network topologies(such as VPC, Edge Service etc), Nacos provides both agent mode and server mode health check. Nacos also provide a unity service health dashboard to help you manage the availability and traffic of services. + +* **Dynamic configuration management** + + Dynamic configuration service allows you to manage the configuration of all applications and services in a centralized, externalized and dynamic manner across all environments. + + Dynamic configuration eliminates the need to redeploy applications and services when configurations are updated. + + Centralized management of configuration makes it more convenient for you to achieve stateless services and elastic expansion of service instances on-demand. + + Nacos provides an easy-to-use UI ([DEMO](http://console.nacos.io/nacos/index.html)) to help you manage all of your application or services's configurations. It provides some out-of-box features including configuration version tracking, canary/beta release, configuration rollback, and client configuration update status tracking to ensure the safety and control the risk of configuration change. + +* **Dynamic DNS service** + + Dynamic DNS service which supports weighted routing makes it easier for you to implement mid-tier load balancing, flexible routing policies, traffic control, and simple DNS resolution services in your production environment within your data center. Dynamic DNS service makes it easier for you to implement DNS-based Service discovery. + + Nacos provides some simple [DNS APIs TODO](./what-is-nacos.md) for you to manage your DNS domain names and IPs. + +* **Service governance and metadata management** + + Nacos allows you to manage all of your services and metadata from the perspective of a microservices platform builder. This includes managing service description, life cycle, service static dependencies analysis, service health status, service traffic management,routing and security rules, service SLA, and first line metrics. + +## Nacos Map +A picture to understand Nacos, the following structure will be described in detail. + +// TODO this picture need to translate. + +![nacos_map](/img/nacosMap.jpg) + +- Large picture of characteristics: To introduce the characteristics of the problem domain we want to solve from the functional characteristics and non-functional characteristics. +- Larger architecture: Get a quick entry into the Nacos world with a clear architecture +- Business picture: Business scenarios that can be supported with current features, and best practices +- Big picture of the ecology: systematically sorting out the relationship between Nacos and mainstream technology ecology +- Big picture of advantage: showcase Nacos core competitiveness +- Strategic picture: Nacos's macro advantage from strategic to tactical level + + +## Nacos landscape + +![nacos_landscape.png](https://cdn.nlark.com/lark/0/2018/png/11189/1533045871534-e64b8031-008c-4dfc-b6e8-12a597a003fb.png) + +As the figure above shows, Nacos seamlessly supports open source ecologies including + +* [Spring Cloud](./ecology/use-nacos-with-spring-cloud.md) +* [Apache Dubbo and Dubbo Mesh](./ecology/use-nacos-with-dubbo.md) +* [Kubernetes and CNCF](./quickstart/quick-start-kubernetes.mdx) + +Use Nacos to simplify your solutions in service discovery, configuration management, and service governance and management. With Nacos, microservices management in open source system is easy. + +For more information about how to use Nacos with other open source projects, see the following: + +[Use Nacos with Spring Cloud](./ecology/use-nacos-with-spring-cloud.md) + +[Use Nacos with Kubernetes](./quickstart/quick-start-kubernetes.mdx) + +[Use Nacos with Dubbo](./ecology/use-nacos-with-dubbo.md) + +## What’s next + +Continue with [quick start](./quickstart/quick-start.mdx) to get started with Nacos. diff --git a/src/content/docs/v3.0/zh-cn/architecture.md b/src/content/docs/v3.0/zh-cn/architecture.md new file mode 100644 index 00000000000..ae8a3737a4b --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/architecture.md @@ -0,0 +1,130 @@ +--- +title: Nacos 架构 +keywords: [Nacos,架构] +description: Nacos 架构 +--- + +# Nacos 架构 + +> 文档优化中...... + +## 基本架构及概念 + +![nacos_arch.jpg](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217892717-1418fb9b-7faa-4324-87b9-f1740329f564.jpeg) + +### 服务 (Service) + +服务是指一个或一组软件功能(例如特定信息的检索或一组操作的执行),其目的是不同的客户端可以为不同的目的重用(例如通过跨进程的网络调用)。Nacos 支持主流的服务生态,如 Kubernetes Service、gRPC|Dubbo RPC Service 或者 Spring Cloud RESTful Service。 + +### 服务注册中心 (Service Registry) + +服务注册中心,它是服务,其实例及元数据的数据库。服务实例在启动时注册到服务注册表,并在关闭时注销。服务和路由器的客户端查询服务注册表以查找服务的可用实例。服务注册中心可能会调用服务实例的健康检查 API 来验证它是否能够处理请求。 + +### 服务元数据 (Service Metadata) + +服务元数据是指包括服务端点(endpoints)、服务标签、服务版本号、服务实例权重、路由规则、安全策略等描述服务的数据。 + +### 服务提供方 (Service Provider) + +是指提供可复用和可调用服务的应用方。 + +### 服务消费方 (Service Consumer) + +是指会发起对某个服务调用的应用方。 + +### 配置 (Configuration) + +在系统开发过程中通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成这个步骤。配置变更是调整系统运行时的行为的有效手段之一。 + +### 配置管理 (Configuration Management) + +在数据中心中,系统中所有配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动统称为配置管理。 + +### 名字服务 (Naming Service) + +提供分布式系统中所有对象(Object)、实体(Entity)的“名字”到关联的元数据之间的映射管理服务,例如 ServiceName -> Endpoints Info, Distributed Lock Name -> Lock Owner/Status Info, DNS Domain Name -> IP List, 服务发现和 DNS 就是名字服务的2大场景。 + +### 配置服务 (Configuration Service) + +在服务或者应用运行过程中,提供动态配置或者元数据以及配置管理的服务提供者。 + +### [更多概念...](./concepts.md) + + +## 逻辑架构及其组件介绍 + +![nacos-logic.jpg](https://cdn.nlark.com/yuque/0/2022/png/25601973/1646715315872-7ee3679a-e66e-49e9-ba9f-d24168a86c14.png) + +- 服务管理:实现服务CRUD,域名CRUD,服务健康状态检查,服务权重管理等功能 +- 配置管理:实现配置管CRUD,版本管理,灰度管理,监听管理,推送轨迹,聚合数据等功能 +- 元数据管理:提供元数据CURD 和打标能力 +- 插件机制:实现三个模块可分可合能力,实现扩展点SPI机制 +- 事件机制:实现异步化事件通知,sdk数据变化异步通知等逻辑 +- 日志模块:管理日志分类,日志级别,日志可移植性(尤其避免冲突),日志格式,异常码+帮助文档 +- 回调机制:sdk通知数据,通过统一的模式回调用户处理。接口和数据结构需要具备可扩展性 +- 寻址模式:解决ip,域名,nameserver、广播等多种寻址模式,需要可扩展 +- 推送通道:解决server与存储、server间、server与sdk间推送性能问题 +- 容量管理:管理每个租户,分组下的容量,防止存储被写爆,影响服务可用性 +- 流量管理:按照租户,分组等多个维度对请求频率,长链接个数,报文大小,请求流控进行控制 +- 缓存机制:容灾目录,本地缓存,server缓存机制。容灾目录使用需要工具 +- 启动模式:按照单机模式,配置模式,服务模式,dns模式,或者all模式,启动不同的程序+UI +- 一致性协议:解决不同数据,不同一致性要求情况下,不同一致性机制 +- 存储模块:解决数据持久化、非持久化存储,解决数据分片问题 +- Nameserver:解决namespace到clusterid的路由问题,解决用户环境与nacos物理环境映射问题 +- CMDB:解决元数据存储,与三方cmdb系统对接问题,解决应用,人,资源关系 +- Metrics:暴露标准metrics数据,方便与三方监控系统打通 +- Trace:暴露标准trace,方便与SLA系统打通,日志白平化,推送轨迹等能力,并且可以和计量计费系统打通 +- 接入管理:相当于阿里云开通服务,分配身份、容量、权限过程 +- 用户管理:解决用户管理,登录,sso等问题 +- 权限管理:解决身份识别,访问控制,角色管理等问题 +- 审计系统:扩展接口方便与不同公司审计系统打通 +- 通知系统:核心数据变更,或者操作,方便通过SMS系统打通,通知到对应人数据变更 +- OpenAPI:暴露标准Rest风格HTTP接口,简单易用,方便多语言集成 +- Console:易用控制台,做服务管理、配置管理等操作 +- SDK:多语言sdk +- Agent:dns-f类似模式,或者与mesh等方案集成 +- CLI:命令行对产品进行轻量化管理,像git一样好用 + +## 领域模型 + +### 数据模型 + +Nacos 数据模型 Key 由三元组唯一确定, Namespace默认是空串,公共命名空间(public),分组默认是 DEFAULT_GROUP。 + +![nacos_data_model](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217857314-95ab332c-acfb-40b2-957a-aae26c2b5d71.jpeg) + +### 服务领域模型 + +![nacos_naming_data_model](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217924697-ba504a35-129f-4fc6-b0df-1130b995375a.jpeg) + +### 配置领域模型 + +围绕配置,主要有两个关联的实体,一个是配置变更历史,一个是服务标签(用于打标分类,方便索引),由 ID 关联。 + +![nacos_config_er](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561217958896-4465757f-f588-4797-9c90-a76e604fabb4.jpeg) + + +## 类视图 + +### Nacos-SDK 类视图 + +服务部分待续 + +![nacos_sdk_class_relation](https://cdn.nlark.com/yuque/0/2022/png/25574784/1650771676187-d95a9e45-8656-4d1a-8b5b-ed63a23a816b.png) + + +## 构建物、部署及启动模式 + +![undefined](https://cdn.yuque.com/lark/0/2018/png/15914/1531730742844-e8325932-258b-49b2-9473-8d1199efe20d.png) + +### 两种交付工件 + +Nacos 支持标准 Docker 镜像(TODO: 0.2版本开始支持)及 zip(tar.gz)压缩包的构建物。 + +### 两种启动模式 + +Nacos 支持将注册中心(Service Registry)与配置中心(Config Center) 在一个进程合并部署或者将2者分离部署的两种模式。 + +### 免费的公有云服务模式 + +除了您自己部署和启动 Nacos 服务之外,在云计算时代,Nacos 也支持公有云模式,在阿里云公有云的商业产品(如[MSE](https://cn.aliyun.com/product/aliware/mse?spm=nacos-website.topbar.0.0.0m), [EDAS](https://www.aliyun.com/product/edas)) 中会提供 Nacos 的免费的公有云服务。我们也欢迎和支持其他的公有云提供商提供 Nacos 的公有云服务。 diff --git a/src/content/docs/v3.0/zh-cn/archive/activity.md b/src/content/docs/v3.0/zh-cn/archive/activity.md new file mode 100644 index 00000000000..03b086225ae --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/activity.md @@ -0,0 +1,48 @@ +--- +title: Nacos 有奖活动介绍 +keywords: [Nacos,活动] +description: Nacos 有奖活动介绍 +--- + +# Nacos 有奖活动介绍 + +## I. 活动任务列表 + +* 阅读官网文案,找官网bug, 给中、英文官网提建设性建议 +* 阅读中英文档,找文档bug, 给中、英文档改善提建议(尤其是关注英文翻译不好的地方,因为英文都是我们程序员自己撸的) +* 尝试下代码 ->编译打包 -> 启动Nacos server -> 停止Nacos server流程,提出改进意见 +* 尝试配置以及启动多节点 Nacos 集群模式任务,提改进意见 +* 尝试使用Nacos Java SDK, 给Java SDK提改进建议 +* 尝试使用Nacos Open API,给OpenAPI提改进建议 +* 尝试根据《如何贡献Nacos文档》试一下 贡献流程,给贡献者流程提建议 +* 给Nacos提需求、发展计划、想法和要求等 + +## II. 活动参与方式 + +* 扫描 “超哥” 微信2微码,让超哥帮助加入 “Nacos社区交流群” + +![微信二维码 | left](https://cdn.yuque.com/lark/0/2018/png/11189/1532004866850-5e03b901-6d76-4380-b7bf-66e227808bdc.png "") + +* 选择 (I) 中的一个或者多个体验任务 +* 发现问题或者BUG之后,按照(III)中的《问题Report方式》,发一个相应的 github issue, 并指派给 @ github账号[xuechaos](https://github.com/xuechaos) + +## III. 问题Report方式 + +* 根据issues模板,在github的nacos仓库,中提交问题。 +* 代码仓库问题,提交pull request修复,通过review后,合并进入主干,就成为了Contributor。 +* +* [https://github.com/alibaba/nacos](https://github.com/alibaba/nacos) +* [https://github.com/nacos-group/nacos-group.github.io](https://github.com/nacos-group/nacos-group.github.io) + +## IV. 任务奖励 + +* 我们正在为参与并作出突出贡献的小伙伴定制一些带有Nacos Logo的小礼品,会考虑快递给过程中有突出贡献的小伙伴。 +* 礼物虽轻,但希望能表达对你们的帮助的感激之情的万一。 +* 因资金有限,我们目前只能提供40份礼品,所以我们这次限制报名参与人数为40人,先到先得。 +* 如果你愿意,希望收到礼物的小伙伴能发一张与礼物的合影(可以隐藏面部)给我们,作为后续我们社区活动的照片素材。 + +## V. 其它说明 + +* 我们不确定每个建议最后都会被采用,但是我们尽量会说明我们基于何种考虑,您的建议我们最后没有采用。 +* 尽量通过邮件列表或者report issue的方式,而不是在微信群里report问题,以便将我们的沟通过程文档化和更容易沉淀。 + diff --git a/src/content/docs/v3.0/zh-cn/archive/cli-guide.md b/src/content/docs/v3.0/zh-cn/archive/cli-guide.md new file mode 100644 index 00000000000..b9609816ef5 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/cli-guide.md @@ -0,0 +1,7 @@ +--- +title: CLI Guide +keywords: [CLI,Guide] +description: In plan with Nacos 1.x.x +--- + +**IN PLAN** with Nacos 1.x.x diff --git a/src/content/docs/v3.0/zh-cn/archive/contributing-dev.md b/src/content/docs/v3.0/zh-cn/archive/contributing-dev.md new file mode 100644 index 00000000000..28130fd7a0f --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/contributing-dev.md @@ -0,0 +1,127 @@ +--- +title: 为 Nacos 做贡献 +keywords: [Nacos,贡献] +description: 欢迎来到Nacos!本文档是关于如何为Nacos做出贡献的指南。 +--- + +# 为 Nacos 做贡献 + +欢迎来到Nacos!本文档是关于如何为Nacos做出贡献的指南。 + +如果您发现不正确或遗失的内容,请留下意见/建议。 + +## 开始之前 + +### 行为守则 + +请务必阅读并遵守我们的[行为准则](https://github.com/alibaba/nacos/blob/master/CODE_OF_CONDUCT.md)。 + +## 贡献和Nacos社区构成 + +Nacos欢迎任何角色的新参与者,包括用户,贡献者,提交者和PMC。 + +![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/contributor_definition.png) + + +我们鼓励新来者积极参与Nacos社区,社区有一套机制可以使您,从用户角色到提交者角色,甚至是PMC角色。为了实现这一目标,新来者需要积极参与Nacos社区。以下段落介绍了如何为Nacos做出贡献并且晋升社区角色。 + +#### 打开/提取准备问题 + +如果您在文档中发现拼写错误,在代码中发现错误,或想要新功能或想要提供建议,您可以[在GitHub上打开一个问题](https://github.com/alibaba/Nacos/issues/new)报告。 + +如果您想开始着手,可以选择github仓库中有以下标签的issues。 +     + + - [good first issue](https://github.com/alibaba/nacos/labels/good%20first%20issue):对于新手来说是非常好的入门issues。 + + - [contribution welcome](https://github.com/alibaba/nacos/labels/contribution%20欢迎):非常需要解决的问题和非常重要的模块,但目前缺少贡献者,欢迎贡献者来贡献。 + +     + +我们非常重视文档以及与Spring Cloud,Kubernetes,Dubbo等其他项目的集成。我们很希望能够就这些方面的任何问题进行研究。 + +请注意,任何PR(Pull Request)必须与有效issues相关联。否则PR将被打回。 + +#### 开始你的贡献 + +现在,如果您想贡献,请创建一个新的拉取请求。 + +我们使用`develop`分支作为开发分支,这表明这是一个不稳定的分支。 + +此外,我们的分支模型符合[https://nvie.com/posts/a-successful-git-branching-model/](https://nvie.com/posts/a-successful-git-branching-model)。我们强烈建议新成员在创建PR之前完成上述文章。 + +现在,如果您准备创建PR,这里是贡献者的工作流程: + +1. fork Nacos仓库到你的github仓库 +     +2. 克隆fork到本地仓库 +     +3. 创建一个新分支并进行处理 +     +4. 保持分支同步 +     +5. 提交您的更改(确保您的提交消息简明扼要) +     +6. 将提交推送到你的github远程仓库 +     +7. 创建一个指向**开发**分支的Pull Request +     + +创建Pull Request时: + +1. 请遵循[拉取请求模板](https://github.com/alibaba/nacos/blob/master/.github/PULL_REQUEST_TEMPLATE.md)。 +     +2. 请创建**开发**分支的请求。 +     +3. 请确保PR有相应的问题。 +     +4. 如果您的PR包含大量更改,例如组件重构或新组件,请写下有关其设计和使用的详细文档。 +     +5. 请注意,单个PR不应该太大。如果需要进行大量更改,最好将更改分成几个单独的PR。 +     +6. 创建PR后,将为拉取请求分配一个或多个审阅者。 +     +7. 在合并PR之前,请注意合并提交节点(git commit),包括修复审核反馈,拼写错误,合并。最终提交消息应该清晰简洁。 +     + +如果您的PR包含大量更改,例如组件重构或新组件,请写下有关其设计和使用的详细文档。 + +### 代码审查指南 + +提交者将轮流审查代码,以确保在合并之前及时审核所有PR以及至少一个提交者。如果我们不做我们的工作(有时我们放弃的东西)。和往常一样,我们欢迎志愿者进行代码审查。 + +一些原则: + + - 可读性 - 重要的代码应该有详细记录。 API应该有Javadoc。代码样式应符合现有代码样式。 +     + - 优雅:新功能,类或组件应精心设计。 +     + - 可测试性 - 单元测试用例应涵盖80%的新代码。 +     + - 可维护性 - 遵守我们的[代码规约](https://github.com/alibaba/nacos/blob/master/style/codeStyle.md) ,至少应保持3个月的频率更新。 +     + +### 现在尝试成为一个Committer怎么样? + +一般来说,贡献8个非平凡的补丁并让至少三个不同的人来审查它们(你需要三个人来支持你)。然后请别人提名你。你正在展示你的: + + - 至少8个PR以及与项目相关的问题, +     + - 与团队协作的能力, +     + - 了解项目的代码库和编码风格, +     + - 编写优秀代码的能力(并非最不重要) +     + +当前的提交者通过标签“提名”在Nacos issues里边,让社区了解 + + - 你的名字和姓氏 +     + - 指向您的Git个人资料的链接 +     + - 解释为什么你应该成为一名提交者, +     + - 详细说明您提交的前三的PR和相关issues + +![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/nomination_process.png) diff --git a/src/content/docs/v3.0/zh-cn/archive/feature-list.md b/src/content/docs/v3.0/zh-cn/archive/feature-list.md new file mode 100644 index 00000000000..da01c218d2b --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/feature-list.md @@ -0,0 +1,203 @@ +--- +title: Nacos功能和需求列表 +keywords: [Nacos,功能] +description: Nacos功能和需求列表 +--- + +# Nacos功能和需求列表 + +本文列举了目前Nacos支持的主要功能和一些还未支持的需求排期,方便读者了解目前Nacos已经支持和计划支持的能力,同时所有计划支持的能力都开放给开发者进行认领,本文末有详细的认领教程。 + +在下面的表格中,每个需求都有一个状态的标志,包含若干种取值,各种取值的含义如下: + +1. 状态的取值: + 1. 不支持:该功能还不支持,且没有在现在的时间表里有任何排期; + 1. 排期中:该功能还不支持,但是已经放到了时间表里,有希望在后面的某个版本支持; + 1. 设计中:表示该功能正在方案设计中,方案的草稿和终稿都会开放访问,供大家讨论; + 1. 开发中:表示该功能设计方案已经确定,正在有对应的开发者进行开发,会在接下来的某个版本正式发布; + 1. beta:该功能已经发布,但是未经过大规模的用户验证,还不能确保稳定性; + 1. 稳定:表示已经迭代至少4个版本,目前没有反馈重大缺陷; + + + +# 服务发现 +代码地址:[https://github.com/alibaba/nacos/tree/develop/naming](https://github.com/alibaba/nacos/tree/develop/naming) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 服务注册与发现 | nkorange | 稳定 | 0.1.0 | +| 健康检查(服务端探测、客户端心跳) | xuanyin | 稳定 | 0.1.0 | +| 路由策略(权重、保护阈值、就近访问) | wangjianwei | 稳定 | 0.1.0 | +| | | | | + + + +# 配置管理 +代码地址:[https://github.com/alibaba/nacos/tree/develop/config](https://github.com/alibaba/nacos/tree/develop/config) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 配置管理(发布、修改、查询、监听配置) | yanlinly | 稳定 | 0.1.0 | +| 灰度配置 | yanlinly | 稳定 | 1.1.0 | +| 加密配置 | | 不支持 | | + + + +# 元数据管理 +代码地址:[https://github.com/alibaba/nacos/tree/develop/cmdb](https://github.com/alibaba/nacos/tree/develop/cmdb) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 对接第三方CMDB | nkorange | beta | 0.7.0 | + + + +# 地址服务器 +代码地址:[https://github.com/alibaba/nacos/tree/develop/address](https://github.com/alibaba/nacos/tree/develop/address) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 支持Nacos寻址 | pbting | beta | 1.1.0 | + + + +# Nacos内核 +代码地址:[https://github.com/alibaba/nacos/tree/develop/core](https://github.com/alibaba/nacos/tree/develop/core) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 去除MySQL依赖 | chuntaojun | 设计中 | | +| Raft协议替换成JRaft | chuntaojun | 稳定 | 1.4.1 | +| 异步通知机制统一 | wfnuser | 设计中 | | +| 线程模块统一 | | 排期中 | | +| 传输通道统一 | nkorange | 设计中 | | +| 推送模块统一 | satjd | 设计中 | | +| 启动模块统一 | | 排期中 | | + + + +# 安全与稳定性 +代码地址:[https://github.com/alibaba/nacos](https://github.com/alibaba/nacos) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 命名空间模块下沉为公共模块 | | 排期中 | | +| 权限控制,包括认证与鉴权 | nkorange | 开发中 | 1.2.0 | +| 操作审计与记录 | | 排期中 | | +| 支持加密传输 | | 排期中 | | +| OpenTracing对接 | | 排期中 | | +| metrics收集 | TsingLiang | 稳定 | 0.8.0 | +| 缓存容灾机制统一 | | 排期中 | | +| 支持命令行运维 | | 排期中 | | +| 数据自动备份 | | 排期中 | | +| 限流模块 | | 排期中 | | +| 容量管理 | | 排期中 | | + + + +# 代码质量 +代码地址:[https://github.com/alibaba/nacos](https://github.com/alibaba/nacos) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 工具类模块统一 | | 排期中 | | +| 常量定义统一 | | 排期中 | | +| 异常处理模块统一 | | 排期中 | | +| 日志模块统一 | | 排期中 | | +| 系统参数模块统一 | | 排期中 | | +| 依赖统一 | | 排期中 | | +| 状态码模块统一 | KeRan213539 | 设计中 | | + + + +# 云原生 +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 对接Istio | nkorange | beta | 1.1.4 | +| 对接ConfigMap | | 排期中 | | +| [对接CoreDNS](https://github.com/nacos-group/nacos-coredns-plugin) | JianweiWang | beta | 0.1.0 | +| 对接SPIFFE | | 排期中 | | + + + +# 客户端 +客户端支持包含了目前已知的Nacos多语言客户端及Spring生态的相关客户端,除了Java客户端和Go客户端,其他均为社区热心贡献者开发,如果您有新的语言的客户端,或者有目前已经支持的语言的客户端的另外一个实现,欢迎在github上留言进行登记。 + +| 描述 | 主要开发者 | 状态 | +| :---: | --- | --- | +| [Java客户端](https://github.com/alibaba/nacos/tree/develop/client) | Nacos | 稳定 | +| [Go客户端](https://github.com/nacos-group/nacos-sdk-go) | atlanssia, lzp0412 | 稳定 | +| [Node.js客户端](https://github.com/nacos-group/nacos-sdk-nodejs) | czy88840616, gxcsoccer | 稳定 | +| [Python客户端](https://github.com/nacos-group/nacos-sdk-python) | sanwei | beta | +| [C#客户端](https://github.com/catcherwong/nacos-sdk-csharp) | catcherwong | 推荐 | +| C++客户端 | | | +| PHP客户端 | | | +| [Spring客户端](https://github.com/nacos-group/nacos-spring-project) | chuntaojun | 稳定 | +| [SpringBoot客户端](https://github.com/nacos-group/nacos-spring-boot-project) | chuntaojun | 稳定 | + + + +# Nacos-Docker +代码地址:[https://github.com/nacos-group/nacos-docker](https://github.com/nacos-group/nacos-docker) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| Docker部署Nacos Server | paderlol | 稳定 | 0.1.0 | + + + +# Nacos-K8s +代码地址:[https://github.com/nacos-group/nacos-k8s](https://github.com/nacos-group/nacos-k8s) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| K8s部署Nacos Server | paderlol | 稳定 | 0.1.0 | + + + +# Nacos-Sync +代码地址:[https://github.com/nacos-group/nacos-sync](https://github.com/nacos-group/nacos-sync) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| Nacos与Nacos服务双向同步 | paderlol | 稳定 | 0.1.0 | +| Nacos与Zookeeper服务双向同步 | paderlol | 稳定 | 0.3.0 | +| Nacos与Eureka服务双向同步 | paderlol | 稳定 | 0.3.0 | +| Nacos与Consul服务双向同步 | paderlol | 稳定 | 0.3.0 | + + + +# Nacos官网 +代码地址:[https://github.com/nacos-group/nacos-group.github.io](https://github.com/nacos-group/nacos-group.github.io) + +| 描述 | 主要开发者 | 状态 | 排期 | +| :---: | --- | --- | --- | +| 支持页面内锚链接 | | 不支持 | | + + + + +# 参与共建 + +### 参与共建能得到什么? +参与Nacos共建,你将有机会让你的代码被全中国甚至全世界的用户阅读并使用,同时在成为Nacos Committer后(如何成为Nacos Committer可以参考[手册](https://github.com/alibaba/nacos/blob/develop/CONTRIBUTING.md)),还可以有以下福利: + +- 在Nacos官网[团队页](https://nacos.io/docs/latest/community/nacos-dev/)留名; +- 收到我们带Nacos logo的小礼物,有T恤、水杯、帽衫等等; +- 代表Nacos参加各种线上线下活动,与更多小伙伴交流; +- 更多我们还在筹划中的福利; + + +### 如何共建 +除了在上面列举的功能和需求,其他的在github仓库上打上了[contribution welcome](https://github.com/alibaba/nacos/issues?q=is%3Aopen+is%3Aissue+label%3A%22contribution+welcome%22)或者[help wanted](https://github.com/alibaba/nacos/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)标签的issue,也非常欢迎大家提交代码贡献。加入Nacos 社区核心贡献小组钉钉群23335652,联系群管理员认领需求。 + +大家提PR的时候有几点需要注意下: + +1. 比较重大的特性需要有方案文档:[https://github.com/alibaba/nacos/issues/858](https://github.com/alibaba/nacos/issues/858) +1. 已经阅读并遵守共建规范: [https://github.com/alibaba/nacos/blob/master/CONTRIBUTING.md](https://github.com/alibaba/nacos/blob/master/CONTRIBUTING.md) +1. 使用github账户提交代码,这样大家才会在contributor列表看到自己的名字; +1. commit信息要带上issue id,这样才能在issue里看到PR的进度; +1. 代码中不要出现中文注释,提交前要格式化,并添加必要的集成测试用例和单元测试用例; +1. 提PR前运行mvn -Prelease-nacos clean install -U和mvn clean install -Pit-test成功; + +任务认领原则:每人一次最多领取两个任务,任务PR合并后,即可开始认领下个任务。 diff --git a/src/content/docs/v3.0/zh-cn/archive/roadmap.md b/src/content/docs/v3.0/zh-cn/archive/roadmap.md new file mode 100644 index 00000000000..17d13a7b55a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/roadmap.md @@ -0,0 +1,41 @@ +--- +title: Nacos 规划 +keywords: [Nacos,规划] +description: Nacos 规划 +--- + +# Nacos 规划 + +我们计划从 Nacos 0.8.0 开始将其做到生产可用状态。在这个版本之前,我们建议您仅将其用于开发和测试环境。我们目前的计划是努力在未来6~8个月内将Nacos演进到生产可用的版本。当然计划可能因为各种因素影响而做调整,包括根据社区的声音进行优先级调整等,但整体应该不会超过1年的时间。 + +以下是未来1年我们的主要路线图与计划。 + +## Nacos 1.0 + +主要目标有两个: + +* 构建简单易用的,服务相关的工具集,包括服务发现、配置管理、服务元数据存储、推送、一致性及元数据管理等; +* 与包括[Spring Cloud](https://github.com/alibaba/spring-cloud-alibaba)、[Kubernetes](https://github.com/kubernetes/kubernetes)、[Dubbo](https://github.com/apache/dubbo)等开源生态做无缝的融合与支持,同时给这些生态带来很多面向生产时需要的优秀特性。 + +以下是大致的计划: + +* 0.1 Basic Nacos server and simple OpenAPI and Java SDK; +* 0.2 - 0.3 Seamless support for Kubernetes, Service Mesh and Spring Cloud service discovery and configuration management; +* 0.4 - 0.5 Build an easy-to-use Web UI/User Console; +* 0.6 - 0.7 High availability, ease of use, monitoring and alert etc.; +* 0.8 Production ready; +* 0.9 Large scale performance tuning and benchmark; +* 1.0 GA for large scale production. + + +## Nacos 2.0 + +主要关注在统一服务管理、服务共享及服务治理体系的开放的服务平台的建设上,主要包括两个方面: + +* Dubbo 4.0 + Nacos 2.0 开放的服务平台 + +![Screen Shot 2018-07-11 at 22.32.17.png](https://cdn.yuque.com/lark/0/2018/png/15914/1531319724777-d19b0304-535c-4af9-bee1-f358b6e55d91.png) + +* Kubernetes + Spring Cloud 统一服务管理 + +![Screen Shot 2018-07-11 at 22.35.30.png](https://cdn.yuque.com/lark/0/2018/png/15914/1531319755930-0040e67e-ca05-47b9-9cd0-07ffd7452eae.png) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/archive/sdk-properties.md b/src/content/docs/v3.0/zh-cn/archive/sdk-properties.md new file mode 100644 index 00000000000..dd03fe72f90 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/sdk-properties.md @@ -0,0 +1,50 @@ +--- +title: Nacos 客户端初始化说明 +keywords: [Nacos,客户端,初始化] +description: Nacos 客户端初始化说明 +--- + +Nacos 客户端初始化说明 + +``` + public final static String ENDPOINT = "endpoint"; + public final static String NAMESPACE = "namespace"; + public final static String ACCESS_KEY = "accessKey"; + public final static String SECRET_KEY = "secretKey"; + public final static String SERVER_ADDR = "serverAddr"; + public final static String CONTEXT_PATH = "contextPath"; + public final static String CLUSTER_NAME = "clusterName"; + public final static String ENCODE = "encode"; + +``` +一、客户端可以通过两种方式初始化(二选一,必传) + +1. 通过直接传入Nacos server端信息(ip:port,或者域名)方式 + + `` + SERVER_ADDR server地址,格式为“ip1:port,ip2.port” + `` +2. 通过接入点进行接入获取环境信息 + + ``` + ENDPOINT 接入点 + CLUSTER_NAME 集群名字 + ``` + +二、链接的server的路径(非必传) + +``` +CONTEXT_PATH server根路径 (默认值 nacos) +``` +三、区域隔离(非必传) + +``` +NAMESPACE 名称区域 +``` + +四、鉴权参数(非必传) + +``` +ACCESS_KEY 公钥 +SECRET_KEY 私钥 +``` diff --git a/src/content/docs/v3.0/zh-cn/archive/use-nacos-with-istio.md b/src/content/docs/v3.0/zh-cn/archive/use-nacos-with-istio.md new file mode 100644 index 00000000000..36a05c3dd3a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/use-nacos-with-istio.md @@ -0,0 +1,7 @@ +--- +title: Nacos Istio +keywords: [Nacos,Istio] +description: In plan with Nacos 1.x.x +--- + +**IN PLAN** with Nacos 1.x.x \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/archive/use-nacos-with-springcloud.md b/src/content/docs/v3.0/zh-cn/archive/use-nacos-with-springcloud.md new file mode 100644 index 00000000000..6b0a6b1f9c0 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/archive/use-nacos-with-springcloud.md @@ -0,0 +1,19 @@ +--- +title: Nacos Spring Cloud +keywords: [nacos,Spring Cloud] +description: Nacos Spring Cloud +--- + +# Nacos Spring Cloud + +## 示例 + +* [Nacos Config Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md) +* [Nacos Discovery Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md) + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/community/community.md b/src/content/docs/v3.0/zh-cn/community/community.md new file mode 100644 index 00000000000..515e9bb11a7 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/community/community.md @@ -0,0 +1,30 @@ +--- +title: 社区 +keywords: [社区,联系方式] +description: 社区 +sidebar: + order: 1 +--- + +# 社区 + +## 联系我们 + +### Nacos Gitter-[https://gitter.im/alibaba/nacos](https://gitter.im/alibaba/nacos) +### Nacos 微博-[https://weibo.com/u/6574374908](https://weibo.com/u/6574374908) +### Nacos segmentfault-[https://segmentfault.com/t/nacos](https://segmentfault.com/t/nacos) + +### 邮件列表 + +邮件列表建议讨论任何与Nacos有关的事情。具体请看[参考手册](https://github.com/apache/incubator-dubbo/wiki/Mailing-list-subscription-guide)描述如何订阅我们的邮件列表。 + +* [dev-nacos@googlegroups.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1): 开发邮件列表。如果你在使用或开发Nacos中遇到任何问题,可以在这里提问题。 +* [commits-nacos@googlegroups.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1): 所有提交将被发送到这个邮件列表。如果你有兴趣Nacos的发展,你可以订阅它。 +* [users-nacos@googlegroups.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1): 在Github中[提问题](https://github.com/alibaba/nacos/issues)、更新和[提交需求](https://github.com/alibaba/nacos/pulls)将被发送到这个邮件列表。 +* [nacos\_dev@linux.alibaba.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1). + +### Nacos 钉钉群 + +欢迎加入Nacos钉钉群 + +![nacos_dingding.jpg](/img/nacos_dingding.jpg) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/community/nacos-dev.md b/src/content/docs/v3.0/zh-cn/community/nacos-dev.md new file mode 100644 index 00000000000..bc3ddf7ba27 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/community/nacos-dev.md @@ -0,0 +1,100 @@ +--- +title: 开发者 +keywords: [nacos,开发者,committer] +description: Nacos 开发者页 +sidebar: + order: 2 +--- + +# 开发者 + +## Nacos开发者角色 + +Nacos开发者包含Maintainer、Committer、Contributor三种角色,每种角色的标准定义如下。 + +### Maintainer + +Maintainer是对Nacos项目(包括nacos-group下的项目)的演进和发展做出显著贡献的个人。具体包含以下的标准: + +* 完成多个关键模块或者工程的设计与开发,是项目的核心开发人员; +* 持续的投入和激情,能够积极参与社区、官网、issue、PR等项目相关事项的维护; +* 在社区中具有有目共睹的影响力,能够代表Nacos参加重要的社区会议和活动; +* 具有培养Committer和Contributor的意识和能力; + +### Committer + +Committer是具有Nacos仓库写权限的个人,包含以下的标准: + +* 能够在长时间内做持续贡献issue、PR的个人; +* 参与issue列表的维护及重要feature的讨论; +* 参与code review; + +### Contributor + +Contributor是对Nacos项目有贡献的个人,标准为: + +* 提交过PR并被合并; + +### Nacos开发者权利及义务 + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/333810/1560151750546-03c55f48-c9c5-41cc-8634-88d62c98c5e4.png#align=left&display=inline&height=524&name=image.png&originHeight=704&originWidth=1002&size=181619&status=done&width=746) + +## 开发团队 + +本页面展示了Nacos的开发团队,并且还在持续扩充中。 + +### + + Github 账号 | 名字 | 组织 | 角色 | 邮箱 | +| ----------------------------------------------- |-----|-------------------| --------- | ------------------------------------------------------------ | +| [xuechaos](https://github.com/xuechaos) | 邢学超 | 阿里巴巴 | Maintainer |xingxuechao@alibaba-inc.com | +| [yanlinly](https://github.com/yanlinly) | 李艳林 | 阿里巴巴 | Maintainer | yan.lin2009@163.com | +| [loadchange](https://github.com/loadchange) | 王彦民 | 饿了么 | Maintainer | wym177771@alibaba-inc.com | +| [nkorange](https://github.com/nkorange) | 朱鹏飞 | Shopee | Maintainer | zpf.073@gmail.com | +| [chuntaojun](https://github.com/chuntaojun) | 廖春涛 | 腾讯 | Maintainer| liaochuntao@live.com | +| [paderlol](https://github.com/paderlol) | 张龙 | 蜜獾 | Maintainer | huangbbbaihao@gmail.com | +| [KomachiSion](https://github.com/KomachiSion) | 杨翊 | 阿里巴巴 | Maintainer | yangyi@apache.org | +| [sanxun0325](https://github.com/sanxun0325) | 张斌斌 | 滴滴出行 | Maintainer | bbz17640380550@163.com | +| [zhangjimmy](https://github.com/zhangjimmy) | 张波 | 虎牙直播 | Committer | zhangjimmy@foxmail.com | +| [nanamikon](https://github.com/nanamikon) | 周健 | 虎牙直播 | Committer | nanamikon@gmail.com | +| [lzp0412](https://github.com/lzp0412) | 李志鹏 | 蚂蚁金服 | Committer | 641785844@qq.com | +| [pguo65535](https://github.com/pguo65535) | 郭平 | 阿里巴巴 | Committer | guoping.gp@alibaba-inc.com | +| [hxy1991](https://github.com/hxy1991) | 黄晓禹 | 阿里巴巴 | Committer | huangxiaoyu1018@gmail.com | +| [mingyixu](https://github.com/mingyixu) | 徐小春 | 阿里巴巴 | Committer | xiaochun.xxc@alibaba-inc.com | +| [JianweiWang](https://github.com/JianweiWang) | 王建伟 | 阿里巴巴 | Committer | wangjianwei.nwpu@gmail.com | +| [jameslcj](https://github.com/jameslcj) | 李晨 | 阿里巴巴 | Committer | zhichen.lc@alibaba-inc.com | +| [mercyblitz](https://github.com/mercyblitz) | 马昕曦 | 阿里巴巴 | Committer | taogu.mxx@alibaba-inc.com | +| [fangjian0423](https://github.com/fangjian0423) | 方剑 | 阿里巴巴 | Committer | fangjian.fj@alibaba-inc.com | +| [wyp12](https://github.com/wyp12) | 吴援飘 | 阿里巴巴 | Committer | caogu.wyp@antfin.com | +| [TsingLiang](https://github.com/TsingLiang) | 卿亮 | 阿里巴巴 | Committer | qingliang.ql@alibaba-inc.com | +| [caojiele](https://github.com/caojiele) | 曹杰乐 | 三诺生物 | Committer| caojiele1225@126.com | +| [KeRan213539](https://github.com/KeRan213539) | 邪影 | 云南盛达实业集团有限公司 | Committer | 213539@qq.com | +| [zongtanghu](https://github.com/zongtanghu) | 胡宗棠 | 中国移动云能力中心 | Committer | zongtanghu@hotmail.com | +| [Maijh97](https://github.com/Maijh97) | 麦建辉 | 广州云流区块链科技有限公司 | Committer | xiaomai_h@163.com | +| [wangweizZZ](https://github.com/wangweizZZ) | 王维 | 中国移动云能力中心 | Committer | wwfortunate@gmail.com | +| [horizonzy](https://github.com/horizonzy) | 赵延 | 多点生活(成都)科技有限公司 | Committer| 1060026287@qq.com | +| [catcherwong](https://github.com/catcherwong) | 黄文清 | 广东莲藕健康科技有限公司 | Committer| catcher_hwq@outlook.com | +| [TTTTTAAAAAKKKKEEEENNNN](https://github.com/TTTTTAAAAAKKKKEEEENNNN) | 刘瀚雨 | 腾讯 | Committer | tensai0lhy@gmail.com | +| [haoyann](https://github.com/haoyann) | 严浩 | 成都书声科技有限公司 | Committer | 1064645534@qq.com | +| [shiyiyue1102](https://github.com/shiyiyue1102) | 柳遵飞 | 阿里巴巴 | Committer | liuzunfei@gmail.com | +| [JackSun-Developer](https://github.com/JackSun-Developer) | 孙立 | 阿里巴巴 | Committer | yongyue.sl@alibaba-inc.com | +| [brotherlu-xcq](https://github.com/brotherlu-xcq) | 程露 | 杭州群核信息技术有限公司(酷家乐) | Committer | 1285823170@qq.com | +| [li-xiao-shuang](https://github.com/li-xiao-shuang) | 李晓双 | 滴滴出行 | Committer | 644968328@qq.com | +| [MajorHe1](https://github.com/MajorHe1) | 贺繁 | 微众银行 | Committer | 601023364@qq.com | +| [hujun-w-2](https://github.com/hujun-w-2) | 胡俊 | 小米科技 | Committer | 510830970@qq.com | +| [onewe](https://github.com/onewe) | 毛文超 | 自由开发者 | Committer | 2583021406@qq.com | +| [chenhao26-nineteen](https://github.com/chenhao26-nineteen)| 陈浩 | 小米科技 | Committer | hashmap2018@163.com | +| [shenkonghui](https://github.com/shenkonghui) | 沈孔辉 | 杭州谐云科技有限公司 | Committer | shenkh1992@gmail.com | +| [CherishCai](https://github.com/CherishCai) | 蔡鸿文 | 蚂蚁金服 | Committer | 785427346@qq.com | +| [The-Gamer-01](https://github.com/The-Gamer-01) | 黄乙轩 | 吉首大学 | Committer | 19974361760@163.com | +| [Karsonto](https://github.com/karsonto) | 陶健敏 | 中国工商银行(亚洲) | Committer | karsontao@hotmail.com | +| [Daydreamer-ia](https://github.com/Daydreamer-ia) | 陈亿钦 | 广东工业大学 | Committer | 2296032269@qq.com | +| [shalk](https://github.com/shalk) | 肖焜 | 老虎证券 | Committer | xshalk@163.com | +| [heqingpan](https://github.com/heqingpan) | 何庆攀 | 爱橙科技 | Committer | heqingpan@126.com | +| [godhth](https://github.com/godhth) | 黄天辉 | 中电云计算技术有限公司 | Committer | 1165559068@qq.com | + +### 活跃贡献者 + +| Github 账号 | 名字 | 组织 |角色 |邮箱 | +| ------------------------------------------------|------------| ----------------| -----------|------------------------------------| +| [wfnuser](https://github.com/wfnuser) | 黄清昊 | 字节跳动 | Contributor| wfnuser@hotmail.com | diff --git a/src/content/docs/v3.0/zh-cn/concepts.md b/src/content/docs/v3.0/zh-cn/concepts.md new file mode 100644 index 00000000000..fa82b3d9349 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/concepts.md @@ -0,0 +1,78 @@ +--- +title: Nacos 概念 +keywords: [Nacos,概念] +description: Nacos 概念 +--- + +# Nacos 概念 + +> NOTE: Nacos 引入了一些基本的概念,系统性的了解一下这些概念可以帮助您更好的理解和正确的使用 Nacos 产品。 + +## 地域 +物理的数据中心,资源创建成功后不能更换。 + +## 可用区 +同一地域内,电力和网络互相独立的物理区域。同一可用区内,实例的网络延迟较低。 + +## 接入点 +地域的某个服务的入口域名。 + +## 命名空间 +用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 + +## 配置 +在系统开发过程中,开发者通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成。配置变更是调整系统运行时的行为的有效手段。 + +## 配置管理 +系统配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动。 + +## 配置项 +一个具体的可配置的参数与其值域,通常以 param-key=param-value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。 + +## 配置集 +一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。 + +## 配置集 ID +Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.taobao.tc.refund.log.level)的命名规则保证全局唯一性。此命名规则非强制。 + +## 配置分组 +Nacos 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT\_GROUP 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database\_url 配置和 MQ\_topic 配置。 + +## 配置快照 +Nacos 的客户端 SDK 会在本地生成配置的快照。当客户端无法连接到 Nacos Server 时,可以使用配置快照显示系统的整体容灾能力。配置快照类似于 Git 中的本地 commit,也类似于缓存,会在适当的时机更新,但是并没有缓存过期(expiration)的概念。 + +## 服务 +通过预定义接口网络访问的提供给客户端的软件功能。 + +## 服务名 +服务提供的标识,通过该标识可以唯一确定其指代的服务。 + +## 服务注册中心 +存储服务实例和服务负载均衡策略的数据库。 + +## 服务发现 +在计算机网络上,(通常使用服务名)对服务下的实例的地址和元数据进行探测,并以预先定义的接口提供给客户端进行查询。 + +## 元信息 +Nacos数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。 + +## 应用 +用于标识服务提供方的服务的属性。 + +## 服务分组 +不同的服务可以归类到同一分组。 + +## 虚拟集群 +同一个服务下的所有服务实例组成一个默认集群, 集群可以被进一步按需求划分,划分的单位可以是虚拟集群。 + +## 实例 +提供一个或多个服务的具有可访问网络地址(IP:Port)的进程。 + +## 权重 +实例级别的配置。权重为浮点数。权重越大,分配给该实例的流量越大。 + +## 健康检查 +以指定方式检查服务下挂载的实例 (Instance) 的健康度,从而确认该实例 (Instance) 是否能提供服务。根据检查结果,实例 (Instance) 会被判断为健康或不健康。对服务发起解析请求时,不健康的实例 (Instance) 不会返回给客户端。 + +## 健康保护阈值 +为了防止因过多实例 (Instance) 不健康导致流量全部流向健康实例 (Instance) ,继而造成流量压力把健康实例 (Instance) 压垮并形成雪崩效应,应将健康保护阈值定义为一个 0 到 1 之间的浮点数。当域名健康实例数 (Instance) 占总服务实例数 (Instance) 的比例小于该值时,无论实例 (Instance) 是否健康,都会将这个实例 (Instance) 返回给客户端。这样做虽然损失了一部分流量,但是保证了集群中剩余健康实例 (Instance) 能正常工作。 diff --git a/src/content/docs/v3.0/zh-cn/contribution/contributing-flow.md b/src/content/docs/v3.0/zh-cn/contribution/contributing-flow.md new file mode 100644 index 00000000000..0d7f28b0d9f --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/contribution/contributing-flow.md @@ -0,0 +1,100 @@ +--- +title: 贡献流程 +keywords: [贡献,源码] +description: 此贡献流程适用于所有的Nacos社区内容,包括但不限于`Nacos`、`Nacos wiki/doc`、`Nacos SDK`。 +sidebar: + order: 2 +--- + +# Nacos 贡献流程 + +此贡献流程适用于所有的Nacos社区内容,包括但不限于`Nacos`、`Nacos wiki/doc`、`Nacos SDK`。 + +以下以贡献`Nacos`为例,详细说明贡献流程。 + +## 1. fork Alibaba/Nacos 项目到您的github库 + +## 2. 克隆或下载您fork的Nacos代码仓库到您本地 + +``` +git clone ${your fork nacos repo address} + +cd nacos +``` + +## 3. 添加Alibaba/Nacos仓库为upstream仓库 + +``` +git remote add upstream https://github.com/alibaba/nacos.git + +git remote -v + + origin ${your fork nacos repo address} (fetch) + origin ${your fork nacos repo address} (push) + upstream https://github.com/alibaba/nacos.git (fetch) + upstream https://github.com/alibaba/nacos.git (push) + +git fetch origin +git fetch upstream +``` + +## 4. 选择一个开发的基础分支,通常是upstream/develop,并基于此创建一个新的分支 + +``` +(从远程仓库拉取分支到本地) +git checkout -b upstream-develop upstream/develop + +(从本地分支创建开发分支, 通常以该PR对应的issue号作为开发分支名) +git checkout -b develop-issue#${issue-number} + +``` + +## 5. 在本地新建的开发分支上进行各种修改 + +首先请保证您阅读并正确设置`Nacos code style`, 相关内容请阅读[Nacos 代码规约](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md) 。 + +修改时请保证该分支上的修改**仅和issue相关**,并尽量细化,做到**一个分支只修改一件事,一个PR只修改一件事**。 + +同时,您的提交记录请尽量使用英文描述,主要以**谓 + 宾**进行描述,如:`Fix xxx problem/bug`。少量简单的提交可以使用`For xxx`来描述,如:`For codestyle`。 如果该提交和某个ISSUE相关,可以添加ISSUE号作为前缀,如:`For #10000, Fix xxx problem/bug`。 + +## 6. Rebase 基础分支和开发分支 + +您修改的时候,可能别人的修改已经提交并被合并,此时可能会有冲突,这里请使用rebase命令进行合并解决,主要有2个好处: + +1. 您的提交记录将会非常优雅,不会出现`Merge xxxx branch` 等字样 +2. rebase后您分支的提交日志也是一条单链,基本不会出现各种分支交错的情况,回查时更轻松 + +``` +git fetch upstream + +git rebase -i upstream/develop + +``` + +OR + +``` +git checkout upstream-develop +git pull +git checkout develop-issue#${issue-number} +git rebase -i upstream-develop +``` + +**如果您使用的是Intellij IDEA**,建议使用IDE的版本管理模块,有可视化界面,更方便解决冲突和squash操作 + +## 7. 将您开发完成rebase后的分支,上传到您fork的仓库 + +``` +git push origin develop-issue#${issue-number} +``` + +## 8. 按照拉取请求模板中的清单创建Pull Request + +[拉取请求模板](./pull-request.md) + +Nacos社区将会Review您的Pull Request,并可能提出修改意见,您可以根据修改意见回到步骤5进行修改,并使用步骤6进行重新提交。 + +**如果您再次提交时提示您存在提交记录冲突,这是因为您修改期间,有其他的修改合并到了基础分支,您rebase后,commit ID发生了变化,此时需要force push 到您的fork分支上即可** + +## 9. 如果没有问题,Nacos社区将会把您的修改合并到基础分支中,恭喜您成为Nacos的官方贡献者。 + diff --git a/src/content/docs/v3.0/zh-cn/contribution/contributing.md b/src/content/docs/v3.0/zh-cn/contribution/contributing.md new file mode 100644 index 00000000000..c286342063d --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/contribution/contributing.md @@ -0,0 +1,101 @@ +--- +title: 如何贡献 +keywords: [贡献,源码] +description: 我们非常欢迎您的贡献和加入,无论是微不足道的清理或大的新功能。 +sidebar: + order: 1 +--- + +# 如何贡献 + +我们非常欢迎您的贡献和加入,无论是微不足道的清理或大的新功能。我们希望为每个编程语言提供高质量、有良好文档的代码。 + +这也不是代码是唯一有贡献项目的方式。我们非常重视文档、与其他项目的集成,并欣然接受这些方面的改进。 + +## 联系我们 + +#### Nacos Gitter-[https://gitter.im/alibaba/nacos](https://gitter.im/alibaba/nacos) +#### Nacos 微博-[https://weibo.com/u/6574374908](https://weibo.com/u/6574374908) +#### Nacos segmentfault-[https://segmentfault.com/t/nacos](https://segmentfault.com/t/nacos) + +#### 邮件列表 + +邮件列表建议讨论任何与Nacos有关的事情。具体请看[参考手册](https://github.com/apache/incubator-dubbo/wiki/Mailing-list-subscription-guide)描述如何订阅我们的邮件列表。 + +* [dev-nacos@googlegroups.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1): 开发邮件列表。如果你在使用或开发Nacos中遇到任何问题,可以在这里提问题。 +* [commits-nacos@googlegroups.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1): 所有提交将被发送到这个邮件列表。如果你有兴趣Nacos的发展,你可以订阅它。 +* [users-nacos@googlegroups.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1): 在Github中[提问题](https://github.com/alibaba/nacos/issues)、更新和[提交需求](https://github.com/alibaba/nacos/pulls)将被发送到这个邮件列表。 +* [nacos\_dev@linux.alibaba.com](https://lark.alipay.com/nacos/nacosdocs/vl19q1). + +## 贡献代码 + +### 贡献代码须知 + +请贡献代码时候,请先确认和检查以下内容: + +#### 阅读Nacos[代码规约](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md) ,并根据指引设置IDE的codeStyle及校验插件。 + +#### 如果变化不大,请编写一些覆盖新功能的单元测试。 + +#### 如果你正在引入一个全新的特性或API,那么首先启动wiki并在基本设计上达成共识,再开始投入。 + +### 贡献流程 + +这是贡献者的大致工作流程: + +* fork当前存储github库。 +* 创建一个分支,作为贡献的基础,这通常是develop分支。 +* 做出一些变更提交。 +* 确保提交消息的格式正确(见下文)。 +* 推送变更到你的fork仓库中。 +* 按照[拉取请求模板](./pull-request.md)中的清单进行操作。 +* 在发送拉取请求之前,请将您的fork仓库与远程存储库同步,这将使您的拉取请求变得简单明了。详情见下面的指南: +``` +git remote add upstream git@github.com:alibaba/nacos.git +git fetch upstream +git rebase upstream/master +git checkout -b your_awesome_patch +... add some work +git push origin your_awesome_patch +``` +* 提交pull request 到 alibaba/nacos,等待回复。如果回复的慢,请无情的催促。 + +* 详细的贡献流程可参考[贡献流程](./contributing-flow.md) + +## 贡献文档 + +### 贡献文档须知 + +请贡献文档时候,请先确认和检查以下内容: + +#### 已确认过文档确实有误或存在缺失。 + +#### 熟悉[Markdown](https://www.markdownguide.org/getting-started) 。 + +#### 熟悉[docsite](https://github.com/txd-team/docsite) ,至少能够根据[官方文档README.md](https://github.com/nacos-group/nacos-group.github.io) 的引导完成本地调试 + +### 贡献流程 + +可参考[贡献流程](./contributing-flow.md) + +## 成为提交者 + +我们会积极纳入新的贡献者。我们更关注的是一系列的持续贡献,良好的品味和对项目维护的持续兴趣。如果你想成为一个提交者(Committer),请让一个现有的提交者(Committer)知道,他们会帮助你通过贡献加入我们。 + +现在,我们有几个重要的贡献点: + +#### Wiki & JavaDoc +#### Nacos Console +#### Nacos SDK(C++\.Net\Php\Python\Go\Node.js) + +#### 前提 + +如果你想贡献以上的项,请你必须遵守我们的一些先决条件: + +##### 可读性,一个API必须具有JavaDoc,一些非常重要的方法也必须有JavaDoc。 + +##### 可测性,关于测试过程的单元测试覆盖率(80%) + +##### 可维护性,可满足我们的[代码规约](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md) ,以及至少3个月的更新频率 + +##### 可部署性,我们可以鼓励您部署到[maven repository](http://search.maven.org/) diff --git a/src/content/docs/v3.0/zh-cn/contribution/how-to-reporting-bugs.md b/src/content/docs/v3.0/zh-cn/contribution/how-to-reporting-bugs.md new file mode 100644 index 00000000000..39e0dccfc15 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/contribution/how-to-reporting-bugs.md @@ -0,0 +1,30 @@ +--- +title: 如何提交问题报告 +keywords: [提交,问题报告] +description: 如何提交问题报告 +sidebar: + order: 4 +--- + +# 如何提交问题报告 + +如果Nacos项目的任何部分存在问题或文档问题,请通过[opening an issue](https://github.com/alibaba/nacos/issues/new)告诉我们。我们非常认真地对待错误和错误,在产品面前没有不重要的问题。不过在创建错误报告之前,请检查是否存在报告相同问题的issues。 + +为了使错误报告准确且易于理解,请尝试创建以下错误报告: + +- 具体到细节。包括尽可能多的细节:哪个版本,什么环境,什么配置等。如果错误与运行Nacos服务器有关,请附加Nacos日志(具有Nacos配置的起始日志尤为重要)。 + +- 可复现。包括重现问题的步骤。我们理解某些问题可能难以重现,请包括可能导致问题的步骤。如果可能,请将受影响的Nacos数据目录和堆栈strace附加到错误报告中。 + +- 不重复。不要复制现有的错误报告。 + +在创建错误报告之前,最好阅读下[Elika Etemad关于提交好错误报告的文章](http://fantasai.inkedblade.net/style/talks/filing-good-bugs/),相信 会给你启发。 + +我们可能会要求您提供更多信息以查找错误。将关闭重复的错误报告。 + +[nacos-issue](https://github.com/alibaba/nacos/issues/new) +[filing-good-bugs](http://fantasai.inkedblade.net/style/talks/filing-good-bugs/) + +# 如何提交安全问题 + +如果您发现Nacos项目中存在安全问题,请通过[ASRC(Alibaba Security Response Center阿里安全响应中心)]( https://security.alibaba.com) 告知我们。 diff --git a/src/content/docs/v3.0/zh-cn/contribution/pull-request.md b/src/content/docs/v3.0/zh-cn/contribution/pull-request.md new file mode 100644 index 00000000000..08c8864b121 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/contribution/pull-request.md @@ -0,0 +1,32 @@ +--- +title: 提交需求模板 +keywords: [pull request,模板] +description: 提交需求模板 +sidebar: + order: 3 +--- + +# 提交需求模板 + +请不要只提交需求而不创建问题。 + +## What is the purpose of the change + +XXXXX + +## Brief changelog + +XX + +## Verifying this change + +XXXX + +Follow this checklist to help us incorporate your contribution quickly and easily: + +* [ ] Make sure there is a Github issue filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. +* [ ] Format the pull request title like `[ISSUE #123] Fix UnknownException when host config not exist`. Each commit in the pull request should have a meaningful subject line and body. +* [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. +* [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in [test module](https://github.com/apache/rocketmq/tree/master/test). +* [ ] Run `mvn -B clean apache-rat:check findbugs:findbugs` to make sure basic checks pass. Run `mvn clean install -DskipITs` to make sure unit-test pass. Run `mvn clean test-compile failsafe:integration-test` to make sure integration-test pass. +* [ ] If this contribution is large, please file an [Apache Individual Contributor License Agreement](http://www.apache.org/licenses/#clas). \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/ecology/img/k8s-sync.jpg b/src/content/docs/v3.0/zh-cn/ecology/img/k8s-sync.jpg new file mode 100644 index 0000000000000000000000000000000000000000..78205dd947923f6970e7698c1e5b83132f0e7c48 GIT binary patch literal 149811 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 literal 0 HcmV?d00001 diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-sync.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-sync.md new file mode 100644 index 00000000000..c28f8e3c2fd --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-sync.md @@ -0,0 +1,241 @@ +--- +title: NacosSync 用户手册 +keywords: [NacosSync,迁移,用户手册] +description: NacosSync 迁移用户手册 +sidebar: + order: 5 +--- + +# NacosSync 用户手册 + +# 手册目标 +* 理解 NacosSync 组件 +* 启动 NacosSync 服务 +* 通过一个简单的例子,演示如何将注册到 Zookeeper 的 Dubbo 客户端迁移到 Nacos。 + +# 介绍 +* NacosSync是一个支持多种注册中心的同步组件,基于Spring boot开发框架,数据层采用Spring Data JPA,遵循了标准的JPA访问规范,支持多种数据源存储,默认使用Hibernate实现,更加方便的支持表的自动创建更新 +* 使用了高效的事件异步驱动模型, 支持多种自定义事件,使得同步任务处理的延时控制在3s,8C16G的单机能够支持6K的同步任务 +* NacosSync除了单机部署,也提供了高可用的集群部署模式,NacosSync是无状态设计,将任务等状态数据迁移到了数据库,使得集群扩展非常方便 +* 抽象出了Sync组件核心接口,通过注解对同步类型进行区分,使得开发者可以很容易的根据自己需求,去扩展不同注册中心,目前已支持的同步类型: + * Nacos数据同步到Nacos + * Zookeeper数据同步到Nacos + * Nacos数据同步到Zookeeper + * Eureka数据同步到Nacos + * Consul数据同步到Nacos + +## 系统模块架构: +![image.png](https://img.alicdn.com/tfs/TB12VPaJVzqK1RjSZSgXXcpAVXa-886-752.png)
控制台
提供了精简Web操作控制台,支持国际化:
+ +### 同步任务管理页面 +![](https://img.alicdn.com/tfs/TB1eSYyJ5LaK1RjSZFxXXamPFXa-2866-1064.png) + +### 注册中心管理页面 + +## ![image.png](https://img.alicdn.com/tfs/TB1e_rdJ7voK1RjSZFNXXcxMVXa-2876-1124.png) + +## 使用场景: +* 多个网络互通的Region之间服务共享,打破Region之间的服务调用限制 + +![image.png](https://img.alicdn.com/tfs/TB1Mo6yJ4jaK1RjSZKzXXXVwXXa-1136-798.png) + +* 双向同步功能,支持Dubbo+Zookeeper服务平滑迁移到Dubbo+Nacos,享受Nacos更加优质的服务 + +![image.png](https://img.alicdn.com/tfs/TB1Dza8J9zqK1RjSZPxXXc4tVXa-1728-838.png) + +# 使用流程 + +## 准备工作 +启动服务之前,你需要安装下面的服务: +* 64bit OS: Linux/Unix/Mac/Windows supported, Linux/Unix/Mac recommended. +* 64bit JDK 1.8+: [downloads](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html), [JAVA_HOME settings](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/). +* Maven 3.2.x+: [downloads](https://maven.apache.org/download.cgi), [settings](https://maven.apache.org/settings.html). +* MySql 5.6.+ + +## 获取安装包 +有两种方式可以获得 NacosSync 的安装包: +* 直接下载 NacosSync 的二进制安装包:[nacosSync.${version}.zip](https://github.com/nacos-group/nacos-sync/releases) +* 从 GitHub 上下载 NacosSync 的源码进行构建 + +Package: +```basic +cd nacosSync/ +mvn clean package -U +``` + +目标文件的路径: +```basic +nacos-sync/nacossync-distribution/target/nacosSync.${version}.zip +``` + +解压安装包之后,工程的文件目录结构: +```basic +nacosSync +├── LICENSE +├── NOTICE +├── bin +│   ├── nacosSync.sql +│   ├── shutdown.sh +│   └── startup.sh +├── conf +│   ├── application.properties +│   └── logback-spring.xml +├── logs +└── nacosSync-server.${version}.jar +``` + +## 初始化数据库 + +系统默认配置的数据库是Mysql,也能支持其他的关系型数据库。 +1.建库,缺省的数据库名字为“nacos_Sync”。 +2.数据库表不需要单独创建,默认使用了hibernate的自动建表功能。 +3.如果你的环境不支持自动建表,可以使用系统自带的sql脚本建表,脚本放在bin目录下。 + +## 数据库配置 + +数据库的配置文件放在`conf/application.properties`中: +```basic +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/nacos_sync?characterEncoding=utf8 +spring.datasource.username=root +spring.datasource.password=root +``` + +## 启动服务器 + +```bash +$ nacosSync/bin: +sh startup.sh restart +``` + +## 检查系统状态 + +1.检查系统日志 + +日志的路径在`nacosSync/logs/nacosSync.log`,检查是否有异常信息。 + +2.检查系统端口 + +缺省的系统端口是`8081`,你可以自己定义在`application.properties`中。 + +```bash +$netstat -ano|grep 8081 +tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN off (0.00/0/0) +``` + +## 控制台 + +访问路径: +``` +http://127.0.0.1:8081/#/serviceSync +``` +![image.png](https://img.alicdn.com/tfs/TB1EKbkJ3HqK1RjSZFEXXcGMXXa-2866-606.png) + +如果检查没有问题,NacosSync 已经正常启动了,NacosSync 的部署结构:![image.png](https://img.alicdn.com/tfs/TB107nfJ9zqK1RjSZFjXXblCFXa-1412-342.png) + +## 开始迁移 + +### 迁移信息 + +Dubbo服务的部署信息:![image.png](https://img.alicdn.com/tfs/TB1Ci_eJ4TpK1RjSZR0XXbEwXXa-938-700.png) + +迁移的服务: + +| Service Name | Version | Group Name | +| --- | --- | --- | +| com.alibaba.nacos.api.DemoService | 1.0.0 | zk | + +### 添加注册中心集群信息 + +1.点击左侧导航栏中的“集群配置”按钮,新增加一个集群,先增加一个Zookeeper集群,选择集群类型为ZK。 +![image.png](https://img.alicdn.com/tfs/TB1oJDnJ7voK1RjSZFwXXciCFXa-2870-1130.png) + +> 注意:集群名字可以自定义,但是一旦确认,不能被修改,否则基于此集群增加的任务,在 NacosSync 重启后,将不会恢复成功。 + +2.同样的步骤,增加Nacos集群。 +![image.png](https://img.alicdn.com/tfs/TB1HQPhJVzqK1RjSZFCXXbbxVXa-2846-1042.png) + +3.添加完成后,可以在列表中查询到: +![image.png](https://img.alicdn.com/tfs/TB1AX6fJVYqK1RjSZLeXXbXppXa-2864-824.png) + +### 添加同步任务 + +1.增加一个同步任务,从Zookeeper集群同步到Nacos集群,同步的粒度是服务,Zookeeper集群则称为源集群,Nacos集群称为目标集群。 +![imagesd.png](https://img.alicdn.com/tfs/TB1tF_fJVYqK1RjSZLeXXbXppXa-2838-1138.png) + +添加完成之后,可以在服务同步列表中,查看已添加的同步任务: +![image.png](https://img.alicdn.com/tfs/TB1l6uJJ9zqK1RjSZPcXXbTepXa-2824-570.png) + +2.同步完成之后,检查下数据是否同步成功到Nacos集群,可以通过Nacos的控制台进行查询。 +![image.png](https://img.alicdn.com/tfs/TB1tPneJ4TpK1RjSZR0XXbEwXXa-2872-828.png) + +3.此刻,数据已经成功从Zookeeper集群同步到了Nacos集群,部署结构如下: +![image.png](https://img.alicdn.com/tfs/TB14kriJ6TpK1RjSZKPXXa3UpXa-1724-772.png) + +### Dubbo 客户端连接到 Nacos 注册中心 + +#### Dubbo Consumer客户端迁移 + +Dubbo 已经支持Nacos注册中心,支持的版本为2.5+,需要增加一个Nacos注册中心的Dubbo扩展插件依赖: +```basic + + com.alibaba + dubbo-registry-nacos + 0.0.2 + +``` + +增加Nacos客户端的依赖: +```basic + + com.alibaba.nacos + nacos-client + 0.6.2 + +``` + +配置Dubbo Consumer的Dubbo配置文件`consumer.yaml`,让客户端能够找到Nacos集群。 +```basic +spring: + application: +name: dubbo-consumer +demo: + service: + version: 1.0.0 + group: zk +dubbo: + registry: + address: nacos://127.0.0.1:8848 +``` + +不需要修改代码,配置更新完毕之后,你就可以重启你的应用,使之生效了。 + +Consumer发布完成之后,目前的部署结构如下: +![image.png](https://img.alicdn.com/tfs/TB181fkJ3HqK1RjSZFEXXcGMXXa-1734-878.png) + +#### Dubbo Provider迁移 + +在升级Provider之前,你需要确保该Provider发布的服务,都已经配置在 NacosSync 中,同步的方式为从Nacos同步到Zookeeper,因为Provider升级连接到Nacos之后,需要确保老的Dubbo Consumer客户端能够在Zookeeper上订阅到该Provider的地址,现在,我们增加一个同步任务: +![image.png](https://img.alicdn.com/tfs/TB1pdDnJ7voK1RjSZFwXXciCFXa-2872-1060.png) + +![image.png](https://img.alicdn.com/tfs/TB19Ey_J6DpK1RjSZFrXXa78VXa-2842-660.png) + +> 注意:Nacos服务同步到Zookeeper,不需要填写版本号,你在选择源集群的时候,版本号的输入框会自动隐藏掉。 + +同步任务完成后,你就可以升级Provider了,升级Provider的方法,参考升级Consumer的步骤。 + +### 新的部署结构 +* 在升级的过程中,会有新老版本的客户端同时存在,部署结构如下: + +![image.png](https://img.alicdn.com/tfs/TB14Y_iJ3HqK1RjSZFPXXcwapXa-1728-838.png) + +* 所有的客户端迁移完成之后,部署结构如下: + +![image.png](https://img.alicdn.com/tfs/TB1Cg2dJYvpK1RjSZPiXXbmwXXa-1466-864.png) + +现在,Zookeeper集群,NacosSync集群就可以下线了。 + +### 注意事项 +* 同步任务添加之后,需要确保下服务是否成功同步到目标集群,可以通过目标集群的控制台进行查询。 +* NacosSync 支持高可用集群模式部署,你只需要把数据库配置成同一个即可。 +* 如果梳理不清楚订阅和发布的服务,建议可以把服务都做双向同步。 +* Dubbo 客户端目前不支持 Nacos 的权重功能,如果你用到了权重功能,需要重新考虑一下方案是否合适。 diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-coredns.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-coredns.md new file mode 100644 index 00000000000..c2cb8814e49 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-coredns.md @@ -0,0 +1,54 @@ +--- +title: Nacos 融合CoreDNS 下发DNS域名 +keywords: [CoreDNS, DNS-F] +description: Nacos 融合CoreDNS 下发DNS域名 +sidebar: + order: 6 +--- + +# Nacos DNS 使用手册 +本插件提供了一个基于CoreDNS的DNS-F客户端,可以将Nacos上注册的服务导出为DNS域名。 本DNS-F客户端是应用程序进程旁边的一个专用代理进程(side car),可以将服务名作为DNS域名查询请求转发到本客户端,提供服务发现的功能。 + +## 快速开始 +要构建和运行本 nacos-coredns 插件,操作系统必须是 Linux 或 Mac。 另外,请确保您的 nacos 服务端版本为2.2或更高级版本,以及 golang 版本为 1.17 或更高级版本, 并且必须正确配置 golang 环境(GOPATH、GOROOT)。因为需要支持 nacos2.x 版本的gRPC连接功能和 go mod 功能。 + +## 构建 +``` +git clone https://github.com/nacos-group/nacos-coredns-plugin.git +cp nacos-coredns-plugin/bin/build.sh ~/ +cd ~/ +sh build.sh +``` + +## 配置 +运行本 nacos-coredns 插件,您需要一个配置文件。 一个标准的配置文件如下: +``` +. { + log + nacos { + nacos_namespaceId public + nacos_server_host 127.0.0.1:8848 + } + forward . /etc/resolv.conf +} +``` +- forward:未在 nacos 注册的域名将被转发到upstream。 +- nacos_namespaceId:nacos namespaceId,默认为public。 +- nacos_server_host:nacos 服务端的IP地址和端口,如果有两个或多个 nacos 服务端,用逗号分隔 + +## 运行 +1. 首先需要部署一个nacos服务端。 [部署参考](https://github.com/alibaba/nacos) +2. 其次,在nacos上注册服务。 +3. 然后输入配置文件 **($path_to_corefile)** 和指定端口 **($dns_port)** ,运行本插件。 +``` +$GOPATH/src/coredns/coredns -conf $path_to_corefile -dns.port $dns_port +``` +![](https://cdn.nlark.com/yuque/0/2022/png/29425667/1663504581023-95437fee-0e3d-4b6a-851c-44a352dedd81.png) + +## 服务发现例子 +- 输入服务名 **($nacos_service_name)** ,以及插件的IP地址 **($dns_ip)** 和端口 **($dns_port)** + +``` +dig $nacos_service_name @$dns_ip -p $dns_port +``` +![](https://cdn.nlark.com/yuque/0/2022/png/29425667/1663504588231-341b38fe-da55-41eb-a24b-e3752b86faa4.png) diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-dubbo.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-dubbo.md new file mode 100644 index 00000000000..6a63a0023cb --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-dubbo.md @@ -0,0 +1,481 @@ +--- +title: Dubbo 融合 Nacos 成为注册中心 +keywords: [Dubbo,Nacos,注册中心] +description: Dubbo 融合 Nacos 成为注册中心 +sidebar: + order: 1 +--- + +# Dubbo 融合 Nacos 成为注册中心 + +Nacos 作为 Dubbo 生态系统中重要的注册中心实现,本文将会介绍如何进行 Dubbo 对接 Nacos 注册中心的工作。 + +## 预备工作 + +请确保后台已经启动 Nacos 服务,可先行参考 [Nacos 快速入门](../quickstart/quick-start.mdx)。 + +## 快速上手 + +Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单,大致步骤可分为“增加 Maven 依赖”以及“配置注册中心“。 + +### 增加 Maven 依赖 + +只需要依赖Dubbo客户端即可,关于推荐的使用版本,请参考Dubbo官方文档或者咨询Dubbo开发人员: + +```xml + + + ... + + + com.alibaba + dubbo + 3.0.5 + + + + + com.alibaba + dubbo-registry-nacos + 3.0.5 + + + + + com.alibaba.spring + spring-context-support + 1.0.11 + + ... + + +``` + +### 配置注册中心 + +假设您 Dubbo 应用使用 Spring Framework 装配,将有两种配置方法可选,分别为:[Dubbo Spring 外部化配置](https://mercyblitz.github.io/2018/01/18/Dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/)以及 Spring XML 配置文件以及,笔者强烈推荐前者。 + +### Dubbo Spring 外部化配置 + +Dubbo Spring 外部化配置是由 Dubbo `2.5.8` 引入的新特性,可通过 Spring `Environment` 属性自动地生成并绑定 Dubbo 配置 Bean,实现配置简化,并且降低微服务开发门槛。 + +假设您 Dubbo 应用的使用 Nacos 作为注册中心,并且其服务器 IP 地址为:`10.20.153.10`,同时,该注册地址作为 Dubbo 外部化配置属性存储在 `dubbo-config.properties` 文件,如下所示: + +```properties +## application +dubbo.application.name = your-dubbo-application + +## Nacos registry address +dubbo.registry.address = nacos://10.20.153.10:8848 +##如果要使用自己创建的命名空间可以使用下面2种方式 +#dubbo.registry.address = nacos://10.20.153.10:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +... +``` +随后,重启您的 Dubbo 应用,Dubbo 的服务提供和消费信息在 Nacos 控制台中可以显示: + +![image-20181213103845976-4668726.png | left | 747x284](https://img.alicdn.com/tfs/TB1n6m7zMTqK1RjSZPhXXXfOFXa-2784-1058.png "") + +如图所示,服务名前缀为 `providers:` 的信息为服务提供者的元信息,`consumers:` 则代表服务消费者的元信息。点击“**详情**”可查看服务状态详情: + +![image-20181213104145998-4668906.png | left | 747x437](https://img.alicdn.com/tfs/TB1vZzfzQzoK1RjSZFlXXai4VXa-2714-1588.png "") + +如果您正在使用 Spring XML 配置文件装配 Dubbo 注册中心的话,请参考下一节。 + +### Spring XML 配置文件 + +同样,假设您 Dubbo 应用的使用 Nacos 作为注册中心,并且其服务器 IP 地址为:`10.20.153.10`,并且装配 Spring Bean 在 XML 文件中,如下所示: + +```xml + + + + + + + + + + + ... + +``` + +重启 Dubbo 应用后,您同样也能发现服务提供方和消费方的注册元信息呈现在 Nacos 控制台中: + +![image-20181213113049185-4671849.png | left | 747x274](https://img.alicdn.com/tfs/TB1zl2dzQPoK1RjSZKbXXX1IXXa-2784-1022.png "") + +您是否觉得配置或切换 Nacos 注册中心超级 Easy 呢?如果您仍旧意犹未尽或者不甚明白的话,可参考以下完整的示例。 + +## 完整示例 + +以上图片中的元数据源于 Dubbo Spring 注解驱动示例以及 Dubbo Spring XML 配置驱动示例,下面将分别介绍两者,您可以选择自己偏好的编程模型。在正式讨论之前,先来介绍两者的预备工作,因为它们皆依赖 Java 服务接口和实现。同时,**请确保本地(`127.0.0.1`)环境已启动 Nacos 服务**。 + +### 示例接口与实现 +完整代码归档位置: +https://github.com/nacos-group/nacos-examples/tree/master/nacos-dubbo-example + +首先定义示例接口,如下所示: + +```java +package com.alibaba.nacos.example.dubbo.service; + +public interface DemoService { + String sayName(String name); +} +``` + +提供以上接口的实现类: + +```java + +package com.alibaba.nacos.example.dubbo.service; + +import com.alibaba.dubbo.config.annotation.Service; +import com.alibaba.dubbo.rpc.RpcContext; +import org.springframework.beans.factory.annotation.Value; + +/** + * Default {@link DemoService} + * https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/ + * @since 2.6.5 + */ +@Service(version = "${demo.service.version}") +public class DefaultService implements DemoService { + + @Value("${demo.service.name}") + private String serviceName; + + public String sayName(String name) { + RpcContext rpcContext = RpcContext.getContext(); + return String.format("Service [name :%s , port : %d] %s(\"%s\") : Hello,%s", + serviceName, + rpcContext.getLocalPort(), + rpcContext.getMethodName(), + name, + name); + } +} +``` + +接口与实现准备妥当后,下面将采用注解驱动和 XML 配置驱动各自实现。 + +### Spring 注解驱动示例 + +Dubbo `2.5.7` 重构了 Spring 注解驱动的编程模型。 + +#### 服务提供方注解驱动实现 + +- 定义 Dubbo 提供方外部化配置属性源 - `provider-config.properties` + +```properties +## application +dubbo.application.name = dubbo-provider-demo + +## Nacos registry address +dubbo.registry.address = nacos://127.0.0.1:8848 +##如果要使用自己创建的命名空间可以使用下面2种方式 +#dubbo.registry.address = nacos://127.0.0.1:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 + +## Dubbo Protocol +dubbo.protocol.name = dubbo +dubbo.protocol.port = -1 + +# Provider @Service version +demo.service.version=1.0.0 +demo.service.name = demoService + +dubbo.application.qosEnable=false + +``` + + +- 实现服务提供方引导类 - `DemoServiceProviderBootstrap` + +```java +package com.alibaba.nacos.example.dubbo.provider; + +import com.alibaba.nacos.example.dubbo.service.DemoService; +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.PropertySource; +import java.io.IOException; + +/** + * {@link DemoService} provider demo + * https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/ + */ +@EnableDubbo(scanBasePackages = "com.alibaba.nacos.example.dubbo.service") +@PropertySource(value = "classpath:/provider-config.properties") +public class DemoServiceProviderBootstrap { + + public static void main(String[] args) throws IOException { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(DemoServiceProviderBootstrap.class); + context.refresh(); + System.out.println("DemoService provider is starting..."); + System.in.read(); + } +} + +``` + +其中注解 `@EnableDubbo` 激活 Dubbo 注解驱动以及外部化配置,其 `scanBasePackages` 属性扫描指定 Java 包,将所有标注 `@Service` 的服务接口实现类暴露为 Spring Bean,随即被导出 Dubbo 服务。 + + `@PropertySource` 是 Spring Framework 3.1 引入的标准导入属性配置资源注解,它将为 Dubbo 提供外部化配置。 + + +#### 服务消费方注解驱动实现 + +- 定义 Dubbo 消费方外部化配置属性源 - `consumer-config.properties` + +```properties +## Dubbo Application info +dubbo.application.name = dubbo-consumer-demo + +## Nacos registry address +dubbo.registry.address = nacos://127.0.0.1:8848 +##如果要使用自己创建的命名空间可以使用下面2种方式 +#dubbo.registry.address = nacos://127.0.0.1:8848?namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 +#dubbo.registry.parameters.namespace=5cbb70a5-xxx-xxx-xxx-d43479ae0932 + +# @Reference version +demo.service.version= 1.0.0 + +dubbo.application.qosEnable=false +``` + +同样地,`dubbo.registry.address` 属性指向 Nacos 注册中心,其他 Dubbo 服务相关的元信息通过 Nacos 注册中心获取。 + + + +- 实现服务消费方引导类 - `DemoServiceConsumerBootstrap` + +```java +package com.alibaba.nacos.example.dubbo.consumer; + + +import com.alibaba.nacos.example.dubbo.service.DemoService; +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.apache.dubbo.config.annotation.DubboReference; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.PropertySource; +import javax.annotation.PostConstruct; +import java.io.IOException; + +/** + * {@link DemoService} consumer demo + * https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/ + */ +@EnableDubbo +@PropertySource(value = "classpath:/consumer-config.properties") +public class DemoServiceConsumerBootstrap { + + @DubboReference(version = "${demo.service.version}") + private DemoService demoService; + + @PostConstruct + public void init() { + for (int i = 0; i < 10; i++) { + System.out.println(demoService.sayName("Nacos")); + } + } + + public static void main(String[] args) throws IOException { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(DemoServiceConsumerBootstrap.class); + context.refresh(); + context.close(); + } +} + +``` + +同样地,`@EnableDubbo` 注解激活 Dubbo 注解驱动和外部化配置,不过当前属于服务消费者,无需指定 Java 包名扫描标注 `@Service` 的服务实现。 + +`@Reference` 是 Dubbo 远程服务的依赖注入注解,需要服务提供方和消费端约定接口(interface)、版本(version)以及分组(group)信息。在当前服务消费示例中,`DemoService` 的服务版本来源于属性配置文件 `consumer-config.properties`。 + +`@PostConstruct` 部分代码则说明当 `DemoServiceConsumerBootstrap` Bean 初始化时,执行十次 Dubbo 远程方法调用。 + + + +#### 运行注解驱动示例 + +在本地启动两次 `DemoServiceProviderBootstrap`,注册中心将出现两个健康服务: + +![image-20181213123909636-4675949.png | left | 747x38](https://img.alicdn.com/tfs/TB1s9fbzMHqK1RjSZFgXXa7JXXa-2390-122.png "") + +再运行 `DemoServiceConsumerBootstrap`,运行结果如下: + +``` +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +``` + +运行无误,并且服务消费方使用了负载均衡策略,将十次 RPC 调用平均分摊到两个 Dubbo 服务提供方实例中。 + + + +### Spring XML 配置驱动示例 + +Spring XML 配置驱动是传统 Spring 装配组件的编程模型。 + + + +#### 服务提供方 XML 配置驱动 + +- 定义服务提供方 XML 上下文配置文件 - `/META-INF/spring/dubbo-provider-context.xml` + +```xml + + + + + + + + + + + + + + + + + + + + +``` + + +- 实现服务提供方引导类 - `DemoServiceProviderXmlBootstrap` + +```xml + +package com.alibaba.nacos.example.dubbo.provider; + +import com.alibaba.dubbo.demo.service.DemoService; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.io.IOException; + +/** + * {@link DemoService} provider demo XML bootstrap + */ +public class DemoServiceProviderXmlBootstrap { + + public static void main(String[] args) throws IOException { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); + context.setConfigLocation("/META-INF/spring/dubbo-provider-context.xml"); + context.refresh(); + System.out.println("DemoService provider (XML) is starting..."); + System.in.read(); + } +} +``` + + +#### 服务消费方 XML 配置驱动 + +- 定义服务消费方 XML 上下文配置文件 - `/META-INF/spring/dubbo-consumer-context.xml` + +```xml + + + + + + + + + + + + + + + +``` + +- 实现服务消费方引导类 - `DemoServiceConsumerXmlBootstrap` + +```java + +package com.alibaba.nacos.example.dubbo.consumer; + +import com.alibaba.dubbo.demo.service.DemoService; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import java.io.IOException; + +/** + * {@link DemoService} consumer demo XML bootstrap + */ +public class DemoServiceConsumerXmlBootstrap { + + public static void main(String[] args) throws IOException { + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(); + context.setConfigLocation("/META-INF/spring/dubbo-consumer-context.xml"); + context.refresh(); + System.out.println("DemoService consumer (XML) is starting..."); + DemoService demoService = context.getBean("demoService", DemoService.class); + for (int i = 0; i < 10; i++) { + System.out.println(demoService.sayName("Nacos")); + } + context.close(); + } +} +``` + +#### 运行 XML 配置驱动示例 + +同样地,先启动两个 `DemoServiceProviderXmlBootstrap` 引导类,观察 Nacos 注册中心服务提供者变化: + +![image-20181213125527201-4676927.png | left | 747x33](https://img.alicdn.com/tfs/TB1HCfbzMHqK1RjSZFgXXa7JXXa-2388-106.png "") + +XML 配置驱动的服务版本为 `2.0.0`,因此注册服务无误。 + +再运行服务消费者引导类 `DemoServiceConsumerXmlBootstrap`,观察控制台输出内容: + +``` +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +Service [name :demoService , port : 20880] sayName("Nacos") : Hello,Nacos +``` + +结果同样运行和负载均衡正常,不过由于当前示例尚未添加属性 `demo.service.name` 的缘故,因此,“name”部分信息输出为 `null`。 + +如果您关注或喜爱 Dubbo 以及 Nacos 等开源工程,不妨为它们点 “star”,加油打气链接: + +- Apache Dubbo:https://github.com/apache/dubbo +- Dubbo Nacos Registry:https://github.com/apache/dubbo/tree/master/dubbo-registry/dubbo-registry-nacos +- Alibaba Nacos:https://github.com/alibaba/nacos diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-istio.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-istio.md new file mode 100644 index 00000000000..05d22ddb01a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-istio.md @@ -0,0 +1,168 @@ +--- +title: Nacos 融合Istio 下发xDS协议 +keywords: [Istio,xDs,Envoy] +description: Nacos 融合Istio 下发xDS协议 +--- +# Istio 指南 + +支持了 xDS 协议中的 CDS、EDS 服务,并为 EDS 以及 MCP 实现了增量推送。用户可以使用 Envoy 或其他支持 xDS 协议的客户端与 Nacos 进行对接,实现服务发现功能。 + +## 配置 + +### 服务端 + +对于发行包,修改 `nacos/conf/application.properties` 中的 `nacos.istio.mcp.server.enabled` 为 true; + +对于源码,修改 `nacos/distribution/conf/application.properties` 中的 `nacos.istio.mcp.server.enabled` 为 true 。 + +若要使用 MCP 增量服务,除上述配置需修改外,还需修改 `nacos/istio/misc/IstioConfig` 中的 `nacos.istio.server.full` 为 false。 + +### 客户端 + +关于客户端,下面示例中使用的是 Envoy,可直接下载 Envoy 或创建镜像并将下述配置文件进行挂载即可。 + +**Config**:其中使用的端口号根据需求可自行更改 + +```yaml +node: + cluster: test-cluster + id: test-idn + +admin: + address: + socket_address: { address: 0.0.0.0, port_value: 15000 } + +dynamic_resources: + ads_config: + api_type: GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: nacos_xds + cds_config: + ads: {} + lds_config: + path: /etc/envoy/lds.yaml + # ads: {} + +static_resources: + clusters: + - type: STATIC + connect_timeout: 1s + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicit_http_config: + http2_protocol_options: {} + name: nacos_xds + load_assignment: + cluster_name: nacos_xds + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 18848 +``` + +**lds**:对于监听的服务获取 CDS 后会主动向服务端获取 EDS,监听的服务可自行更改 + +```yaml +resources: +- "@type": type.googleapis.com/envoy.config.listener.v3.Listener + name: listener_0 + address: + socket_address: { address: 0.0.0.0, port_value: 80 } + # listener_filters: + # - name: "envoy.filters.listener.tls_inspector" + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress_http + access_log: + - name: envoy.access_loggers.stdout + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog + codec_type: AUTO + route_config: + name: local_route + virtual_hosts: + - name: local_service + domains: ["*"] + routes: + - match: { prefix: "/" } + name: test + route: + cluster: outbound|8071||service-provider.DEFAULT-GROUP.e77d7925-1c90-4fa9-93cb-83153a099636.nacos + http_filters: + - name: envoy.filters.http.router +``` + +## 运行 + +注:同一服务下的各个实例使用的协议需一致,EDS 默认使用增量推送。 + +1. 部署 Nacos,[部署参考](https://nacos.io/docs/next/quickstart/quick-start/); +2. 按上述要求修改配置; +3. 启动服务器,详细的启动命令可在上述部署参考中查看; + + ```bash + bash startup.sh -m standalone -p embedded + ``` + +4. 启动客户端。 + + ```bash + docker start envoy + ``` + +## CDS 示例 + +注:日志在 nacos/logs/istio-main.log 查看 + +示例中注册的服务配置如下,[示例参考](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example)。 + +```properties +server.port=8071 +spring.application.name=service-provider +spring.cloud.nacos.discovery.namespace=e77d7925-1c90-4fa9-93cb-83153a099636 +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +![CDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341241-4e9b2dde-55c7-43ae-af1e-dc081565ab72.png) + +## EDS 示例 + +服务配置如上 + +![EDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341176-fe312687-6488-41c2-bdd1-346d7a344bd2.png) + +## 全量 CDS 示例 + +现注册两个服务,其配置分别如下: + +```properties +#service-provider +server.port=8071 +spring.application.name=service-provider +spring.cloud.nacos.discovery.namespace=e77d7925-1c90-4fa9-93cb-83153a099636 +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 + +#service-consumer +server.port=8080 +spring.application.name=service-consumer +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +在控制台仅修改 service-consumer 服务配置,推送如下: + +![Full CDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341233-bc35de56-5653-4d5f-a510-819180dfe7f0.png) + +## 增量 EDS 示例 + +在控制台仅修改 service-consumer 实例配置,推送如下: + +![Incremental EDS](https://cdn.nlark.com/yuque/0/2022/png/28990648/1666247341234-aa195810-c76d-4ff5-977a-55626775e697.png) diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-k8s-sync.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-k8s-sync.md new file mode 100644 index 00000000000..b6a48b326c8 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-k8s-sync.md @@ -0,0 +1,38 @@ +--- +title: Nacos支持从K8S服务发现中同步服务元数据 +keywords: [Nacos,k8s,kubernetes] +description: Nacos支持从K8S服务发现中同步服务元数据 +--- + +# Nacos支持从K8S服务发现中同步服务元数据 + +## 数据同步 +Nacos监听K8S中服务和实例的变化情况,并获取其服务元数据,同步变更信息到Nacos的服务发现模块和实例中。支持K8S版本1.22(对应K8S-Java-API版本为14.0.0)。示意图如下: +![](img/k8s-sync.jpg) + +K8S资源同步至Nacos资源的映射方案(单向,Nacos资源同步至K8S资源待补充): + + +K8S中需要同步的数据|K8S中的字段|映射到Nacos中的字段 +---|---|--- +service name|service.metadata.name|service.name +service targetPort(pod port)(多个)|service.ports.targetPort|instance.port +service name|service.metadata.name|instance.cluster +service port(多个)|service.ports.port|instance.extendData +pod ip|pod.status.hostIP或者service.ipFamilies|instance.ip + +## 配置文件 +按照[部署文档](../guide/admin/deployment.md)搭建好Nacos集群。 + +配置application.properties文件,打开K8S同步的开关: +``` +nacos.k8s.sync.enabled=true +``` + +如果是从K8S集群外的应用程序使用Java API,需要指定kubeConfig: +``` +nacos.k8s.sync.outsideCluster=true +nacos.k8s.sync.kubeConfig=/.kube/config +``` + +配置完成后,K8S中的服务和实例变化时会自动同步到Nacos中。 diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-boot.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-boot.md new file mode 100644 index 00000000000..a7c089a8ec1 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-boot.md @@ -0,0 +1,173 @@ +--- +title: Nacos 融合 Spring Boot,成为注册配置中心 +keywords: [Nacos,Spring Boot] +description: 本文主要面向 Spring Boot 的使用者,通过示例来介绍如何使用 Nacos 来实现分布式环境下的配置管理和服务发现。 +sidebar: + order: 3 +--- + +# Nacos 融合 Spring Boot,成为注册配置中心 + +本文主要面向 Spring Boot 的使用者,通过两个示例来介绍如何使用 Nacos 来实现分布式环境下的配置管理和服务发现。 + +关于 Nacos Spring Boot 的详细文档请参看:[nacos-spring-boot-project](https://github.com/nacos-group/nacos-spring-boot-project/wiki/spring-boot-0.2.2-%E4%BB%A5%E5%8F%8A-0.1.2%E7%89%88%E6%9C%AC%E6%96%B0%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C)。 + +* 通过 Nacos Server 和 nacos-config-spring-boot-starter 实现配置的动态变更; +* 通过 Nacos Server 和 nacos-discovery-spring-boot-starter 实现服务的注册与发现。 + +## 前提条件 + +您需要先下载 Nacos 并启动 Nacos server。操作步骤参见 [Nacos 快速入门](../quickstart/quick-start.mdx)。 + +## 启动配置管理 + +启动了 Nacos server 后,您就可以参考以下示例代码,为您的 Spring Boot 应用启动 Nacos 配置管理服务了。完整示例代码请参考:[nacos-spring-boot-config-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-boot-example/nacos-spring-boot-config-example) + +1. 添加依赖。 + +``` + + com.alibaba.boot + nacos-config-spring-boot-starter + ${latest.version} + +``` + +**注意**:版本 [0.2.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-config-spring-boot-starter) 对应的是 Spring Boot 2.x 版本,版本 [0.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-config-spring-boot-starter) 对应的是 Spring Boot 1.x 版本。 + +2. 在 `application.properties` 中配置 Nacos server 的地址: + +``` +nacos.config.server-addr=127.0.0.1:8848 +``` + +3. 使用 `@NacosPropertySource` 加载 `dataId` 为 `example` 的配置源,并开启自动更新: + +```plain +@SpringBootApplication +@NacosPropertySource(dataId = "example", autoRefreshed = true) +public class NacosConfigApplication { + + public static void main(String[] args) { + SpringApplication.run(NacosConfigApplication.class, args); + } +} +``` + +4. 通过 Nacos 的 `@NacosValue` 注解设置属性值。 + +``` +@Controller +@RequestMapping("config") +public class ConfigController { + + @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true) + private boolean useLocalCache; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public boolean get() { + return useLocalCache; + } +} +``` + +5. 启动 `NacosConfigApplication`,调用 `curl http://localhost:8080/config/get`,返回内容是 `false`。 + +6. 通过调用 [Nacos Open API](../guide/user/open-api.md) 向 Nacos server 发布配置:dataId 为`example`,内容为`useLocalCache=true` + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example&group=DEFAULT_GROUP&content=useLocalCache=true" +``` + +7. 再次访问 `http://localhost:8080/config/get`,此时返回内容为`true`,说明程序中的`useLocalCache`值已经被动态更新了。 + +## 启动服务发现 + +本节演示如何在您的 Spring Boot 项目中启动 Nacos 的服务发现功能。完整示例代码请参考:[nacos-spring-boot-discovery-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-boot-example/nacos-spring-boot-discovery-example) + +1. 添加依赖。 + +``` + + com.alibaba.boot + nacos-discovery-spring-boot-starter + ${latest.version} + +``` + +**注意**:版本 [0.2.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-discovery-spring-boot-starter) 对应的是 Spring Boot 2.x 版本,版本 [0.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.boot/nacos-discovery-spring-boot-starter) 对应的是 Spring Boot 1.x 版本。 + +2. 在 `application.properties` 中配置 Nacos server 的地址: + +``` +nacos.discovery.server-addr=127.0.0.1:8848 +``` + +3. 使用 `@NacosInjected` 注入 Nacos 的 `NamingService` 实例: + +``` +@Controller +@RequestMapping("discovery") +public class DiscoveryController { + + @NacosInjected + private NamingService namingService; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public List get(@RequestParam String serviceName) throws NacosException { + return namingService.getAllInstances(serviceName); + } +} + +@SpringBootApplication +public class NacosDiscoveryApplication { + + public static void main(String[] args) { + SpringApplication.run(NacosDiscoveryApplication.class, args); + } +} +``` + + +4. 启动 `NacosDiscoveryApplication`,调用 `curl http://localhost:8080/discovery/get?serviceName=example`,此时返回为空 JSON 数组`[]`。 + +5. 通过调用 [Nacos Open API](../guide/user/open-api.md) 向 Nacos server 注册一个名称为 `example` 服务 + +``` +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=example&ip=127.0.0.1&port=8080' +``` + +6. 再次访问 `curl http://localhost:8080/discovery/get?serviceName=example`,此时返回内容为: + +``` +[ + { + "instanceId": "127.0.0.1-8080-DEFAULT-example", + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "cluster": { + "serviceName": null, + "name": "", + "healthChecker": { + "type": "TCP" + }, + "defaultPort": 80, + "defaultCheckPort": 80, + "useIPPort4Check": true, + "metadata": {} + }, + "service": null, + "metadata": {} + } +] +``` + +## 相关项目 +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-cloud.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-cloud.md new file mode 100644 index 00000000000..07a1f4c575c --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring-cloud.md @@ -0,0 +1,203 @@ +--- +title: Nacos 融合 Spring Cloud,成为注册配置中心 +keywords: [Nacos,Spring Cloud] +description: 本文主要面向 Spring Cloud 的使用者,通过示例来介绍如何使用 Nacos 来实现分布式环境下的配置管理和服务发现 +sidebar: + order: 4 +--- + +# Nacos 融合 Spring Cloud,成为注册配置中心 + +本文主要面向 [Spring Cloud](https://spring.io/projects/spring-cloud) 的使用者,通过两个示例来介绍如何使用 Nacos 来实现分布式环境下的配置管理和服务注册发现。 + +关于 Nacos Spring Cloud 的详细文档请参看:[Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/Nacos-config) 和 [Nacos Discovery](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/Nacos-discovery)。 + +* 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-config 实现配置的动态变更。 +* 通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现。 + +## 前提条件 + +您需要先下载 Nacos 并启动 Nacos server。操作步骤参见 [Nacos 快速入门](../quickstart/quick-start.mdx) + +## 启动配置管理 + +启动了 Nacos server 后,您就可以参考以下示例代码,为您的 Spring Cloud 应用启动 Nacos 配置管理服务了。完整示例代码请参考:[nacos-spring-cloud-config-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-config-example) + +1. 添加依赖: + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + ${latest.version} + +``` + +**注意**:版本 [2.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config) 对应的是 Spring Boot 2.1.x 版本。版本 [2.0.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config) 对应的是 Spring Boot 2.0.x 版本,版本 [1.5.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-config) 对应的是 Spring Boot 1.5.x 版本。 + +更多版本对应关系参考:[版本说明 Wiki](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E) + +2. 在 `bootstrap.properties` 中配置 Nacos server 的地址和应用名 + +``` +spring.cloud.nacos.config.server-addr=127.0.0.1:8848 + +spring.application.name=example +``` + +说明:之所以需要配置 `spring.application.name` ,是因为它是构成 Nacos 配置管理 `dataId`字段的一部分。 + +在 Nacos Spring Cloud 中,`dataId` 的完整格式如下: + +```plain +${prefix}-${spring.profiles.active}.${file-extension} +``` + +* `prefix` 默认为 `spring.application.name` 的值,也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置。 +* `spring.profiles.active` 即为当前环境对应的 profile,详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)。 + **注意:当 `spring.profiles.active` 为空时,对应的连接符 `-` 也将不存在,dataId 的拼接格式变成 `${prefix}.${file-extension}`** +* `file-exetension` 为配置内容的数据格式,可以通过配置项 `spring.cloud.nacos.config.file-extension` 来配置。目前只支持 `properties` 和 `yaml` 类型。 + +4. 通过 Spring Cloud 原生注解 `@RefreshScope` 实现配置自动更新: + +``` +@RestController +@RequestMapping("/config") +@RefreshScope +public class ConfigController { + + @Value("${useLocalCache:false}") + private boolean useLocalCache; + + @RequestMapping("/get") + public boolean get() { + return useLocalCache; + } +} +``` + + +5. 首先通过调用 [Nacos Open API](../guide/user/open-api.md) 向 Nacos Server 发布配置:dataId 为`example.properties`,内容为`useLocalCache=true` + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=true" +``` + +6. 运行 `NacosConfigApplication`,调用 `curl http://localhost:8080/config/get`,返回内容是 `true`。 + +7. 再次调用 [Nacos Open API](../guide/user/open-api.md) 向 Nacos server 发布配置:dataId 为`example.properties`,内容为`useLocalCache=false` + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=false" +``` + +8. 再次访问 `http://localhost:8080/config/get`,此时返回内容为`false`,说明程序中的`useLocalCache`值已经被动态更新了。 + +## 启动服务发现 + +本节通过实现一个简单的 `echo service` 演示如何在您的 Spring Cloud 项目中启用 Nacos 的服务发现功能,如下图示: + +![echo service](https://cdn.nlark.com/lark/0/2018/png/15914/1542119181336-b6dc0fc1-ed46-43a7-9e5f-68c9ca344d60.png) + +完整示例代码请参考:[nacos-spring-cloud-discovery-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-cloud-example/nacos-spring-cloud-discovery-example) + +1. 添加依赖: + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + ${latest.version} + +``` + +**注意**:版本 [2.1.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery) 对应的是 Spring Boot 2.1.x 版本。版本 [2.0.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery) 对应的是 Spring Boot 2.0.x 版本,版本 [1.5.x.RELEASE](https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-nacos-discovery) 对应的是 Spring Boot 1.5.x 版本。 + +更多版本对应关系参考:[版本说明 Wiki](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E) + +2. 配置服务提供者,从而服务提供者可以通过 Nacos 的服务注册发现功能将其服务注册到 Nacos server 上。 + + i. 在 `application.properties` 中配置 Nacos server 的地址: + +``` +server.port=8070 +spring.application.name=service-provider + +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +ii. 通过 Spring Cloud 原生注解 `@EnableDiscoveryClient` 开启服务注册发现功能: + +``` +@SpringBootApplication +@EnableDiscoveryClient +public class NacosProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(NacosProviderApplication.class, args); + } + + @RestController + class EchoController { + @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET) + public String echo(@PathVariable String string) { + return "Hello Nacos Discovery " + string; + } + } +} +``` + + +3. 配置服务消费者,从而服务消费者可以通过 Nacos 的服务注册发现功能从 Nacos server 上获取到它要调用的服务。 + +i. 在 `application.properties` 中配置 Nacos server 的地址: + +``` +server.port=8080 +spring.application.name=service-consumer + +spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 +``` + +ii. 通过 Spring Cloud 原生注解 `@EnableDiscoveryClient` 开启服务注册发现功能。给 [RestTemplate](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-resttemplate.html) 实例添加 `@LoadBalanced` 注解,开启 `@LoadBalanced` 与 [Ribbon](https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html) 的集成: + +``` +@SpringBootApplication +@EnableDiscoveryClient +public class NacosConsumerApplication { + + @LoadBalanced + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + public static void main(String[] args) { + SpringApplication.run(NacosConsumerApplication.class, args); + } + + @RestController + public class TestController { + + private final RestTemplate restTemplate; + + @Autowired + public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;} + + @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET) + public String echo(@PathVariable String str) { + return restTemplate.getForObject("http://service-provider/echo/" + str, String.class); + } + } +} +``` + + +4. 启动 `ProviderApplication` 和 `ConsumerApplication` ,调用 `http://localhost:8080/echo/2018`,返回内容为 `Hello Nacos Discovery 2018`。 + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) diff --git a/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring.md b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring.md new file mode 100644 index 00000000000..23a17573513 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/ecology/use-nacos-with-spring.md @@ -0,0 +1,456 @@ +--- +title: Nacos 融合 Spring,成为注册配置中心 +keywords: [Nacos,Spring,快速开始] +description: 本文主要面向 Spring 的使用者,通过两个示例来介绍如何使用 Nacos 来实现分布式环境下的配置管理和服务发现。 +sidebar: + order: 2 +--- + +# Nacos 融合 Spring,成为注册配置中心 + +本文主要面向 Spring 的使用者,通过两个示例来介绍如何使用 Nacos 来实现分布式环境下的配置管理和服务发现。 + +关于 Nacos Spring 的详细文档请参看:[nacos-spring-project](https://github.com/nacos-group/nacos-spring-project/wiki/Nacos-Spring-Project-0.3.1-%E6%96%B0%E5%8A%9F%E8%83%BD%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C)。 + +* 通过 Nacos server 和 Nacos Spring 配置管理模块,实现配置的动态变更; +* 通过 Nacos server 和 Nacos Spring 服务发现模块,实现服务的注册与发现。 + +## 前提条件 + +您需要先下载 Nacos 并启动 Nacos server。操作步骤参见 [Nacos 快速入门](../quickstart/quick-start.mdx)。 + +## 启动配置管理 + +启动了 Nacos server 后,您就可以参考以下示例代码,为您的 Spring 应用启动 Nacos 配置管理服务了。完整示例代码请参考:[nacos-spring-config-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-config-example) + +1. 添加依赖。 + +``` + + com.alibaba.nacos + nacos-spring-context + ${latest.version} + +``` + +最新版本可以在 maven 仓库,如 "[mvnrepository.com](https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-spring-context)" 中获取。 + +2. 添加 `@EnableNacosConfig` 注解启用 Nacos Spring 的配置管理服务。以下示例中,我们使用 `@NacosPropertySource` 加载了 `dataId` 为 `example` 的配置源,并开启自动更新: + +``` +@Configuration +@EnableNacosConfig(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) +@NacosPropertySource(dataId = "example", autoRefreshed = true) +public class NacosConfiguration { + +} +``` + +3. 通过 Nacos 的 `@NacosValue` 注解设置属性值。 + +``` +@Controller +@RequestMapping("config") +public class ConfigController { + + @NacosValue(value = "${useLocalCache:false}", autoRefreshed = true) + private boolean useLocalCache; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public boolean get() { + return useLocalCache; + } +} +``` + +4. 启动 Tomcat,调用 `curl http://localhost:8080/config/get`尝试获取配置信息。由于此时还未发布过配置,所以返回内容是 `false`。 + +5. 通过调用 [Nacos Open API](../guide/user/open-api.md) 向 Nacos Server 发布配置:dataId 为`example`,内容为`useLocalCache=true` + +``` +curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example&group=DEFAULT_GROUP&content=useLocalCache=true" +``` + +6. 再次访问 `http://localhost:8080/config/get`,此时返回内容为`true`,说明程序中的`useLocalCache`值已经被动态更新了。 + +## 启动服务发现 + +本节演示如何在您的 Spring 项目中启动 Nacos 的服务发现功能。完整示例代码请参考:[nacos-spring-discovery-example](https://github.com/nacos-group/nacos-examples/tree/master/nacos-spring-example/nacos-spring-discovery-example) + +1. 添加依赖。 + +``` + + com.alibaba.nacos + nacos-spring-context + ${latest.version} + +``` + +最新版本可以在 maven 仓库,如 "[mvnrepository.com](https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-spring-context)" 中获取。 + +2. 通过添加 `@EnableNacosDiscovery` 注解开启 Nacos Spring 的服务发现功能: + +``` +@Configuration +@EnableNacosDiscovery(globalProperties = @NacosProperties(serverAddr = "127.0.0.1:8848")) +public class NacosConfiguration { + +} +``` + +3. 使用 `@NacosInjected` 注入 Nacos 的 `NamingService` 实例: + +``` +@Controller +@RequestMapping("discovery") +public class DiscoveryController { + + @NacosInjected + private NamingService namingService; + + @RequestMapping(value = "/get", method = GET) + @ResponseBody + public List get(@RequestParam String serviceName) throws NacosException { + return namingService.getAllInstances(serviceName); + } +} +``` + +4. 启动 Tomcat,调用 `curl http://localhost:8080/discovery/get?serviceName=example`,此时返回为空 JSON 数组`[]`。 + +5. 通过调用 [Nacos Open API](../guide/user/open-api.md) 向 Nacos server 注册一个名称为 `example` 服务。 + +``` +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=example&ip=127.0.0.1&port=8080' +``` + +6. 再次访问 `curl http://localhost:8080/discovery/get?serviceName=example`,此时返回内容为: + +``` +[ + { + "instanceId": "127.0.0.1#8080#DEFAULT#example", + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "cluster": { + "serviceName": null, + "name": "", + "healthChecker": { + "type": "TCP" + }, + "defaultPort": 80, + "defaultCheckPort": 80, + "useIPPort4Check": true, + "metadata": {} + }, + "service": null, + "metadata": {} + } +] +``` + +# Nacos Spring关键特性 + +下文将介绍 [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) 中的一些关键的特性: + +- 注解驱动 +- 依赖注入 +- 外部化配置 +- 事件驱动 + +## 1. 注解驱动 + +### 1.1. 启用 Nacos + +`@EnableNacos`是一个模块驱动的注解,它支持 Nacos Spring 的所有功能,包括**服务发现**和**配置管理**。它等于 `@EnableNacosDiscovery` 加上 `@EnableNacosConfig`,可以单独配置并在不同场景中使用。 + +### 1.2. 配置监听 + +假设在 Nacos 服务中有一个配置,其 `dataId` 是 "testDataId" 而 `groupId` 是默认组("DEFAULT_GROUP")。 现在,您可以使用 `ConfigService#publishConfig` 方法更改其内容: + +```java +@NacosInjected +private ConfigService configService; + +@Test +public void testPublishConfig() throws NacosException { + configService.publishConfig(DATA_ID, DEFAULT_GROUP, "9527"); +} +``` + +然后您可以添加一个监听器,它将监听配置的变化。 您可以通过在 Spring Bean 中添加配置变更监听器方法来执行此操作: + +```java +@NacosConfigListener(dataId = DATA_ID) +public void onMessage(String config) { + assertEquals("mercyblitz", config); // asserts true +} +``` + +下面的代码具有相同的效果: + +```java +configService.addListener(DATA_ID, DEFAULT_GROUP, new AbstractListener() { + @Override + public void receiveConfigInfo(String config) { + assertEquals("9527", config); // asserts true + } +}); +``` + +另外,`@NacosConfigListener` 支持更丰富的类型转换。 + +- 请参看: [Simple Sample of `@NacosConfigListener`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/listener/SimpleNacosConfigListener.java) + + +#### 1.2.1. 类型 + +`@NacosConfigListener` 的类型转换包括内置和自定义实现。 默认情况下,内置类型转换基于 Spring `DefaultFormattingConversionService`,这意味着它包好了大多数情况以及 Spring 框架更高级版本的丰富功能。 + +例如,前面示例中的内容 "9527" 也可以通过带 "int" 或 "Integer" 参数的方法进行监听:: + +```java +@NacosConfigListener(dataId = DATA_ID) +public void onInteger(Integer value) { + assertEquals(Integer.valueOf(9527), value); // asserts true +} + +@NacosConfigListener(dataId = DATA_ID) +public void onInt(int value) { + assertEquals(9527, value); // asserts true +} +``` + +当然, [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) 为开发人员提供弹性扩展。 如果定义名为`nacosConfigConversionService`的Spring Bean,其类型为`ConversionService`,则将忽略`DefaultFormattingConversionService`。 此外,您可以自定义`NacosConfigConverter`接口的实现,以指定类型转换的侦听器方法: + +```java +public class UserNacosConfigConverter implements NacosConfigConverter { + + @Override + public boolean canConvert(Class targetType) { + return true; + } + + @Override + public User convert(String source) { + return JSON.parseObject(source, User.class); + } +} +``` + +`UserNacosConfigConverter` 类绑定在 `@NacosConfigListener.converter()` 属性上,如下: + +```java +@NacosInjected +private ConfigService configService; + +@Test +public void testPublishUser() throws NacosException { + configService.publishConfig("user", DEFAULT_GROUP, "{\"id\":1,\"name\":\"mercyblitz\"}"); +} + +@NacosConfigListener(dataId = "user", converter = UserNacosConfigConverter.class) +public void onUser(User user) { + assertEquals(Long.valueOf(1L), user.getId()); + assertEquals("mercyblitz", user.getName()); +} +``` + +- 请参看:[Type Conversion Sample of `@NacosConfigListener`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/listener/PojoNacosConfigListener.java) + + +#### 1.2.2. 超时时间 + +由于运行自定义的 `NacosConfigConverter` 可能需要一些时间,因此您可以在 `@NacosConfigListener.timeout()` 属性中设置最大执行时间,以防止它阻塞其他侦听器: + +```java +@Configuration +public class Listeners { + + private Integer integerValue; + + private Double doubleValue; + + @NacosConfigListener(dataId = DATA_ID, timeout = 50) + public void onInteger(Integer value) throws Exception { + Thread.sleep(100); // timeout of execution + this.integerValue = value; + } + + @NacosConfigListener(dataId = DATA_ID, timeout = 200) + public void onDouble(Double value) throws Exception { + Thread.sleep(100); // normal execution + this.doubleValue = value; + } + + public Integer getIntegerValue() { + return integerValue; + } + + public Double getDoubleValue() { + return doubleValue; + } +} +``` + +`Listeners` Bean 的 `integerValue` 总是为`null`,不会改变。 因此,以下断言都将是 `true`: + +```java +@Autowired +private Listeners listeners; + +@Test +public void testPublishConfig() throws NacosException { + configService.publishConfig(DATA_ID, DEFAULT_GROUP, "9527"); + assertNull(listeners.getIntegerValue()); // asserts true + assertEquals(Double.valueOf(9527), listeners.getDoubleValue()); // asserts true +} +``` + +- 请参看:[Timeout Sample of `@NacosConfigListener`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/listener/TimeoutNacosConfigListener.java) + +### 1.3. 全局和自定义 Nacos 属性 + +`globalProperties` 是任何 `@EnableNacos`,`@EnableNacosDiscovery` 或 `@EnableNacosConfig` 中的必选属性,其类型为 `@NacosProperties`。 + +`globalProperties` 将初始化为其他注解或组件的 "**全局 Nacos 属性**",例如:`@NacosInjected`。 + +换句话说,**全局 Nacos 属性** 定义全局和默认属性。它设置为具有最低优先级,并且也可以被覆盖。覆盖优先级如下表所示: + +| Precedence Order | Nacos Annotation | Required | +| ---------------- | ------------------------------------------------------------ | -------- | +| 1 | `*.properties()` | N | +| 2 | `@EnableNacosConfig.globalProperties()` or `@EnableNacosDiscovery.globalProperties()` | Y | +| 3 | `@EnableNacos.globalProperties()` | Y | + + +`*.properties()` 定义来自以下之一的自定义 Nacos 属性: + +- `@NacosInjected.properties()` +- `@NacosConfigListener.properties()` +- `@NacosPropertySource.properties()` +- `@NacosConfigurationProperties.properties()` + +自定义的 Nacos 属性也由 `@NacosProperties` 配置。 不过,它们是可选的,用于在特殊情况下覆盖全局 Nacos 属性。 如果没有定义,Nacos 属性将尝试从 `@EnableNacosConfig.globalProperties()` 或 `@EnableNacosDiscovery.globalProperties()` 或 + `@EnableNacos.globalProperties()` 中查找属性。 + + +### 1.4. `@NacosProperties` + +`@NacosProperties` 是全局和自定义 Nacos 属性的统一注解。 它充当Java `Properties` 和 `NacosFactory` 类之间的中介。`NacosFactory` 负责创建 `ConfigService` 或 `NamingService` 实例。 + +`@NacosProperties` 的属性完全支持占位符,它的源是Spring `Environment` 抽象中的各种 `PropertySource`,通常是Java System `Properties` 和操作系统环境变量。 所有占位符的前缀都是 `nacos.`。`@NacosProperties` 和 Nacos 属性的属性之间的映射如下所示: + +| Attribute | Property | Placeholder | Description | Required | +| --------------- | -------------- | ------------------------ | ----------- | --------- | +| `endpoint()` | `endpoint` | `${nacos.endpoint:}` | | N | +| `namespace()` | `namespace` | `${nacos.namespace:}` | | N | +| `accessKey()` | `access-key` | `${nacos.access-key:}` | | N | +| `secretKey()` | `secret-key` | `${nacos.secret-key:}` | | N | +| `serverAddr()` | `server-addr` | `${nacos.server-addr:}` | | Y | +| `contextPath()` | `context-path` | `${nacos.context-path:}` | | N | +| `clusterName()` | `cluster-name` | `${nacos.cluster-name:}` | | N | +| `encode()` | `encode` | `${nacos.encode:UTF-8}` | | N | + + +请注意,`@EnableNacosDiscovery` 和 `@EnableNacosConfig` 之间 `globalProperties()` 的占位符存在一些差异: + + +| Attribute | `@EnableNacosDiscovery`'s Placeholder |`@EnableNacosConfig`'s Placeholder | +| --------------- | -------------------------------------------------------- | ------------------------------------------------- | +| `endpoint()` | `${nacos.discovery.endpoint:${nacos.endpoint:}}` |`${nacos.config.endpoint:${nacos.endpoint:}}` | +| `namespace()` | `${nacos.discovery.namespace:${nacos.namespace:}}` |`${nacos.config.namespace:${nacos.namespace:}}` | +| `accessKey()` | `${nacos.discovery.access-key:${nacos.access-key:}}` |`${nacos.config.access-key:${nacos.access-key:}}` | +| `secretKey()` | `${nacos.discovery.secret-key:${nacos.secret-key:}}` |`${nacos.config.secret-key:${nacos.secret-key:}}` | +| `serverAddr()` | `${nacos.discovery.server-addr:${nacos.server-addr:}}` | `${nacos.config.server-addr:${nacos.server-addr:}}` | +| `contextPath()` | `${nacos.discovery.context-path:${nacos.context-path:}}` | `${nacos.config.context-path:${nacos.context-path:}}` | +| `clusterName()` | `${nacos.discovery.cluster-name:${nacos.cluster-name:}}` |`${nacos.config.cluster-name:${nacos.cluster-name:}}` | +| `encode()` | `${nacos.discovery.encode:${nacos.encode:UTF-8}}` |`${nacos.config.encode:${nacos.encode:UTF-8}}` | + + +这些 `@EnableNacosDiscovery` 和 `@EnableNacosConfig` 的占位符用于隔离不同的 Nacos 服务,在大多数情况下都是不必要的。默认情况下,将使用常规占位符。 + +## 2. 依赖注入 + +`@NacosInjected` 是一个核心注解,用于在Spring Beans 中注入 `ConfigService` 或 `NamingService` 实例,并使这些实例**可缓存**。 这意味着如果它们的 `@NacosProperties` 相等,则实例将是相同的,无论属性是来自全局还是自定义的 Nacos 属性: + +```java +@NacosInjected +private ConfigService configService; + +@NacosInjected(properties = @NacosProperties(encode = "UTF-8")) +private ConfigService configService2; + +@NacosInjected(properties = @NacosProperties(encode = "GBK")) +private ConfigService configService3; + +@NacosInjected +private NamingService namingService; + +@NacosInjected(properties = @NacosProperties(encode = "UTF-8")) +private NamingService namingService2; + +@NacosInjected(properties = @NacosProperties(encode = "GBK")) +private NamingService namingService3; + +@Test +public void testInjection() { + + Assert.assertEquals(configService, configService2); + Assert.assertNotEquals(configService2, configService3); + + Assert.assertEquals(namingService, namingService2); + Assert.assertNotEquals(namingService2, namingService3); +} +``` + +属性 `configService` 使用 `@EnableNacos#globalProperties()` 或 `@EnableNacosConfig#globalProperties()`,因为 `encode` 属性的默认值是 "UTF-8",因此 `configService` 实例和由 `@NacosProperties(encode ="UTF-8")` 注解的 `configService2` 实例是相同的。 `namingService` 和 `namingService2` 也是如此。 + +值得注意的是,与 `NacosFactory.createConfigService()` 方法创建的 `ConfigService` 实例不同,`@NacosInjected` 注解创建的 `ConfigService` 实例支持 Nacos Spring 事件。 例如,在增强的 `ConfigService` 调用 `publishConfig()` 方法之后会有一个 `NacosConfigPublishedEvent`。 有关更多详细信息,请参阅"事件驱动"部分。 + +- 请参看:[Dependency Injection Sample](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/NacosConfiguration.java) + + +## 3. 外部化配置 + +外部化配置是 Spring Boot 引入的概念,它允许应用程序接收外部属性源以控制运行时行为。 Nacos Server 在应用程序外部运行单独的进程以维护应用程序配置。 [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) 提供了对象绑定,动态配置(自动刷新)等功能。 + +这里有 [`nacos-spring-context`](https://github.com/nacos-group/nacos-spring-project) 和 Spring Stack 之间的简单比较: + +| Spring Stack | Nacos Spring | Highlight | +| -------------------------- | ------------------------------- | ---------------------------------------------- | +| `@Value` | `@NacosValue` | auto-refreshed | +| `@ConfigurationProperties` | `@NacosConfigurationProperties` | auto-refreshed,`@NacosProperty`,`@NacosIgnore` | +| `@PropertySource` | `@NacosPropertySource` | auto-refreshed, precedence order control | +| `@PropertySources` | `@NacosPropertySources` | | + + +- 请参看:[Auto-Refreshed Sample of `@NacosConfigurationProperties`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/properties/NacosConfigurationPropertiesConfiguration.java) +- 请参看:[Sample of `@NacosPropertySources` and `@NacosPropertySource`](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/env/NacosPropertySourceConfiguration.java) + +## 4. 事件驱动 + +Nacos 事件驱动 基于标准的 Spring Event / Listener 机制。 Spring 的 `ApplicationEvent` 是所有 Nacos Spring 事件的抽象超类: + +| Nacos Spring Event | Trigger | +| -------------------------------------------- | ------------------------------------------------------------ | +| `NacosConfigPublishedEvent` | After `ConfigService.publishConfig()` | +| `NacosConfigReceivedEvent` | After`Listener.receiveConfigInfo()` | +| `NacosConfigRemovedEvent` | After `configService.removeConfig()` | +| `NacosConfigTimeoutEvent` | `ConfigService.getConfig()` on timeout | +| `NacosConfigListenerRegisteredEvent` | After `ConfigService.addListner()` or `ConfigService.removeListener()` | +| `NacosConfigurationPropertiesBeanBoundEvent` | After `@NacosConfigurationProperties` binding | +| `NacosConfigMetadataEvent` | After Nacos Config operations | + +- 请参看:[Event/Listener Sample](https://github.com/nacos-group/nacos-spring-project/blob/master/nacos-spring-samples/nacos-spring-webmvc-sample/src/main/java/com/alibaba/nacos/samples/spring/event/NacosEventListenerConfiguration.java) + +# 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/cluster-mode-quick-start.md b/src/content/docs/v3.0/zh-cn/guide/admin/cluster-mode-quick-start.md new file mode 100644 index 00000000000..38cef550937 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/cluster-mode-quick-start.md @@ -0,0 +1,176 @@ +--- +title: 集群部署说明 +keywords: [集群,部署] +description: 集群部署说明 +sidebar: + order: 2 +--- + + + +> 该文档即将废弃,推荐查看[运维手册-部署手册-集群模式部署](../../manual/admin/deployment/deployment-cluster.md)。 + +## 集群模式部署 + +这个快速开始手册是帮忙您快速在你的电脑上,下载安装并使用Nacos,部署生产使用的集群模式。 + +### 集群部署架构图 + +因此开源的时候推荐用户把所有服务列表放到一个vip下面,然后挂到一个域名下面 + + 直连ip模式,机器挂则需要修改ip才可以使用。 + + 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。 + + 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式 + +![deployDnsVipMode.jpg](/img/deployDnsVipMode.jpg) + +|端口|与主端口的偏移量|描述| +|--|--|--| +|8848|0|主端口,客户端、控制台及OpenAPI所使用的HTTP端口| +|9848|1000|客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求| +|9849|1001|服务端gRPC请求服务端端口,用于服务间同步等| +|7848|-1000|Jraft请求服务端端口,用于处理服务端间的Raft相关请求| + +**使用VIP/nginx请求时,需要配置成TCP转发,不能配置http2转发,否则连接会被nginx断开。** +**9849和7848端口为服务端之间的通信端口,请勿暴露到外部网络环境和客户端测。** + +## 1. 预备环境准备 + +请确保是在环境中安装使用: + +1. 64 bit OS Linux/Unix/Mac,推荐使用Linux系统。 +2. 64 bit JDK 1.8+;[下载](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). [配置](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/)。 +3. Maven 3.2.x+;[下载](https://maven.apache.org/download.cgi). [配置](https://maven.apache.org/settings.html)。 +4. 3个或3个以上Nacos节点才能构成集群。 + +## 2. 下载源码或者安装包 + +你可以通过两种方式来获取 Nacos。 + +### 从 Github 上下载源码方式 + +```bash +unzip nacos-source.zip +cd nacos/ +mvn -Prelease-nacos clean install -U +cd nacos/distribution/target/nacos-server-1.3.0/nacos/bin +``` + +### 下载编译后压缩包方式 + +下载地址 + +您可以从 [最新稳定版本](https://github.com/alibaba/nacos/releases) 下载 `nacos-server-$version.zip` 包 或 `nacos-server-$version.tar.gz`。 + +```bash + unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz + cd nacos/bin +``` + +## 3. 配置集群配置文件 + +在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点) + +```plain +# ip:port +200.8.9.16:8848 +200.8.9.17:8848 +200.8.9.18:8848 +``` + +### 3.1 开启默认鉴权插件(可选) + +之后修改`conf`目录下的`application.properties`文件。 + +设置其中 + +```properties +nacos.core.auth.enabled=true +nacos.core.auth.system.type=nacos +nacos.core.auth.plugin.nacos.token.secret.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.value=${自定义,保证所有节点一致} +``` + +上述内容详情可查看[权限认证](../../plugin/auth-plugin.md). + +> 注意,文档中的默认值`SecretKey012345678901234567890123456789012345678901234567890123456789`和`VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=`为公开默认值,可用于临时测试,实际使用时请**务必**更换为自定义的其他有效值。 + +## 4. 确定数据源 + +### 使用内置数据源 + +无需进行任何配置 + +### 使用外置数据源 + + +生产使用建议至少主备模式,或者采用高可用数据库。 + +#### 初始化 MySQL 数据库 + +[sql语句源文件](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +#### application.properties 配置 + +[application.properties配置文件](https://github.com/alibaba/nacos/blob/master/distribution/conf/application.properties) + +## 5. 启动服务器 + +### Linux/Unix/Mac + +#### Stand-alone mode + +```bash +sh startup.sh -m standalone +``` + +#### 集群模式 + +> 使用内置数据源 + +```bash +sh startup.sh -p embedded +``` + +> 使用外置数据源 + +```bash +sh startup.sh +``` + +## 6. 服务注册&发现和配置管理 + +### 服务注册 + +`curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'` + +> 注意:如果开启默认鉴权插件,需要在Header中带上用户名密码。 + +### 服务发现 + +`curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'` + +> 注意:如果开启默认鉴权插件,需要在Header中带上用户名密码。 + +### 发布配置 + +`curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld"` + +> 注意:如果开启默认鉴权插件,需要在Header中带上用户名密码。 + +### 获取配置 + +`curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"` + +> 注意:如果开启默认鉴权插件,需要在Header中带上用户名密码。 + +## 7. 关闭服务器 + +Linux/Unix/Mac + +```bash +sh shutdown.sh +``` diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/console-guide.md b/src/content/docs/v3.0/zh-cn/guide/admin/console-guide.md new file mode 100644 index 00000000000..a3a436869b9 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/console-guide.md @@ -0,0 +1,189 @@ +--- +title: 控制台手册 +keywords: [控制台,手册] +description: Nacos 控制台主要旨在于增强对于服务列表,健康状态管理,服务治理,分布式配置管理等方面的管控能力。 +sidebar: + order: 5 +--- + +# 控制台手册 + +> 该文档即将废弃,推荐查看[运维手册-控制台手册](../../manual/admin/console.md)。 + +[Nacos 控制台](http://console.nacos.io/nacos/index.html)主要旨在于增强对于服务列表,健康状态管理,服务治理,分布式配置管理等方面的管控能力,以便进一步帮助用户降低管理微服务应用架构的成本,将提供包括下列基本功能: + +* 服务管理 + * 服务列表及服务健康状态展示 + * 服务元数据存储及编辑 + * 服务流量权重的调整 + * 服务优雅上下线 +* 配置管理 + * 多种配置格式编辑 + * 编辑DIFF + * 示例代码 + * 推送状态查询 + * 配置版本及一键回滚 +* 命名空间 +* 登录管理 + +## 特性详解 + +### 服务管理 + +开发者或者运维人员往往需要在服务注册后,通过友好的界面来查看服务的注册情况,包括当前系统注册的所有服务和每个服务的详情。并在有权限控制的情况下,进行服务的一些配置的编辑操作。Nacos在这个版本开放的控制台的服务发现部分,主要就是提供用户一个基本的运维页面,能够查看、编辑当前注册的服务。 + +#### 服务列表管理 + +服务列表帮助用户以统一的视图管理其所有的微服务以及服务健康状态。整体界面布局是左上角有服务的搜索框和搜索按钮,页面中央是服务列表的展示。服务列表主要展示服务名、集群数目、实例数目、健康实例数目和详情按钮五个栏目。 + +![image.png | left | 747x281](https://cdn.nlark.com/lark/0/2018/png/15356/1540536911804-3660f0e9-855f-4439-ac23-e76f6f644360.png "") + +在服务列表页面点击详情,可以看到服务的详情。可以查看服务、集群和实例的基本信息。 + +#### 服务流量权重支持及流量保护 + +Nacos 为用户提供了流量权重控制的能力,同时开放了服务流量的阈值保护,以帮助用户更好的保护服务服务提供者集群不被意外打垮。如下图所以,可以点击实例的编辑按钮,修改实例的权重。如果想增加实例的流量,可以将权重调大,如果不想实例接收流量,则可以将权重设为0。 + +![image.png | left | 747x266](https://cdn.nlark.com/lark/0/2018/png/15356/1540537029452-dffbb078-4ae5-4397-9f70-083e0ebbb5be.png "") + +#### 服务元数据管理 + +Nacos提供多个维度的服务元数据的暴露,帮助用户存储自定义的信息。这些信息都是以K-V的数据结构存储,在控制台上,会以k1=v1,k2=v2这样的格式展示。类似的,编辑元数据可以通过相同的格式进行。例如服务的元数据编辑,首先点击服务详情页右上角的“编辑服务”按钮,然后在元数据输入框输入:version=1.0,env=prod。 + +![image.png | left | 747x271](https://cdn.nlark.com/lark/0/2018/png/15356/1540537359751-217d7500-c19c-4bad-8508-27f347f48a2f.png "") + +点击确认,就可以在服务详情页面,看到服务的元数据已经更新了。 + +![image.png | left | 747x145](https://cdn.nlark.com/lark/0/2018/png/15356/1540537452673-01dc6c92-329a-4b6f-a616-36dc546c3355.png "") + +#### 服务优雅上下线 + +Nacos还提供服务实例的上下线操作,在服务详情页面,可以点击实例的“上线”或者“下线”按钮,被下线的实例,将不会包含在健康的实例列表里。 + +![image.png | left | 747x142](https://cdn.nlark.com/lark/0/2018/png/15356/1540537640435-b28cb279-75af-4965-8a9a-54cee213f1a5.png "") + +### 配置管理 + +Nacos支持基于Namespace和Group的配置分组管理,以便用户更灵活的根据自己的需要按照环境或者应用、模块等分组管理微服务以及Spring的大量配置,在配置管理中主要提供了配置历史版本、回滚、订阅者查询等核心管理能力。 + +![image.png | left | 747x297](https://cdn.nlark.com/lark/0/2018/png/9687/1540458893745-219a46a8-ebd9-405b-9e8f-226f3f0c7e76.png "") + +#### 多配置格式编辑器 + +Nacos支持 YAML、Properties、TEXT、JSON、XML、HTML 等常见配置格式在线编辑、语法高亮、格式校验,帮助用户高效编辑的同时大幅降低格式错误带来的风险。 + +Nacos支持配置标签的能力,帮助用户更好、更灵活的做到基于标签的配置分类及管理。同时支持用户对配置及其变更进行描述,方便多人或者跨团队协作管理配置。 + +![image.png | left | 747x426](https://cdn.nlark.com/lark/0/2018/png/9687/1540458995051-b3e67fd4-c905-4552-9e52-f54b6ef59941.png "") + +#### 编辑DIFF + +Nacos支持编辑DIFF能力,帮助用户校验修改内容,降低改错带来的风险。 + +![image.png | left | 747x338](https://cdn.nlark.com/lark/0/2018/png/9687/1540457990344-a60e1db3-ca1a-47ed-a03e-f92e37745247.png "") + +#### 示例代码 + +Nacos提供示例代码能力,能够让新手快速使用客户端编程消费该配置,大幅降低新手使用门槛。 + +![image.png | left | 747x223](https://cdn.nlark.com/lark/0/2018/png/9687/1540456991412-01acc11c-8b48-48d8-9032-589ebb9388d9.png "") + +![image.png | left | 747x380](https://cdn.nlark.com/lark/0/2018/png/9687/1540532899571-ccea6b6f-a1e1-44d1-a130-f9afaba01c51.png "") + +#### 监听者查询 + +Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配置变更是否推送到 Client 端。 + +![image.png | left | 747x185](https://cdn.nlark.com/lark/0/2018/png/9687/1540459212236-0abdc558-68b9-4585-b11e-c9a1924ce7ef.png "") + +#### 配置的版本及一键回滚 + +Nacos通过提供配置版本管理及其一键回滚能力,帮助用户改错配置的时候能够快速恢复,降低微服务系统在配置管理上的一定会遇到的可用性风险。 + +![image.png | left | 747x242](https://cdn.nlark.com/lark/0/2018/png/9687/1540459226967-a258b9a7-f95f-41b0-874f-2a0a5da2fc5c.png "") + +![image.png | left | 747x493](https://cdn.nlark.com/lark/0/2018/png/9687/1540459237821-d4c06d16-b356-4953-a6e7-da949b1f3aec.png "") + +## 命名空间管理 + +Nacos 基于Namespace 帮助用户逻辑隔离多个命名空间,这可以帮助用户更好的管理测试、预发、生产等多环境服务和配置,让每个环境的同一个配置(如数据库数据源)可以定义不同的值。 + +![image.png | left | 747x298](https://cdn.nlark.com/lark/0/2018/png/9687/1540519411777-74908cc2-29bc-4270-be58-aed62605228f.png "") + +![image.png | left | 747x206](https://cdn.nlark.com/lark/0/2018/png/9687/1540519427066-effd5153-02c9-4e21-ae9f-1a2e9ae7713e.png "") + +## 登录管理 + +Nacos0.8 版本支持简单登录功能,默认用户名/密码为: `nacos/nacos`。 + +![login](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561262748106-4fc05174-bf70-4806-bcbd-90296c5bcbaa.jpeg) + +### 修改默认用户名/密码方法 + +1. 生成加密密码, 在`com.alibaba.nacos.console.utils.PasswordEncoderUtil.main`函数中,将 nacos 改成你要改成的密码,运行即可得到加密有算法。注意盐值是随机的,所以生成密码每次可能不一样,请不要担心。 + +``` +public class PasswordEncoderUtil { + + public static void main(String[] args) { + System.out.println(new BCryptPasswordEncoder().encode("nacos")); + } +} +``` + +2. 创建用户名或者密码的时候,用指定用户名密码即可。 +``` +INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); +INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN'); +``` + +

+ +### 关闭登录功能 + +Nacos默认控制台在`2.2.2`版本前,无论是否开启[鉴权](../user/auth.md)功能,默认控制台都会跳转到登录页,导致用户被误导认为控制台存在鉴权功能,实际没有开启鉴权,存在安全隐患。 + +经过社区和安全工程师协商讨论,需要在使用Nacos默认控制台时,鉴权开关关闭时将会自动关闭控制台登录功能。 + +因此从`2.2.2`版本开始,当鉴权开关`nacos.core.auth.enabled`关闭时,Nacos默认控制台将不再跳转登录页,同时添加页面提示,提示当前集群未开启鉴权功能。 + +同时针对自定义的[鉴权插件](../../plugin/auth-plugin.md)添加新接口`com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService#isLoginEnabled(默认返回false)`来对自定义插件进行登录页控制。 + +

+ +### 关闭默认控制台 + +部分公司或用户希望关闭默认控制台,使用公司的统一平台进行Nacos的配置和服务管理;或将控制台鉴权和客户端访问的鉴权分离,即控制台操作进行鉴权但客户端请求不进行鉴权。 + +从`2.3.0`版本开始,可以通过`${nacoshome}/conf/application.properties`中的`nacos.console.ui.enabled`来开启或关闭Nacos默认控制台,默认为开启。 + +同时在关闭默认控制台时,默认控制台会读取`${nacoshome}/conf/console-guide.conf`文件中的内容,并在默认控制台中生成引导页,让维护者自定义将使用默认控制台的用户引导向自定义的统一平台上进行操作。 + +### 会话时间 + +默认会话保持时间为30分钟。30分钟后需要重新登录认证。 暂时不支持修改该默认时间。 + +## 社区参与的前端共建 + +在Nacos前端风格、布局的讨论中,社区踊跃投票,最终选择了这套经典黑白蓝风格的皮肤,并且通过我们UED程瑶同学的设计,布局,让交互变得十分自然流畅。 + +在控制台的开发之前,我们通过社区招募到了很多前端同学一起参与了前端代码的开发,在此尤其感谢李晨、王庆、王彦民同学在Nacos前端开发过程中的大力支持! + +## 坚持社区化发展,欢迎加入并贡献社区 + +> 比吐槽更重要的是搭把手,参与社区一起发展Nacos! + +要加入Nacos 微信社区讨论 Nacos 产品的演进,你可以通过扫**超哥**的微信二维码,让“超哥” 帮你拉入 “Nacos社区交流群”。 + +![Screen Shot 2018-06-27 at 13.39.09.png | left](https://cdn.yuque.com/lark/0/2018/png/15914/1530077965587-8f4e3100-bdd4-469a-9ea0-7af7061bc9ef.png "") + +更多与 Nacos 相关的开源项目信息: + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring Project](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) +* [Dubbo](https://github.com/apache/dubbo) +* [Sentinel](https://github.com/alibaba/Sentinel) +* [Spring Cloud](https://projects.spring.io/spring-cloud/) +* [Nepxion Discovery](https://github.com/Nepxion/Discovery) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/deployment.md b/src/content/docs/v3.0/zh-cn/guide/admin/deployment.md new file mode 100644 index 00000000000..c06b51b15e5 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/deployment.md @@ -0,0 +1,99 @@ +--- +title: Nacos支持三种部署模式 +keywords: [Nacos,部署模式] +description: Nacos支持三种部署模式 +sidebar: + order: 1 +--- + +> 该文档即将废弃,推荐查看[运维手册-部署手册-部署手册概览](../../manual/admin/deployment/deployment-overview.mdx)。 + +# Nacos部署环境 + +Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。 + +以下文档中提及的VIP,网卡等所有网络相关概念均处于内部网络环境。 + +# Nacos支持三种部署模式 + +* 单机模式 - 用于测试和单机试用。 +* 集群模式 - 用于生产环境,确保高可用。 +* 多集群模式 - 用于多数据中心场景。 + +# 环境准备 +- 安装好 JDK,需要 1.8 及其以上版本 +- 建议: 2核 CPU / 4G 内存 及其以上 +- 建议: 生产环境 3 个节点 及其以上 + +## 单机模式下运行Nacos + +### Linux/Unix/Mac + +```shell +# Standalone means it is non-cluster Mode. +$ sh startup.sh -m standalone +``` + +### Windows + +```shell +# Standalone means it is non-cluster Mode. +$ cmd startup.cmd -m standalone +``` + +### 单机模式支持mysql + +在0.7版本之前,在单机模式时nacos使用嵌入式数据库实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤: + +- 1.安装数据库,版本要求:5.6.5+ +- 2.初始化mysql数据库,数据库初始化文件:mysql-schema.sql +- 3.修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。 + +``` +spring.datasource.platform=mysql + +db.num=1 +db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true +db.user=nacos_devtest +db.password=youdontknow +``` + +再以单机模式启动nacos,nacos所有写嵌入式数据库的数据都写到了mysql + +## 集群模式下运行Nacos + +[集群模式下运行Nacos](./cluster-mode-quick-start.md) + +## 多集群模式 + +Nacos支持NameServer路由请求模式,通过它您可以设计一个有用的映射规则来控制请求转发到相应的集群,在映射规则中您可以按命名空间或租户等分片请求... + +## 多网卡IP选择 + +当本地环境比较复杂的时候,Nacos服务在启动的时候需要选择运行时使用的IP或者网卡。Nacos从多网卡获取IP参考Spring Cloud设计,通过nacos.inetutils参数,可以指定Nacos使用的网卡和IP地址。目前支持的配置参数有: + +- ip-address参数可以直接设置nacos的ip + +``` +nacos.inetutils.ip-address=10.11.105.155 +``` + +- use-only-site-local-interfaces参数可以让nacos使用局域网ip,这个在nacos部署的机器有多网卡时很有用,可以让nacos选择局域网网卡 + +``` +nacos.inetutils.use-only-site-local-interfaces=true +``` + +- ignored-interfaces支持网卡数组,可以让nacos忽略多个网卡 + +``` +nacos.inetutils.ignored-interfaces[0]=eth0 +nacos.inetutils.ignored-interfaces[1]=eth1 +``` + +- preferred-networks参数可以让nacos优先选择匹配的ip,支持正则匹配和前缀匹配 + +``` +nacos.inetutils.preferred-networks[0]=30.5.124. +nacos.inetutils.preferred-networks[0]=30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))),30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))) +``` diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/management-api.md b/src/content/docs/v3.0/zh-cn/guide/admin/management-api.md new file mode 100644 index 00000000000..bccd5360a35 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/management-api.md @@ -0,0 +1,9 @@ +--- +title: Management API +keywords: [Management,API] +description: In plan with Nacos 1.x.x +sidebar: + order: 4 +--- + +> 该文档已废弃,请查看[运维手册-运维API](../../manual/admin/admin-api.md) diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/monitor-guide.md b/src/content/docs/v3.0/zh-cn/guide/admin/monitor-guide.md new file mode 100644 index 00000000000..7f4c172faa6 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/monitor-guide.md @@ -0,0 +1,248 @@ +--- +title: Nacos 监控手册 +keywords: [Nacos,监控手册] +description: Nacos 监控手册 +sidebar: + order: 6 +--- + +# Nacos 监控手册 + +> 该文档即将废弃,推荐查看[运维手册-监控手册](../../manual/admin/monitor.md)。 + +Nacos 0.8.0版本完善了监控系统,支持通过暴露metrics数据接入第三方监控系统监控Nacos运行状态,目前支持prometheus、elastic search和influxdb,下面结合prometheus和grafana如何监控Nacos。与elastic search和influxdb结合可自己查找相关资料 + +## 搭建Nacos集群暴露metrics数据 +按照[部署文档](./deployment.md)搭建好Nacos集群 + +配置application.properties文件,暴露metrics数据 +``` +management.endpoints.web.exposure.include=* +``` + +访问{ip}:8848/nacos/actuator/prometheus,看是否能访问到metrics数据 + +## 搭建prometheus采集Nacos metrics数据 +下载你想安装的prometheus版本,地址为[download prometheus](https://prometheus.io/download/) + + +### linux & mac +解压prometheus压缩包 +``` +tar xvfz prometheus-*.tar.gz +cd prometheus-* +``` + +修改配置文件prometheus.yml采集Nacos metrics数据 +``` + metrics_path: '/nacos/actuator/prometheus' + static_configs: + - targets: ['{ip1}:8848','{ip2}:8848','{ip3}:8848'] +``` + +启动prometheus服务 +``` +./prometheus --config.file="prometheus.yml" +``` + +### windows + +下载对应的windows版本并解压 + +修改配置文件prometheus.yml采集Nacos metrics数据 +``` + metrics_path: '/nacos/actuator/prometheus' + static_configs: + - targets: ['{ip1}:8848','{ip2}:8848','{ip3}:8848'] +``` + +启动prometheus服务 + +``` +prometheus.exe --config.file=prometheus.yml +``` + +通过访问http://{ip}:9090/graph可以看到prometheus的采集数据,在搜索栏搜索nacos_monitor可以搜索到Nacos数据说明采集数据成功 +![IMAGE](https://img.alicdn.com/tfs/TB1LThVCQvoK1RjSZFwXXciCFXa-2832-1576.png) + +## 搭建grafana图形化展示metrics数据 +和prometheus在同一台机器上安装grafana,使用 yum 安装grafana + +### mac + +``` +brew install grafana +brew services start grafana +``` + +### linux +``` +sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.2.4-1.x86_64.rpm +sudo service grafana-server start +``` + +### windows +参考文档:http://docs.grafana.org/installation/windows/ + + +访问grafana: http://{ip}:3000 + + +配置prometheus数据源 +![IMAGE](https://img.alicdn.com/tfs/TB1bTafCOLaK1RjSZFxXXamPFXa-2832-1568.png) + +导入Nacos grafana监控[模版](https://github.com/nacos-group/nacos-template/blob/master/nacos-grafana.json) +![IMAGE](https://img.alicdn.com/tfs/TB1JadVCPDpK1RjSZFrXXa78VXa-2742-1338.png) + +Nacos监控分为三个模块: +- nacos monitor展示核心监控项 +![IMAGE](https://img.alicdn.com/tfs/TB1PMpUCQvoK1RjSZFDXXXY3pXa-2832-1584.png) +- nacos detail展示指标的变化曲线 +![IMAGE](https://img.alicdn.com/tfs/TB1ZBF4CNjaK1RjSZFAXXbdLFXa-2742-1480.png) +- nacos alert为告警项 +![IMAGE](https://img.alicdn.com/tfs/TB1ALlUCFzqK1RjSZFCXXbbxVXa-2742-1476.png) + +## 配置grafana告警 + +当Nacos运行出现问题时,需要grafana告警通知相关负责人。grafana支持多种告警方式,常用的有邮件,钉钉和webhook方式 + +### 钉钉告警 +钉钉可以通过配置钉钉机器人 +![IMAGE](https://img.alicdn.com/tfs/TB1eJ0RCSzqK1RjSZFjXXblCFXa-2742-1482.png) + +配置钉钉通知url +![IMAGE](https://img.alicdn.com/tfs/TB1ERtQCSzqK1RjSZFjXXblCFXa-2832-1578.png) + +测试告警项 +![IMAGE](https://img.alicdn.com/tfs/TB1KvXPCHPpK1RjSZFFXXa5PpXa-996-504.png) + +### 邮件告警 + +修改defaults.ini配置文件,增加邮件告警 + +``` +#################################### SMTP / Emailing ########################## +[smtp] +enabled = true +host = smtp.126.com:25 +user = xxxxxx +password = xxxxx +;cert_file = +;key_file = +skip_verify = true +from_address = xxxxxx@126.com + +[emails] +;welcome_email_on_sign_up = false +``` + +配置通知邮箱 +![IMAGE](https://img.alicdn.com/tfs/TB12qyhCNnaK1RjSZFtXXbC2VXa-2832-1576.png) + + +## Nacos metrics含义 + +### jvm metrics + +指标|含义 +---|--- +system_cpu_usage|CPU使用率 +system_load_average_1m|load +jvm_memory_used_bytes|内存使用字节,包含各种内存区 +jvm_memory_max_bytes|内存最大字节,包含各种内存区 +jvm_gc_pause_seconds_count|gc次数,包含各种gc +jvm_gc_pause_seconds_sum|gc耗时,包含各种gc +jvm_threads_daemon|线程数 + +### Nacos 监控指标 + +指标|含义 +---|--- +http_server_requests_seconds_count|http请求次数,包括多种(url,方法,code) +http_server_requests_seconds_sum|http请求总耗时,包括多种(url,方法,code) +nacos_timer_seconds_sum|Nacos config水平通知耗时 +nacos_timer_seconds_count|Nacos config水平通知次数 +grpc_server_requests_seconds_max|Nacos grpc请求处理情况记录 +grpc_server_executor{name='maximumPoolSize'}|Nacos grpc服务器线程池的最大线程数 +grpc_server_executor{name='corePoolSize'}|Nacos grpc服务器线程池的核心线程数 +grpc_server_executor{name='taskCount'}|Nacos grpc服务器线程池的任务数量 +grpc_server_executor{name='poolSize'}|Nacos grpc服务器线程池当前线程数量 +grpc_server_executor{name='activeCount'}|Nacos grpc服务器线程池当前活跃的线程数量 +grpc_server_executor{name='completedTaskCount'}|Nacos grpc服务器线程池完成的任务数量 +grpc_server_executor{name='inQueueTaskCount'}|Nacos grpc服务器线程池在任务队列中的任务数量 +nacos_monitor{name='longPolling'}|Nacos config长连接数 +nacos_monitor{name='configCount'}|Nacos config配置个数 +nacos_monitor{name='dumpTask'}|Nacos config配置落盘任务堆积数 +nacos_monitor{name='notifyTask'}|Nacos config配置水平通知任务堆积数 +nacos_monitor{name='getConfig'}|Nacos config读配置统计数 +nacos_monitor{name='publish'}|Nacos config写配置统计数 +nacos_monitor{name='ipCount'}|Nacos naming ip个数 +nacos_monitor{name='domCount'}|Nacos naming域名个数(1.x 版本) +nacos_monitor{name='serviceCount'}|Nacos naming域名个数(2.x 版本) +nacos_monitor{name='failedPush'}|Nacos naming推送失败数 +nacos_monitor{name='avgPushCost'}|Nacos naming平均推送耗时(ms) +nacos_monitor{name='leaderStatus'}|Nacos naming角色状态 +nacos_monitor{name='maxPushCost'}|Nacos naming最大推送耗时(ms) +nacos_monitor{name='mysqlhealthCheck'}|Nacos naming mysql健康检查次数 +nacos_monitor{name='httpHealthCheck'}|Nacos naming http健康检查次数 +nacos_monitor{name='tcpHealthCheck'}|Nacos naming tcp健康检查次数 +nacos_monitor{name='longConnection'}|Nacos基于模块划分的连接数量 + +### nacos 异常指标 +指标|含义 +---|--- +nacos_exception_total{name='db'}|数据库异常 +nacos_exception_total{name='configNotify'}|Nacos config水平通知失败 +nacos_exception_total{name='unhealth'}|Nacos config server之间健康检查异常 +nacos_exception_total{name='disk'}|Nacos naming写磁盘异常 +nacos_exception_total{name='leaderSendBeatFailed'}|Nacos naming leader发送心跳异常 +nacos_exception_total{name='illegalArgument'}|请求参数不合法 +nacos_exception_total{name='nacos'}|Nacos请求响应内部错误异常(读写失败,没权限,参数错误) + +### client metrics +指标|含义 +---|--- +nacos_monitor{name='subServiceCount'}|订阅的服务数 +nacos_monitor{name='pubServiceCount'}|发布的服务数 +nacos_monitor{name='configListenSize'}|监听的配置数 +nacos_client_request_seconds_count|请求的次数,包括多种(url,方法,code) +nacos_client_request_seconds_sum|请求的总耗时,包括多种(url,方法,code) + +## Nacos-Sync监控 + +随着Nacos 0.9版本发布,Nacos-Sync 0.3版本支持了metrics监控,能通过metrics数据观察Nacos-Sync服务的运行状态,提升了Nacos-Sync的在生产环境的监控能力。 +整体的监控体系的搭建参考[Nacos监控手册](./monitor-guide.md) + +## grafana监控Nacos-Sync +和Nacos监控一样,Nacos-Sync也提供了监控模版,导入监控[模版](https://github.com/nacos-group/nacos-template/blob/master/nacos-sync-grafana) + +Nacos-Sync监控同样也分为三个模块: +- nacos-sync monitor展示核心监控项 +![monitor](https://img.alicdn.com/tfs/TB1GeNWKmzqK1RjSZFHXXb3CpXa-2834-1588.png) +- nacos-sync detail和alert展示监控曲线和告警 +![detail](https://img.alicdn.com/tfs/TB1kP8UKbvpK1RjSZPiXXbmwXXa-2834-1570.png) + +## Nacos-Sync metrics含义 +Nacos-Sync的metrics分为jvm层和应用层 +### jvm metrics + +指标|含义 +---|--- +system_cpu_usage|CPU使用率 +system_load_average_1m|load +jvm_memory_used_bytes|内存使用字节,包含各种内存区 +jvm_memory_max_bytes|内存最大字节,包含各种内存区 +jvm_gc_pause_seconds_count|gc次数,包含各种gc +jvm_gc_pause_seconds_sum|gc耗时,包含各种gc +jvm_threads_daemon|线程数 + +## 应用层 metrics + +指标|含义 +---|--- +nacosSync_task_size|同步任务数 +nacosSync_cluster_size|集群数 +nacosSync_add_task_rt|同步任务执行耗时 +nacosSync_delete_task_rt|删除任务耗时 +nacosSync_dispatcher_task|从数据库中分发任务 +nacosSync_sync_task_error|所有同步执行时的异常 diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/nacos2-config-benchmark.md b/src/content/docs/v3.0/zh-cn/guide/admin/nacos2-config-benchmark.md new file mode 100644 index 00000000000..b9f99dbde96 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/nacos2-config-benchmark.md @@ -0,0 +1,181 @@ +--- +title: Nacos2.0服务配置性能测试报告 +keywords: [Nacos,服务,配置,性能] +description: Nacos2.0服务配置性能测试报告 +sidebar: + order: 7 +--- + +# Nacos 2.0.0-ALPHA2 配置性能测试报告 + +## 测试目的 + +1. 长链接各项业务指标的最高值 +2. 长链接相比短链接的差异数据对比 + +## 测试工具 + +我们使用自研的PAS性能评估服务平台进行压测,其原理是基于利用JMeter引擎,使用PAS自动生成的JMeter脚本,进行智能压测。 + + ![Pas图](https://img.alicdn.com/tfs/TB1xCfDDpzqK1RjSZFvXXcB7VXa-692-297.png) + + ## 测试环境 + + ### 1.环境 + +| **指标** | **参数** | +|-------|-------| +|机器|CPU 8核,内存16G| +|集群规模|单机| +|Nacos版本|Nacos 2.0.0-ALPHA2, Nacos 1.4.0| +|数据库|32C128G| + +2.设置启动参数 + +因为grpc使用的直接内存,堆内存相对使用较少,所以jvm参数有所调整 + +#### Nacos2.0 gRPC +``` +JAVA_OPT="${JAVA_OPT} -server -Xms9216m -Xmx9216m -XX:MaxDirectMemorySize=4096m -XX:NewSize=4096m -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics -DisPushContent=false -XX:MaxNewSize=4096m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nacos/logs/java_heapdump.hprof -XX:-UseLargePages -Xloggc:/home/admin/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -DQUERYTIMEOUT=90 -XX:SurvivorRatio=10 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nacos/logs -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:CMSMaxAbortablePrecleanTime=5000 -XX:CMSInitiatingOccupancyFraction=74 -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=8 -Dnacos.core.auth.enabled=false " +``` + +#### Nacos1.X HTTP + +``` +-server -Xms12880m -Xmx12880m -XX:MaxDirectMemorySize=1024m -XX:NewSize=1024m -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics -DisPushContent=false -XX:MaxNewSize=4096m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nacos/logs/java_heapdump.hprof -XX:-UseLargePages -Xloggc:/home/admin/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -DQUERYTIMEOUT=90 -XX:SurvivorRatio=10 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSClassUnloadingEnabled -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nacos/logs -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:CMSMaxAbortablePrecleanTime=5000 -XX:CMSInitiatingOccupancyFraction=74 -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=8 -Dnacos.core.auth.enabled=false -Dnacos.member.list= -Djava.ext.dirs=/opt/taobao/java/jre/lib/ext:/opt/taobao/java/lib/ext -Xloggc:/home/admin/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M +``` + +## 测试场景 + +以下测试场景都是服务配置重要接口: + +- 验证Nacos服务发布配置的能力 +- 验证Nacos服务获取配置的能力 +- 验证Nacos服务监听配置的能力 +- 验证Nacos服务长连接容量能力 + +测试方式均是在相同的环境下,使用相同的压力进行测试,分别比对Nacos2.X版本和Nacos1.X版本的性能差异。 + +## 测试数据 + +### 1. 发布配置 + +#### Nacos2.0 + +|tps|500|1000|1200|1500|2000|2500|3000| +|---|---|---|---|---|---|---|---| +|avg rt(ms)|7|8|12|9|9|10.89|1044| +|80% rt(ms)|7.9|8|11|9|9|10.69|1581| +|95% rt(ms)|8.7|11|25|15|14|24.74|2966| +|cpu|12|22|28|36|47|55|62| +|load|0.5|1.5|1.5|1.5|3.5|4|5 + +#### Nacos1.X + +|tps|500|1000|1200|1400|2000|2500|3000| +|---|---|---|---|---|---|---|---| +|avg rt(ms)|9|8.67|8|9|10|11.88|1038| +|80% rt(ms)|9|9.4|10|9|10|12.48|1090| +|95% rt(ms)|11|11.4|12|14|18|25.7|1170| +|cpu|14|25|30|35|50|60|65| +|load|0.9|1.4|2|2.5|3|2.5|3.7| + + +#### 结果分析 +发布配置两者差别不大,TPS 在2500tps左右出现拐点,http和长链接通道的cpu消耗分布类似。长链接对发布配置提升不大。 + +### 2. 获取配置 + +随机获取200个 5K大小配置 + +#### Nacos2.0 + +|tps|2000|4000|6000|8000|10000|14000|18000(实际15000)| +|---|---|---|---|---|---|---|---| +|avg rt(ms)|3.3|4|3.5|5|7|26|133| +|80% rt(ms)|2.2|2.2|2.5|2.5|4|41|174| +|95% rt(ms)|3.3|4.8|5.4|24|38|93|238| +|cpu|12|25|37|48|65|83|85| +|load|1.2|2|3|4|5|20|36| + +#### Nacos1.X + +|tps|2000|4000|6000|8000|10000|14000(实际11000)| +|---|---|---|---|---|---|---|---| +|avg rt(ms)|3|7.4|12|22|46|176| +|80% rt(ms)|1.8|2|4|7|35|185| +|95% rt(ms)|4.4|6|15|33|118|380| +|cpu|15|30|40|52|60|70| +|load|1.1|2.2|2.5|4|5.5|9| + +#### 结果分析 +长链接支撑的读QPS提升50%,CPU消耗降低50%,http的CPU消耗50%在于请求地址解析 + +### 3. 监听配置 + +两者均为单链接监听200配置。 + +#### Nacos2.0 + +|tps|1500|3000|6000| +|---|---|---|---| +|cpu|30|30|60| +ygc|0|3.75s/次,7次 0.5秒|3s/次,10次 1.4秒| +cmsgc|0|0|0| +load|6|14|20| + +#### Nacos1.X + +|tps|3000|4000|6000| +|---|---|---|---| +cpu|80%|90%|80%| +ygc|3s一次,10次耗时0.5s|2s一次,15次耗时1.5s|1.5s一次,20次耗时1.3秒| +cmsgc|3s一次,10次耗时18s|4.5一次,7次耗时10s|7.5s一次,4次耗时5s| +load|6|8|11| + +#### 结果分析 +gRPC以1500tps持续变更推送,可以保证系统指标稳定,超过1500tps,系统内存和load持续升高,但完全没有CMS GC,CPU也维持在较低的水准。 Http 则有较高的CMS GC,GC耗时严重,CPU损耗高。 + +### 4. 长连接容量测试 + +两者均为单链接监听200配置。快上为同时进行大量配置发布。 + +#### Nacos2.0 + +|count|6000|8000|12000|15000|21000|31500|42000| +|---|---|---|---|---|---|---|---| +|快上时cpu|40|60|80|77|79|80|74| +|快上时load|1.5|3|3.3|3.6|5.45|5.6|6.3| +|快上耗时|55s|55s|76|100|80|140|130| +|稳定时cpu|1|1|1|1.3|2.8|1.7|2.1| +|稳定时load|0.3|0.5|0.5|0.8|0.9|0.8|0.8| +|gc|稳定后无GC消耗|稳定后无GC消耗|稳定后无GC消耗|稳定后无GC消耗|稳定后无GC消耗|稳定后无GC消耗|稳定后无GC消耗| + +#### Nacos1.X + +|count|6000|8000|12000|15000|21000|31500|42000| +|---|---|---|---|---|---|---|---| +快上时cpu|80|-|-|-|-|-|-| +快上时load|8|-|-|-|-|-|-| +快上耗时|100s|-|-|-|-|-|-| +稳定时cpu|60|-|-|-|-|-|-| +稳定时load|1|-|-|-|-|-|-| +gc|cmsgc频繁,4秒一次|-|-|-|-|-|-| + +#### 结果分析 + +当连接量达到600时,Nacos1.X的CMS GC已经十分严重,4s一次,基本已经达到极限;反观Nacos2.0,可以继续增长,单从支撑长链接数量角度,Nacos2.0比Nacos1.X支撑7倍以上长链接。 + +## 测试结论 + +单项基础接口压测 + +- Nacos2.0读QPS 14000QPS,对比Nacos1.X QPS 8000 提升75%。 +- Nacos2.0写TPS 2500TPS,对比Nacos1.X无明显提升。 +- Nacos2.0支撑长链接40000以上,对比Nacos1.X提升7倍以上。 +- Nacos2.0变更推送1500/s, 对比Nacos1.X无明显提升。 + +**注意** + +- 本测试为对比Nacos1.X版本的测试场景,仅测试单核心接口的能力值,**真实模拟场景压测** 将在后续版本给出; +- 本测试供给大家作为参考,如有不足或偏差,请指正! 如果对性能有其他需求,可以给我们提issue。 diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/nacos2-naming-benchmark.md b/src/content/docs/v3.0/zh-cn/guide/admin/nacos2-naming-benchmark.md new file mode 100644 index 00000000000..d6149b2c00b --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/nacos2-naming-benchmark.md @@ -0,0 +1,131 @@ +--- +title: Nacos2.0服务发现性能测试报告 +keywords: [Nacos,服务,发现,性能] +description: Nacos2.0服务发现性能测试报告 +sidebar: + order: 8 +--- + +# Nacos2.0.0-ALPHA2 服务发现性能测试报告 + +## 测试目的 + +Nacos2.0对连接模型,服务发现的数据模型也运作模式进行了大范围的重构,因此需要在相同或类似的场景下,了解Nacos2的服务发现性能负载和容量与Nacos1的区别,帮助用户更快的运用评估Nacos系统负荷。 + +Nacos1.0性能测试参考 [服务发现性能测试报告](https://nacos.io/docs/v1/nacos-naming-benchmark) + +## 测试工具 + +我们使用自研的PAS性能评估服务平台进行压测,其原理是基于利用JMeter引擎,使用PAS自动生成的JMeter脚本,进行智能压测。 + +![Pas图](https://img.alicdn.com/tfs/TB1xCfDDpzqK1RjSZFvXXcB7VXa-692-297.png) + +## 测试环境 + +### 1. 环境 + +| **指标** | **参数** | +|-----------|-----------| +|机器| CPU 8核,内存16G| +|集群规模 |3节点| +|Nacos版本| 服务端:Nacos2.0.0-ALPHA2, 客户端:Nacos2.0.0-ALPHA2| + +### 2.设置启动参数 + +``` +${JAVA_HOME}/bin/java -DembeddedStorage=true -server -Xms10g -Xmx10g -Xmn4g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/nacos/logs/java_heapdump.hprof -XX:-UseLargePages -Dnacos.member.list= -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext -Xloggc:/home/admin/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dloader.path=/home/admin/nacos/plugins/health,/home/admin/nacos/plugins/cmdb -Dnacos.home=/home/admin/nacos -jar /home/admin/nacos/target/nacos-server.jar --spring.config.additional-location=file:/home/admin/nacos/conf/ --logging.config=/home/admin/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288 nacos.nacos +``` + +## 测试场景 + +以下测试场景都是服务发现重要接口: + +- 验证Nacos服务发现注册实例的能力 +- 验证Nacos服务发现查询实例的能力 +- 验证Nacos服务发现注销实例的能力 + +## 测试数据 + +### 1. 注册实例 + +施压机模拟100个客户端同时发起注册服务,每个客户端一条长连接,每个客户端注册1W个服务。总数100W个服务及实例。 + +注册完成之后每个客户端继续不停进行注册请求,模拟重复注册请求(会进行更新替换),同时记录下整个过程中的相关数据。 + +#### 相关API + +`NamingService.registerInstance(String serviceName, Instance instance)`  + +#### 结果数据如下 + +| 施压机数量 | 每台线程数 | 平均TPS | 平均RT | 最小RT | 最大RT | 80%RT(ms) | 95%RT(ms) | 99%RT(ms) | CPU使用率 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 1 | 100 | 7256.32 | 13.14 | 0.39 | 2522.25 | 6.72 | 12.86 | 126.33 | 80% | +| 2 | 50 | 16418.04 | 5.8 | 0.41 | 3906.77 | 4.0 | 8.88 | 48.84 | 90% | +| 5 | 20 | 26784.84 | 3.6 | 0.38 | 1606.41 | 3.82 | 8.91 | 30.62 | 90% | + +#### 结果分析 + +相较Nacos1.X版本,注册性能总体提升至少2倍,在服务端机能减半的情况下,服务实例数基本一致的情况下,TPS仍能做到2倍左右的提高。 + +### 2. 查询实例 + +施压机先模拟发起注册服务,总数10W个服务,每个服务10个实例,总数100W实例。 + +注册完成后,模拟100个客户端同时不停进行随机服务查询请求,并且有实例长度校验。同时记录下整个过程中的相关数据。 + +#### 相关API + +`NacosNamingService.getAllInstances(String serviceName, boolean subscribe)`  + +**注意** subscribe 为 false 进行测试,否则将会优先查询客户端缓存。 + +#### 结果数据如下 + +| 施压机数量 | 每台线程数 | 平均TPS | 平均RT | 最小RT | 最大RT | 80%RT(ms) | 95%RT(ms) | 99%RT(ms) | CPU使用率 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 1 | 100 | 12998.46 | 7.54 | 0.55 | 213.86 | 9.68 | 10.69 | 27.92 | 40% | +| 2 | 50 | 12785.01 | 7.93 | 0.38 | 900.48 | 8.34 | 14.18 | 33.04 | 40% | +| 2 | 100 | 18451.78 | 10.63 | 0.6 | 829.42 | 11.95 | 23.79 | 44.19 | 45% | +| 5 | 20 | 30680.48 | 3.12 | 0.46 | 1138.38 | 4.33 | 5.9 | 9.57 | 50% | + +#### 结果分析 + +相较Nacos1.X版本,查询性能总体提升至少3倍,在服务端机能减半的情况下,服务实例数基本一致的情况下,TPS仍能做到3倍左右的提高,单机多线程场景甚至有10倍的提升。 + +### 3. 注销实例 + +施压机先模拟100个客户端同时发起注册服务,每个客户端一条长连接,每个客户端注册1W个服务。总数100W个服务及实例。 + +注册完成后,模拟使用相同100个客户端同时不停进行随机服务注销请求,同时记录下整个过程中的相关数据。 + +#### 相关API + +`NacosNamingService.deregisterInstance(String serviceName, String ip, int port)`  + +#### 结果数据如下 + +| 施压机数量 | 每台线程数 | 平均TPS | 平均RT | 最小RT | 最大RT | 80%RT(ms) | 95%RT(ms) | 99%RT(ms) | CPU使用率 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| 1 | 100 | 9614.96 | 9.88 | 0.41 | 1115.27 | 8.85 | 15.32 | 104.76 | 70% | +| 2 | 50 | 22252.07 | 4.28 | 0.39 | 856.1 | 4.03 | 5.65 | 31.02 | 90% -> 60%  | +| 5 | 20 | 29393.8 | 2.55 | 0.42 | 741.09 | 2.67 | 8.91 | 15.85 | 90% -> 60% | + +#### 结果分析 + +相较Nacos1.X版本,注销性能总体提升至少2倍,在服务端机能减半的情况下,服务实例数基本一致的情况下,TPS仍能做到2倍左右的提高。 +关于CPU由90% 降低为 60%的场景, 是由于随着注销的的服务和实例增多,重复注销的操作变得频繁,未命中服务和实例的操作会被快速返回且操作量小,因此CPU下降、TPS相对注册略高。 + +## 测试结论 + +Nacos2服务发现性能测试都是针对重点功能,通过对3节点规模集群进行压测,可以看到接口性能负载和容量,以及对比相同/类似场景下Nacos1.X版本的提升。 + +1. 压测时服务及实例容量达到百万级,集群运行持续稳定,达到预期;(该场景没有计算频繁变更导致的频繁推送内容,仅单纯计算容量上线,附带推送的真实场景将在下轮压测报告中给出) +2. 注册/注销实例TPS达到 26000 以上,总体较Nacos1.X提升至少2倍,接口达到预期; +3. 查询实例TPS能够达到 30000 以上,总体较Nacos1.X提升3倍左右,接口达到预期; + +**注意** + +- 本次只测试临时实例注册/查询/注销,未涉及持久实例; +- 本测试为对比Nacos1.X版本的测试场景,仅测试单核心接口的能力值,**真实模拟场景压测** 将在后续版本给出; +- 本测试供给大家作为参考,如有不足或偏差,请指正! 如果对性能有其他需求,可以给我们提issue。 diff --git a/src/content/docs/v3.0/zh-cn/guide/admin/system-configurations.md b/src/content/docs/v3.0/zh-cn/guide/admin/system-configurations.md new file mode 100644 index 00000000000..9110e61598a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/admin/system-configurations.md @@ -0,0 +1,107 @@ +--- +title: Nacos系统参数介绍 +keywords: [Nacos,系统参数] +description: Nacos系统参数介绍 +sidebar: + order: 3 +--- + +# Nacos 系统参数介绍 + +> 该文档即将废弃,Server的参数推荐查看[运维手册-系统参数](../../manual/admin/system-configurations.md),Client的参数推荐查看[用户手册-客户端参数 TODO](#nacos-java-client) + +## Nacos Server + +对于Server端来说,一般是设置在`{nacos.home}/conf/application.properties`里,如果参数名后标注了(-D)的,则表示是 JVM 的参数,需要在`{nacos.home}/bin/startup.sh`里进行相应的设置。例如像设置 nacos.home 的值,可以在`{nacos.home}/bin/startup.sh`进行如下设置: + +``` +JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}" +``` + +### 全局参数 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|nacos.home(-D)| Nacos的根目录 | 目录路径| Nacos安装的目录 | >= 0.1.0 | +|nacos.standalone(-D)| 是否在单机模式 | true/false | false | >= 0.1.0 | +|nacos.functionMode(-D)| 启动模式,支持只启动某一个模块,不设置时所有模块都会启动 | config/naming/空 | 空 | >= 0.9.0 | +|nacos.inetutils.prefer-hostname-over-ip| `cluster.conf`里是否应该填`hostname`| true/false| false | >= 0.3.0 | +|nacos.inetutils.ip-address | 本机IP,该参数设置后,将会使用这个IP去`cluster.conf`里进行匹配,请确保这个IP的值在`cluster.conf`里是存在的 | 本机IP| null | >= 0.3.0 | + +### Naming模块 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|nacos.naming.data.warmup| 是否在Server启动时进行数据预热 | true/false | false | >= 1.0.2 | +|nacos.naming.expireInstance| 是否自动摘除临时实例 | true/false | true | >= 1.0.2 | +|nacos.naming.distro.taskDispatchPeriod| 同步任务生成的周期,单位为毫秒 | 正整数 | 2000 | >= 1.0.2 | +|nacos.naming.distro.batchSyncKeyCount| 同步任务每批的key的数目 | 正整数 | 1000 | >= 1.0.2 | +|nacos.naming.distro.syncRetryDelay| 同步任务失败的重试间隔,单位为毫秒 | 正整数 | 5000 | >= 1.0.2 | + +除了上面列到的在`application.properties`里配置的属性,还有一些可以在运行时调用接口来进行调节,这些参数都在[Open API](../user/open-api.md)里的```查看系统当前数据指标```这个API里有声明。 + +### Config模块 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|db.num| 数据库数目 | 正整数 | 0 | >= 0.1.0 | +|db.url.0| 第一个数据库的URL | 字符串 | 空 | >= 0.1.0 | +|db.url.1| 第二个数据库的URL | 字符串 | 空 | >= 0.1.0 | +|db.user| 数据库连接的用户名 | 字符串 | 空 | >= 0.1.0 | +|db.password| 数据库连接的密码 | 字符串 | 空 | >= 0.1.0 | +|spring.datasource.platform|数据库类型|字符串|mysql|>=1.3.0| +|db.pool.config.xxx| 数据库连接池参数,使用的是hikari连接池,参数与hikari连接池相同,如`db.pool.config.connectionTimeout`或`db.pool.config.maximumPoolSize`|字符串|同hikariCp对应默认配置|>=1.4.1| + +当前数据库配置支持多数据源。通过`db.num`来指定数据源个数,`db.url.index`为对应的数据库的链接。`db.user`以及`db.password`没有设置`index`时,所有的链接都以`db.user`和`db.password`用作认证。如果不同数据源的用户名称或者用户密码不一样时,可以通过符号`,`来进行切割,或者指定`db.user.index`,`db.user.password`来设置对应数据库链接的用户或者密码。需要注意的是,当`db.user`和`db.password`没有指定下标时,因为当前机制会根据`,`进行切割。所以当用户名或者密码存在`,`时,会把`,`切割后前面的值当成最后的值进行认证,会导致认证失败。 + +Nacos从1.3版本开始使用HikariCP连接池,但在1.4.1版本前,连接池配置由系统默认值定义,无法自定义配置。在1.4.1后,提供了一个方法能够配置HikariCP连接池。 +`db.pool.config`为配置前缀,`xxx`为实际的hikariCP配置,如`db.pool.config.connectionTimeout`或`db.pool.config.maximumPoolSize`等。更多hikariCP的配置请查看[HikariCP](https://github.com/brettwooldridge/HikariCP) +需要注意的是,url,user,password会由`db.url.n`,`db.user`,`db.password`覆盖,driverClassName则是默认的MySQL8 driver(该版本mysql driver支持mysql5.x) + +### CMDB模块 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|nacos.cmdb.loadDataAtStart| 是否打开CMDB | true/false | false | >= 0.7.0 | +|nacos.cmdb.dumpTaskInterval| 全量dump的间隔,单位为秒 | 正整数 | 3600 | >= 0.7.0 | +|nacos.cmdb.eventTaskInterval| 变更事件的拉取间隔,单位为秒 | 正整数 | 10 | >= 0.7.0 | +|nacos.cmdb.labelTaskInterval| 标签集合的拉取间隔,单位为秒 | 正整数 | 300 | >= 0.7.0 | + +## Nacos Java Client + +> TODO: 单独一篇文档,放在用户指南中 + +客户端的参数分为两种,一种是通过-D参数进行指定的配置,一种是构造客户端时,通过`Properties`对象指定的配置,以下没有带-D标注的都是通过`Properties`注入的配置。 + +### 通用参数 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|endpoint| 连接Nacos Server指定的连接点,可以参考[文档](https://nacos.io/blog/case-address-server/) | 域名 | 空 | >= 0.1.0 | +|endpointPort| 连接Nacos Server指定的连接点端口,可以参考[文档](https://nacos.io/blog/case-address-server/) | 合法端口号 | 空 | >= 0.1.0 | +|namespace| 命名空间的ID | 命名空间的ID | config模块为空,naming模块为public | >= 0.8.0 | +|serverAddr| Nacos Server的地址列表,这个值的优先级比endpoint高 | ip:port,ip:port,... | 空 | >= 0.1.0 | +|JM.LOG.PATH(-D)| 客户端日志的目录 | 目录路径 | 用户根目录 | >= 0.1.0 | + +### Naming客户端 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|namingLoadCacheAtStart| 启动时是否优先读取本地缓存 | true/false | false | >= 1.0.0 | +|namingCacheRegistryDir| 指定缓存子目录,位置为 `.../nacos/{SUB_DIR}/naming` | 子目录路径 | 空字符串 | >=2.0.2 +|namingClientBeatThreadCount| 客户端心跳的线程池大小 | 正整数 | 机器的CPU数的一半 | >= 1.0.0 | +|namingPollingThreadCount| 客户端定时轮询数据更新的线程池大小 | 正整数 | 机器的CPU数的一半 | >= 1.0.0 | +|com.alibaba.nacos.naming.cache.dir(-D)| 客户端缓存目录 | 目录路径 | `{user.home}/nacos/naming` | >= 1.0.0 | +|com.alibaba.nacos.naming.log.level(-D)| Naming客户端的日志级别 | info,error,warn等 | info | >= 1.0.0 | +|com.alibaba.nacos.client.naming.tls.enable(-D)| 是否打开HTTPS | true/false | false | >= 1.0.0 | + +### Config客户端 + +|参数名 |含义 | 可选值 | 默认值| 支持版本 | +|------|------|-----------|-----------------|-------| +|configLongPollTimeout(config.long-poll.timeout 1.0.1版本)| 长轮询的超时时间,单位为毫秒 | 正整数 | 30000 | >= 1.0.2 | +|configRetryTime(config.retry.time 1.0.1版本)| 长轮询任务重试时间,单位为毫秒 | 正整数 | 2000 | >= 1.0.2 | +|maxRetry| 长轮询的重试次数 | 正整数 | 3 | >= 1.0.2 | +|enableRemoteSyncConfig| 监听器首次添加时拉取远端配置 | 布尔值 | false | >= 1.0.2 | +|com.alibaba.nacos.config.log.level(-D)| Config客户端的日志级别 | info,error,warn等 | info | >= 1.0.0 | +|JM.SNAPSHOT.PATH(-D)| 客户端缓存目录 | 目录路径 | {user.home}/nacos/config | >= 1.0.0 | diff --git a/src/content/docs/v3.0/zh-cn/guide/user/auth.md b/src/content/docs/v3.0/zh-cn/guide/user/auth.md new file mode 100644 index 00000000000..ae4b4059f25 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/auth.md @@ -0,0 +1,236 @@ +--- +title: 权限校验 +keywords: [Authorization] +description: Authorization +sidebar: + order: 5 +--- + +> 该文档即将废弃,若想查看服务端如何开启鉴权功能推荐查看[运维手册-鉴权手册](../../manual/admin/auth.mdx); 若想查看客户端如何配置鉴权信息推荐查看[用户手册-配置鉴权信息](../../manual/user/auth.mdx)。 + +> 注意 +> - Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。 +> - Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。 +> - 如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做进行[自定义插件开发](../../plugin/auth-plugin.md)。 + +# 鉴权 + +## 相关参数 + +|参数名|默认值|启止版本|说明| +|-----|------|------|----| +|nacos.core.auth.enabled|false|1.2.0 ~ latest|是否开启鉴权功能| +|nacos.core.auth.system.type|nacos|1.2.0 ~ latest|鉴权类型| +|nacos.core.auth.plugin.nacos.token.secret.key|SecretKey012345678901234567890123456789012345678901234567890123456789(2.2.0.1后无默认值)|2.1.0 ~ latest|默认鉴权插件用于生成用户登陆临时accessToken所使用的密钥,**使用默认值有安全风险**| +|nacos.core.auth.plugin.nacos.token.expire.seconds|18000|2.1.0 ~ latest|用户登陆临时accessToken的过期时间| +|nacos.core.auth.enable.userAgentAuthWhite|false|1.4.1 ~ latest|是否使用useragent白名单,主要用于适配老版本升级,**置为true时有安全风险**| +|nacos.core.auth.server.identity.key|serverIdentity(2.2.1后无默认值)|1.4.1 ~ latest|用于替换useragent白名单的身份识别key,**使用默认值有安全风险**| +|nacos.core.auth.server.identity.value|security(2.2.1后无默认值)|1.4.1 ~ latest|用于替换useragent白名单的身份识别value,**使用默认值有安全风险**| +|~~nacos.core.auth.default.token.secret.key~~|SecretKey012345678901234567890123456789012345678901234567890123456789|1.2.0 ~ 2.0.4|同`nacos.core.auth.plugin.nacos.token.secret.key`| +|~~nacos.core.auth.default.token.expire.seconds~~|18000|1.2.0 ~ 2.0.4|同`nacos.core.auth.plugin.nacos.token.expire.seconds`| + +## 默认控制台登录页 + +2.2.2版本之前的Nacos默认控制台,无论服务端是否开启鉴权,都会存在一个登录页;这导致很多用户被**误导**认为Nacos默认是存在鉴权的。在社区安全工程师的建议下,Nacos自**2.2.2**版本开始,在未开启鉴权时,默认控制台将不需要登录即可访问,同时在控制台中给予提示,提醒用户当前集群未开启鉴权。 + +在用户开启鉴权后,控制台才需要进行登录访问。 同时针对不同的鉴权插件,提供新的接口方法,用于提示控制台是否开启登录页;同时在`2.2.3`版本后,Nacos可支持关闭开源控制台,并引导到用户自定义的Nacos控制台,详情可查看[Nacos鉴权插件-服务端插件](../../plugin/auth-plugin.md)及[控制台手册-关闭登录功能](../admin/console-guide.md#1.1) +## 服务端如何开启鉴权 + +### 非Docker环境 + +按照官方文档配置启动,默认是不需要登录的,这样会导致配置中心对外直接暴露。而启用鉴权之后,需要在使用用户名和密码登录之后,才能正常使用nacos。 + +开启鉴权之前,application.properties中的配置信息为: +```java +### If turn on auth system: +nacos.core.auth.enabled=false +``` +开启鉴权之后,application.properties中的配置信息为: +```java +### If turn on auth system: +nacos.core.auth.system.type=nacos +nacos.core.auth.enabled=true +``` + +#### 自定义密钥 + +开启鉴权之后,你可以自定义用于生成JWT令牌的密钥,application.properties中的配置信息为: + +> 注意: +> 1. 文档中提供的密钥为公开密钥,在实际部署时请更换为其他密钥内容,防止密钥泄漏导致安全风险。 +> 2. 在2.2.0.1版本后,社区发布版本将移除以文档如下值作为默认值,需要自行填充,否则无法启动节点。 +> 3. 密钥需要保持节点间一致,长时间不一致可能导致403 invalid token错误。 + +```properties +### The default token(Base64 String): +nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 + +### 2.1.0 版本后 +nacos.core.auth.plugin.nacos.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789 +``` + +自定义密钥时,推荐将配置项设置为**Base64编码**的字符串,且**原始密钥长度不得低于32字符**。例如下面的的例子: + +```properties +### The default token(Base64 String): +nacos.core.auth.default.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= + +### 2.1.0 版本后 +nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= +``` + +> 注意:鉴权开关是修改之后立马生效的,不需要重启服务端。动态修改`token.secret.key`时,请确保token是有效的,如果修改成无效值,会导致后续无法登录,请求访问异常。 + +### Docker环境 + +#### 官方镜像 + +如果使用官方镜像,请在启动docker容器时,添加如下环境变量 + +```powershell +NACOS_AUTH_ENABLE=true +``` + +例如,可以通过如下命令运行开启了鉴权的容器: + +```powershell +docker run --env PREFER_HOST_MODE=hostname \ + --env MODE=standalone \ + --env NACOS_AUTH_ENABLE=true \ + -e NACOS_AUTH_TOKEN=SecretKeyM1Z2WDc4dnVyZkQ3NmZMZjZ3RHRwZnJjNFROdkJOemEK \ + -e NACOS_AUTH_IDENTITY_KEY=mpYGXyu7 \ + -e NACOS_AUTH_IDENTITY_VALUE=mpYGXyu7 \ + -p 8848:8848 nacos/nacos-server +``` + +除此之外,还可以添加其他鉴权相关的环境变量信息: + +| name | description | option | +| ----------------------------- | -------------------------------------- | -------------------------------------- | +| NACOS_AUTH_ENABLE | 是否开启权限系统 | 默认:false| +| NACOS_AUTH_TOKEN_EXPIRE_SECONDS | token 失效时间 | 默认:18000 | +| NACOS_AUTH_TOKEN | token | 默认:SecretKey012345678901234567890123456789012345678901234567890123456789 | +| NACOS_AUTH_CACHE_ENABLE | 权限缓存开关 ,开启后权限缓存的更新默认有15秒的延迟 | 默认 : false | + + +然后运行docker-compose构建命令,例如 +```powershell +docker-compose -f example/standalone-derby.yaml up +``` + +#### 自定义镜像 + +如果选择自定义镜像,请在构建镜像之前,修改nacos工程中的application.properties文件, + +将下面这一行配置信息 +``` +nacos.core.auth.enabled=false +``` +修改为 +``` +nacos.core.auth.system.type=nacos +nacos.core.auth.enabled=true +``` +然后再配置nacos启动命令。 + +## 客户端如何进行鉴权 + +### Java SDK鉴权 + +在构建“Properties”类时,需传入用户名和密码。 +```java +properties.put("username","${username}"); +properties.put("password","${password}"); +``` +#### 示例代码 +```java +try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + // if need username and password to login + properties.put("username","nacos"); + properties.put("password","nacos"); + + ConfigService configService = NacosFactory.createConfigService(properties); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` +### 其他语言的SDK鉴权 + +待补充 + +### Open-API鉴权 +首先需要使用用户名和密码登陆nacos。 + +```plain +curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos' +``` + +若用户名和密码正确,返回信息如下: + +``` +{"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyOTE2Nn0.2TogGhhr11_vLEjqKko1HJHUJEmsPuCxkur-CfNojDo","tokenTtl":18000,"globalAdmin":true} +``` + +接下来进行配置信息或服务信息时,应当使用该accessToken鉴权,在url后添加参数accessToken=${accessToken},其中${accessToken}为登录时返回的token信息,例如 + +```plain +curl -X GET '127.0.0.1:8848/nacos/v1/cs/configs?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&dataId=nacos.example.1&group=nacos_group' +``` + +```plain +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=n1' +``` + +## 开启Token缓存功能 + +服务端自2.2.1版本后,默认鉴权插件模块支持token缓存功能,可参见ISSUE #9906 +``` +https://github.com/alibaba/nacos/issues/9906 +``` +#### 背景 +无论是客户端SDK还是OpenAPI,在调用login接口获取accessToken之后,携带accessToken访问服务端,服务端解析Token进行鉴权。解析的动作比较耗时,如果想要提升接口的性能,可以考虑开启缓存Token的功能,用字符串比较代替Token解析。 + +#### 开启方式 +``` +nacos.core.auth.plugin.nacos.token.cache.enable=true +``` + +#### 注意事项 +在开启Token缓存功能之前,服务端对每一个携带用户名密码访问login接口的请求都会生成新的token,接口的返回值中的tokenTtl字段跟服务端配置文件中设置的值相等,配置如下: +``` +nacos.core.auth.plugin.nacos.token.expire.seconds=18000 +``` +在开启Token缓存功能之后,服务端对每一个携带用户名密码访问login接口的请求,会先检查缓存中是否存在该用户名对应的token。若不存在,生成新的Token,插入缓存再返回;若存在,返回该token,此时tokenTtl字段的值为配置文件中设置的值减去该Token在缓存中存留的时长。 +如果Token在缓存中存留的时长超过配置文件设置的值的90%,当login接口收到请求时,尽管缓存中存在该用户名对应的Token,服务端会重新生成Token返回给请求方,并更新缓存。因此,最差情况下,请求方收到的tokenTtl只有配置文件设置的值的10%。 + +## 开启服务身份识别功能 + +开启鉴权功能后,服务端之间的请求也会通过鉴权系统的影响。考虑到服务端之间的通信应该是可信的,因此在1.2~1.4.0版本期间,通过User-Agent中是否包含Nacos-Server来进行判断请求是否来自其他服务端。 + +但这种实现由于过于简单且固定,导致可能存在安全问题。因此从1.4.1版本开始,Nacos添加服务身份识别功能,用户可以自行配置服务端的Identity,不再使用User-Agent作为服务端请求的判断标准。 + +开启方式: + +``` +### 开启鉴权 +nacos.core.auth.enabled=true + +### 关闭使用user-agent判断服务端请求并放行鉴权的功能 +nacos.core.auth.enable.userAgentAuthWhite=false + +### 配置自定义身份识别的key(不可为空)和value(不可为空) +nacos.core.auth.server.identity.key=example +nacos.core.auth.server.identity.value=example +``` + +** 注意 ** 所有集群均需要配置相同的`server.identity`信息,否则可能导致服务端之间数据不一致或无法删除实例等问题。 + +### 旧版本升级 + +考虑到旧版本用户需要升级,可以在升级期间,开启`nacos.core.auth.enable.userAgentAuthWhite=true`功能,待集群整体升级到1.4.1并稳定运行后,再关闭此功能。 diff --git a/src/content/docs/v3.0/zh-cn/guide/user/failover.md b/src/content/docs/v3.0/zh-cn/guide/user/failover.md new file mode 100644 index 00000000000..13613788f23 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/failover.md @@ -0,0 +1,144 @@ +--- +title: Java客户端容灾 +keywords: [容灾] +description: Java客户端容灾用户指南 +sidebar: + order: 7 + hidden: true +--- + +# Java客户端容灾 + +我们可以在客户端开启本地容灾,用来应对Nacos服务端出现问题时,保证客户端的数据和接口稳定性。 + +这里有两个使用场景: + +1. 在Nacos服务端发布的时候,我们主动把容灾打开,这样客户端只使用本地容灾数据,Nacos服务的数据抖动或者数据错误都不会影响客户端,我们在Nacos服务端升级完成并且数据验证没问题后再关闭容灾; +2. 在Nacos运行期间,突然出现接口不可用或者数据异常,我们可以快速的开启容灾,让客户端使用容灾数据,减小服务受影响的窗口,等Nacos服务端恢复后再关闭容灾; + +具体方案可以参考:https://github.com/alibaba/nacos/issues/11053 + +## 流程简介 + +image + +如上图所示,客户端的查询请求都会先经过FailoverReactor,如果FailoverReactor有数据,则直接使用,从而忽略掉Nacos Server返回的数据;如果FailoverReactor里面没有数据,则走正常流程,从ServiceInfoHolder里读取缓存; + +## 磁盘容灾 + +FailoverReactor里的数据可以使用不同的数据源,默认的数据源为磁盘。 + +### 磁盘容灾文件目录 + +默认的磁盘容灾文件目录为: + +``` +{user.home}/nacos/naming/{namespace}/failover +``` + +这个目录可以定制,如果设置了-D参数: + +``` +-DJM.SNAPSHOT.PATH=/mypath +``` + +则容灾磁盘文件目录变为: + +``` +/mypath/nacos/naming/{namespace}/failover +``` + +### 磁盘容灾开关 + +容灾开关存放在磁盘容灾文件目录下的一个文件里,具体文件名为: + +``` +00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 +``` + +文件里存放一个数字0或者1,0代表关闭容灾,1代表打开容灾 + +### 磁盘容灾数据 + +容灾的数据分成多个文件,都是存放在磁盘容灾文件目录下,每一个文件存储一个单独的服务的容灾数据,每个文件的文件名格式如下: + +``` +{group.name}%40%40{service.name} +``` + +里面的内容为客户端的ServiceInfo类的JSON序列化字符串,例如: + +``` +{ + "name":"DEFAULT_GROUP@@test.2", + "groupName":"DEFAULT_GROUP", + "clusters":"", + "cacheMillis":10000, + "hosts":[ + { + "instanceId":"1.1.2.1#8888#DEFAULT#DEFAULT_GROUP@@test.2", + "ip":"1.1.2.1", + "port":8888, + "weight":1, + "healthy":true, + "enabled":true, + "ephemeral":true, + "clusterName":"DEFAULT", + "serviceName":"DEFAULT_GROUP@@test.2", + "metadata":{ + "k1":"v1" + }, + "instanceHeartBeatInterval":5000, + "instanceHeartBeatTimeOut":15000, + "ipDeleteTimeout":30000 + } + ], + "lastRefTime":1689835375819, + "checksum":"", + "allIPs":false, + "reachProtectionThreshold":false, + "valid":true +} +``` + +## 扩展容灾数据源 + +磁盘容灾不需要外部依赖,逻辑比较简单,但是管理起来不太方便。因此我们也支持使用SPI来扩展容灾数据源,使用磁盘以外的存储。以下是扩展的步骤。 + +### 开发自己的容灾数据源类 + +编写一个类,实现接口com.alibaba.nacos.client.naming.backups.FailoverDataSource: + +``` +public class MyFailoverDataSource implements FailoverDataSource { + + @Override + public FailoverSwitch getSwitch() { + // TODO write your own implementation. + return null; + } + + @Override + public Map getFailoverData() { + // TODO write your own implementation. For naming module, the map + // should contain failover data with service name as key and ServiceInfo as value + return null; + } +} +``` + +### 配置容灾数据源类 + +在资源目录下新建文件: + +``` +{resource.root}/META-INF/services/com.alibaba.nacos.client.naming.backups.FailoverDataSource +``` + +{resource.root}的一个例子是src/main/resources。 + +文件内容为: + +``` +your.package.MyFailoverDataSource +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/guide/user/faq.md b/src/content/docs/v3.0/zh-cn/guide/user/faq.md new file mode 100644 index 00000000000..f619f7089f9 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/faq.md @@ -0,0 +1,308 @@ +--- +title: FAQ +keywords: [Nacos,FAQ] +description: Nacos FAQ +sidebar: + order: 6 +--- + +# FAQ + +> 文档优化中...... + +- Nacos常规问题 + - [Nacos是什么](#1.1) + - [Nacos如何支持多环境](#1.2) + - [Nacos是否生产可用](#1.3) + - [Nacos有什么依赖](#1.4) + - [Nacos使用什么开源协议](#1.5) + +- Nacos运维问题 + - [Nacos如何单机部署](#2.1) + - [Nacos单机部署如何使用Mysql](#2.2) + - [生产环境如何部署Nacos](#2.3) + - [Nacos如何Docker部署](#2.4) + - [如何在k8s中部署Nacos](#2.5) + - [如何监控Nacos](#2.6) + - [Nacos在Docker环境下集群部署,无法正常启动,日志一直打印 Nacos is starting...](#2.7) + +- Nacos使用问题 + - [Zookeeper服务可以迁移到Nacos上吗](#3.1) + - [Nacos支持多配置文件](#3.2) + - [Nacos支持Dubbo](#3.3) + - [Nacos支持Spring体系](#3.4) + - [不使用Nacos SDK如何访问Nacos](#3.5) + - [Nacos对多语言的支持](#3.6) + - [Nacos0.8版本登陆失败](#3.7) + - [服务端报错`java.lang.IllegalStateException: unable to find local peer: 127.0.0.1:8848`](#3.8) + - [Nacos如何对配置进行加密](#3.9) + - [Nacos报401错误](#3.10) + - [Nacos权重不生效](#3.11) + - [Nacos如何扩缩容](#3.12) + - [Nacos客户端修改日志级别](#3.13) + - [Nacos与Zipkin 整合出现`Service not found`问题](#3.14) + - [如何依赖最新的Nacos客户端?](#3.15) + - [客户端CPU高,或者内存耗尽的问题](#3.16) + - [日志打印频繁的问题](#3.17) + - [集群管理页面,raft term显示不一致问题](#3.18) + - [找不到符号`com.alibaba.nacos.consistency.entity`](#3.19) + - [启动报错`the length of secret key must great than or equal 32 bytes...`](#3.20) + - [启动报错`The specified key byte array is x bits ...`](#3.20) + + + +- Nacos原理问题 + +## Nacos常规问题 +

Nacos是什么

+ +Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。详情可以参考[Nacos官网介绍](../../what-is-nacos.md)。 + +

Nacos如何支持多环境

+ +在日常使用中常常需要不同的环境,比如日常,预发,线上环境,如果是逻辑隔离可以使用命名空间,Nacos支持命名空间来支持多环境隔离,可以在Nacos控制台创建多个命名空间。如果需要物理隔离,就要部署多套Nacos环境。 + +

Nacos是否生产可用

+ +Nacos在2019.1发布了Pre-GA版本,支持了安全隔离、监控和服务迁移等上生产的最后一公里,以更稳定的支撑用户的生产环境。详情可以参考[Nacos 发布 v0.8.0 Pre-GA 版本,安全稳定上生产](https://www.oschina.net/news/104019/nacos-0-8-0-pre-ga)。 + +

Nacos有什么依赖

+ +在单机模式下,Nacos没有任何依赖,在集群模式下,Nacos依赖Mysql做存储,详情可以参考[Nacos部署](../admin/deployment.md)。 + +

Nacos使用什么开源协议

+ +Nacos使用[Apache 2.0](https://github.com/alibaba/nacos/blob/master/LICENSE)。 + +## Nacos运维问题 +

Nacos如何单机部署

+ +可以参考Nacos官网部署手册[quick start](../../quickstart/quick-start.mdx)。 + +

Nacos单机部署如何使用mysql

+ +Nacos单机模式默认使用内嵌的数据库作为存储引擎,如果想换成自己安装的mysql,可以按照[官网文档](../admin/deployment.md)。 + +

生产环境如何部署Nacos

+ +生产环境使用Nacos为了达到高可用不能使用单机模式,需要搭建nacos集群,具体详情可以参考[集群部署手册](../admin/cluster-mode-quick-start.md)。 + +

Nacos如何Docker部署

+ +除了使用压缩包部署Nacos,Nacos也提供了相应的Docker镜像,当Nacos发布新的版本的时候,Nacos会发布对应的镜像版本支持Docker部署。具体详情可以参考[Nacos Docker](../../quickstart/quick-start-docker.mdx)。 + +

如何在k8s中部署Nacos

+ +在生产环境部署Nacos集群,如果要对Nacos进行扩容操作,需要手动更改集群ip文件,启动新的Nacos服务。为了能进行自动化运维,Nacos和k8s结合利用StatefulSets提供了自动运维方案,能对Nacos进行动态扩缩容,具体详情参考[Kubernetes Nacos](https://github.com/nacos-group/nacos-k8s/blob/master/README-CN.md)。 + +

如何监控Nacos

+ +Nacos0.8版本提供了Metrics数据暴露能力,能通过Metrics数据的内容对Nacos的运行状态进行监控,详情参考[Nacos监控](../admin/monitor-guide.md)。 + +

Nacos在Docker环境下集群部署,无法正常启动,日志一直打印 Nacos is starting...

+ +原因可能是由于Docker环境下,内存不足导致另外的服务无法正常启动,最后导致服务报错,一直重启,可以通过增大Docker限制内存尝试解决。 + +## Nacos使用问题 +

Zookeeper上的服务可以迁移到Nacos上吗

+ +可以通过Nacos-Sync把Zookeeper服务迁移到Nacos,也可以从Nacos迁移到Zookeeper,具体可以参考[Nacos Sync 使用](https://github.com/paderlol/nacos-sync-example)。 + +

Nacos支持多配置文件

+ +Nacos通过Spring Cloud Alibaba Nacos Config支持了多配置文件,可以将配置存储在多个独立的配置文件中。关联的[issue](https://github.com/alibaba/nacos/issues/320),详情参考文档[Spring Cloud Alibaba Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/Nacos-config)。 + +

Nacos支持Dubbo

+ +Nacos 0.6版本和Dubbo集成,支持使用Nacos作为注册中心,关联[issue](https://github.com/alibaba/nacos/issues/390),具体文档参考 [Dubbo 融合 Nacos 成为注册中心](../../ecology/use-nacos-with-dubbo.md) 。 + +

Nacos支持Spring体系

+ +Nacos完善支持了Sping技术栈,具体可以参考[Nacos Spring](../../ecology/use-nacos-with-spring.md)、[Nacos Spring Boot](../../ecology/use-nacos-with-spring-boot.md)、[Spring Cloud](../../ecology/use-nacos-with-spring-cloud.md)。 + +

不使用Nacos SDK如何访问Nacos

+ +Nacos的网络交互都是基于Http协议实现的,提供了[Open-API](./open-api.md)可以很容易实现Nacos的访问。 + +

Nacos对多语言的支持

+ +Nacos目前只支持Java,对于其他语言的支持还正在开发中,需要大家大力支持一起共建。 + +

Nacos0.8版本登陆失败

+ +Nacos 0.8版本当使用openjdk并且没有`JAVA_HOME`的环境变量时,nacos可以启动成功,是因为`yum install`安装的openjdk 会把java命令注册一份到`/bin`目录下面,所以会引发`SignatureException`异常。这个问题已经修复,0.9版本会发版,具体详情可以参考[issue](https://github.com/alibaba/nacos/issues/711)。 + +

服务端报错 java.lang.IllegalStateException: unable to find local peer: 127.0.0.1:8848

+ +这个问题是因为Nacos获取本机IP时,没有获取到正确的外部IP.需要保证`InetAddress.getLocalHost().getHostAddress()`或者`hostname -i`的结果是与cluster.conf里配置的IP是一致的。 + +

Nacos如何对配置进行加密

+ +Nacos计划在1.X版本提供加密的能力,目前还不支持加密,只能靠sdk做好了加密再存到nacos中。 + +

Nacos报401错误

+ +Nacos服务端报错了,可以检查服务端日志,参考[issue](https://github.com/alibaba/nacos/issues/816)。 + +

Nacos权重不生效

+ +Nacos控制台上编辑权重, 目前从SpringCloud客户端和Dubbo客户端都没有打通, 所以不能生效. 对于SpringCloud客户端, 应用可以实现Ribbon的负载均衡器来进行权重过滤。 + +

Nacos如何扩缩容

+ +目前支持修改cluster.conf文件的方式进行扩缩容, 改完后无需重启, Server会自动刷新到文件新内容。 + +

Nacos客户端修改日志级别

+ +配置-D参数com.alibaba.nacos.naming.log.level设置naming客户端的日志级别,例如设置为error: +`-Dcom.alibaba.nacos.naming.log.level=error` +同样的,-D参数com.alibaba.nacos.config.log.level用来设置config客户端的日志级别。 + +

Nacos与Zipkin 整合出现 Service not found 问题

+ +配置`spring-cloud-seluth`参数:`spring.zipkin.discovery-client-enabled=false`。 + +如果仍然存在`Service not found`错误,则建议先使用open-api将Zipkin-server注册为永久实例服务: + +`curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?port=9411&healthy=true&ip=127.0.0.1&weight=1.0&serviceName=zipkin-server&ephemeral=false&namespaceId=public'` + +然后,前往nacos控制台,找到服务名为`zipkin-server`的服务,找到集群配置,设置健康检查模式为`TCP`,端口号为`9411`(即zipkin-server的端口)。 + + +

如何依赖最新的Nacos客户端?

+很多用户都是通过Spring Cloud Alibaba或者Dubbo依赖的Nacos客户端,那么Spring Cloud Alibaba和Dubbo中依赖的Nacos客户端版本,往往会落后于Nacos最新发布的版本。在一些情况下,用户需要强制将Nacos客户端升级到最新,此时却往往不知道该升级哪个依赖,这里将Spring Cloud Alibaba和Dubbo的依赖升级说明如下: + + +##### Spring Cloud Alibaba + +用户通常是配置以下Maven依赖来使用的Nacos: + +```xml + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + [latest version] + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + [latest version] + +``` + +这两个jar包实际上又依赖了以下的jar包: +```xml + + com.alibaba.nacos + nacos-client + [a particular version] + +``` + +如果nacos-client升级了,对应的spring-cloud客户端版本不一定也同步升级,这个时候可以采用如下的方式强制升级nacos-client(以nacos-discovery为例): + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + [latest version] + + + com.alibaba.nacos + nacos-client + + + + + + com.alibaba.nacos + nacos-client + [latest version] + +``` + +##### Dubbo +Dubbo也是类似的道理,用户通常引入的是以下的依赖: +```xml + + com.alibaba + dubbo-registry-nacos + [latest version] + + + + + com.alibaba + dubbo + [latest version] + +``` + +需要升级Nacos客户端时,只需要如下修改依赖: +```xml + + com.alibaba.nacos + nacos-client + [latest version] + +``` + + +

客户端CPU高,或者内存耗尽的问题

+问题的现象是依赖Nacos客户端的应用,在运行一段时间后出现CPU占用率高,内存占用高甚至内存溢出的现象,可以参考issue:[https://github.com/alibaba/nacos/issues/1605](https://github.com/alibaba/nacos/issues/1605)。这种情况首先要做的是分析CPU高或者内存占用高的原因,常用的命令有top、jstack、jmap、jhat等。其中一种情况是Nacos客户端实例在Spring Cloud Alibaba服务框架中被反复构造了多次,可以参考issue:[https://github.com/alibaba/spring-cloud-alibaba/issues/859](https://github.com/alibaba/spring-cloud-alibaba/issues/859)。这个问题已经得到了修复,预期会在下个Spring Cloud Alibaba版本中发布。 + + +

日志打印频繁的问题

+在老的Nacos版本中,往往会有大量的无效日志打印,这些日志的打印会迅速占用完用户的磁盘空间,同时也让有效日志难以查找。目前社区反馈的日志频繁打印主要有以下几种情况: + +1. access日志大量打印,相关issue有:[https://github.com/alibaba/nacos/issues/1510](https://github.com/alibaba/nacos/issues/1510)。主要表现是{nacos.home}/logs/access_log.2019-xx-xx.log类似格式文件名的日志大量打印,而且还不能自动清理和滚动。这个日志是Spring Boot提供的tomcat访问日志打印,Spring Boot在关于该日志的选项中,没有最大保留天数或者日志大小控制的选项。因此这个日志的清理必须由应用新建crontab任务来完成,或者通过以下命令关闭日志的输出(在生产环境我们还是建议开启该日志,以便能够有第一现场的访问记录): + +``` +server.tomcat.accesslog.enabled=false +``` + +2. 服务端业务日志大量打印且无法动态调整日志级别。这个问题在1.1.3已经得到优化,可以通过API的方式来进行日志级别的调整,调整日志级别的方式如下: + +``` +# 调整naming模块的naming-raft.log的级别为error: +curl -X PUT '$nacos_server:8848/nacos/v1/ns/operator/log?logName=naming-raft&logLevel=error' +# 调整config模块的config-dump.log的级别为warn: +curl -X PUT '$nacos_server:8848/nacos/v1/cs/ops/log?logName=config-dump&logLevel=warn' +``` + +3. 客户端日志大量打印,主要有心跳日志、轮询日志等。这个问题已经在1.1.3解决,请升级到1.1.3版本。 + + +

集群管理页面,raft term显示不一致问题

+在Nacos 1.0.1版本中,Nacos控制台支持了显示当前的集群各个机器的状态信息。这个功能受到比较多用户的关注,其中一个被反馈的问题是列表中每个节点的集群任期不一样。如下图所示(图片信息来自issue:https://github.com/alibaba/nacos/issues/1786): + +![image.png](https://cdn.nlark.com/yuque/0/2019/png/333810/1567737322959-eed80457-e74c-4adb-bc29-6655860f8ca9.png#align=left&display=inline&height=279&name=image.png&originHeight=558&originWidth=1685&size=45557&status=done&width=842.5) + +对于这个任期不一致的问题,原因主要是因为获取这个信息的逻辑有一些问题,没有从对应的节点上获取集群任期。这个问题会在下一个Nacos版本中修复。目前一个手动检查集群任期的办法是在每个节点上执行以下命令: + +``` +curl '127.0.0.1:8848/nacos/v1/ns/raft/state' +``` + +然后在返回信息中查找本节点的集群任期。因为每个节点返回的集群任期中,只有当前节点的信息是准确的,返回的其他节点的信息都是不准确的。 + +

找不到符号`com.alibaba.nacos.consistency.entity`

+ +这个包目录是由`protobuf`在编译时自动生成,您可以通过`mvn compile`来自动生成他们。如果您使用的是IDEA,也可以使用IDEA的protobuf插件。 + +

启动报错java.lang.IllegalArgumentException: the length of secret key must great than or equal 32 bytes...

+

启动报错java.lang.IllegalArgumentException: The specified key byte array is x bits which is not secure enough for any JWT HMAC-SHA algorithm.

+ +默认鉴权插件需要密钥来生成访问token,密钥格式需要长度大于32。若`secret.key`进行BASE64解密后的长度小于32,则会在启动过程中此错误。 +您可以在`application.properties`中设置正确的`secret.key`,详情见[用户指南-权限认证](./auth.md). + +

启动报错Empty identity, Please set `nacos.core.auth.server.identity.key` and `nacos.core.auth.server.identity.value`

+ +2.2.1后的版本,移除了配置中间中`nacos.core.auth.server.identity.key` 和 `nacos.core.auth.server.identity.value`的默认值,并添加了启动校验。 +如果在开启鉴权但未设置`nacos.core.auth.server.identity.key` 和 `nacos.core.auth.server.identity.value`的情况下,nacos server会提示以上报错信息,并阻止启动。 +可查看[用户指南-权限认证](./auth.md)文档中相关内容,进行设置后启动。 + +## Nacos原理问题 diff --git a/src/content/docs/v3.0/zh-cn/guide/user/open-api.md b/src/content/docs/v3.0/zh-cn/guide/user/open-api.md new file mode 100644 index 00000000000..2757333a7b2 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/open-api.md @@ -0,0 +1,2531 @@ +--- +title: Open API 指南 +keywords: [Open API,指南] +description: Open API 指南 +sidebar: + order: 3 +--- + +> 该文档即将废弃,请参考[用户手册-OpenAPI指南](../../manual/user/open-api.md),部分接口被定义为运维API,请参考[运维手册-运维API](../../manual/admin/admin-api.md)。 + +# Open API 指南 + +Nacos 2.X 版本兼容 Nacos1.X 版本的OpenAPI, 请参考文档[Nacos1.X OpenAPI](https://nacos.io/docs/v1/open-api)使用。 + +> 注意:未特殊注明支持版本的OpenAPI均从2.2.0版本开始支持。 + +- 文档约定 + - [API 统一返回体格式](#0.1) + - [API 错误码汇总](#0.2) + +- 配置管理 + - [获取配置](#1.1) + - [发布配置](#1.2) + - [删除配置](#1.3) + - [查询配置历史版本列表](#1.4) + - [查询具体版本的历史配置](#1.5) + - [查询配置上一版本信息](#1.6) + - [查询指定命名空间下的配置列表](#1.7) +- 服务发现 + - [注册实例](#2.1) + - [注销实例](#2.2) + - [更新实例](#2.3) + - [查询实例详情](#2.4) + - [查询指定服务下的实例列表](#2.5) + - [批量更新实例元数据(Beta)](#2.6) + - [批量删除实例元数据(Beta)](#2.7) + - [创建服务](#2.8) + - [删除服务](#2.9) + - [修改服务](#2.10) + - [查询服务详情](#2.11) + - [查询服务列表](#2.12) + - [查询系统开关](#2.13) + - [修改系统开关](#2.14) + - [查看系统当前数据指标](#2.15) + - [更新实例的健康状态](#2.16) + - [查询客户端列表(新)](#2.17) + - [查询客户端信息(新)](#2.18) + - [查询客户端的注册信息(新)](#2.19) + - [查询客户端的订阅信息(新)](#2.20) + - [查询注册指定服务的客户端信息(新)](#2.21) + - [查询订阅指定服务的客户端信息(新)](#2.22) +- 命名空间 + - [查询命名空间列表](#3.1) + - [查询具体命名空间](#3.2) + - [新增命名空间](#3.3) + - [编辑命名空间](#3.4) + - [删除命名空间](#3.5) +- 集群管理 + - [查询当前节点信息](#4.1) + - [查询集群节点列表](#4.2) + - [查询当前节点健康状态](#4.3) + - [切换寻址模式](#4.4) + +## 文档规定 + +

API 统一返回体格式

+ +2.0版本Open API,所有接口请求的响应均为`json`类型的返回体,返回体具有相同的格式 + +```json +{ + "code": 0, + "message": "success", + "data": {} +} +``` + +返回体中各字段的含义如下表所示 + +| 名称 | 类型 | 描述 | +|:---------:|:--------:|--------------------------------------------------------| +| `code ` | `int` | 错误码,`0`代表执行成功,非`0`代表执行失败的某一种情况 | +| `message` | `String` | 错误码提示信息,执行成功为"`success`" | +| `data` | 任意类型 | 返回数据,执行失败时为详细出错信息 | + +> 由于执行成功的情况下code字段与message字段相同,后续在介绍接口的返回结果时,只介绍返回数据的data字段 + +

API 错误码汇总

+ +API接口返回体中的错误码及对应提示信息汇总见下表 + +| 错误码 | 提示信息 | 含义 | +|---------|------------------------------|----------------------------| +| `0` | `success` | 成功执行 | +| `10000` | `parameter missing` | 参数缺失 | +| `10001` | `access denied` | 访问拒绝 | +| `10002` | `data access error` | 数据访问错误 | +| `20001` | `'tenant' parameter error` | `tenant`参数错误 | +| `20002` | `parameter validate error` | 参数验证错误 | +| `20003` | `MediaType Error` | 请求的`MediaType`错误 | +| `20004` | `resource not found` | 资源未找到 | +| `20005` | `resource conflict` | 资源访问冲突 | +| `20006` | `config listener is null` | 监听配置为空 | +| `20007` | `config listener error` | 监听配置错误 | +| `20008` | `invalid dataId` | 无效的`dataId`(鉴权失败) | +| `20009` | `parameter mismatch` | 请求参数不匹配 | +| `21000` | `service name error` | `serviceName`服务名错误 | +| `21001` | `weight error` | `weight`权重参数错误 | +| `21002` | `instance metadata error` | 实例`metadata`元数据错误 | +| `21003` | `instance not found` | `instance`实例不存在 | +| `21004` | `instance error` | `instance`实例信息错误 | +| `21005` | `service metadata error` | 服务`metadata`元数据错误 | +| `21006` | `selector error` | 访问策略`selector`错误 | +| `21007` | `service already exist` | 服务已存在 | +| `21008` | `service not exist` | 服务不存在 | +| `21009` | `service delete failure` | 存在服务实例,服务删除失败 | +| `21010` | `healthy param miss` | `healthy`参数缺失 | +| `21011` | `health check still running` | 健康检查仍在运行 | +| `22000` | `illegal namespace` | 命名空间`namespace`不合法 | +| `22001` | `namespace not exist` | 命名空间不存在 | +| `22002` | `namespace already exist` | 命名空间已存在 | +| `23000` | `illegal state` | 状态`state`不合法 | +| `23001` | `node info error` | 节点信息错误 | +| `23002` | `node down failure` | 节点离线操作出错 | +| ... | ... | ... | +| 30000 | `server error` | 其他内部错误 | + +## 配置管理 + +

获取配置

+ +### 接口描述 + +获取指定配置 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/cs/config` + +### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `tag` | `String` | 否 | 标签 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| `data` | `String` | 配置内容 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "contentTest" + } + ``` + +

发布配置

+ +### 接口描述 + +发布指定配置 + +> 当配置已存在时,则对配置进行更新 + +### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/cs/config` + +### 请求Body + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置组名 | +| `dataId` | `String` | **是** | 配置名 | +| `content` | `String` | **是** | 配置内容 | +| `tag` | `String` | 否 | 标签 | +| `appName` | `String` | 否 | 应用名 | +| `srcUser` | `String` | 否 | 源用户 | +| `configTags` | `String` | 否 | 配置标签列表,可多个,逗号分隔 | +| `desc` | `String` | 否 | 配置描述 | +| `use` | `String` | 否 | - | +| `effect` | `String` | 否 | - | +| `type` | `String` | 否 | 配置类型 | +| `schema` | `String` | 否 | - | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'dataId=nacos.example' \ + -d 'group=DEFAULT_GROUP' \ + -d 'namespaceId=public' \ + -d 'content=contentTest' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/cs/config' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

删除配置

+ +### 接口描述 + +删除指定配置 + +### 请求方式 + +`DELETE` + +### 请求URL + +`/nacos/v2/cs/config` + +### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `tag` | `String` | 否 | 标签 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

查询配置历史列表

+ +### 接口描述 + +获取指定配置的历史版本列表 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/cs/history/list` + +### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `pageNo` | `int` | 否 | 当前页,默认为`1` | +| `pageSize` | `int` | 否 | 页条目数,默认为`100`,最大为`500` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------|------------|----------------------------------------------------------| +| `data` | `Object` | 分页查询结果 | +| `data.totalCount` | `int` | 总数 | +| `data.pageNumber` | `int` | 当前页 | +| `data.pagesAvailable` | `int` | 总页数 | +| `data.pageItems` | `Object[]` | 历史配置项列表,参见[历史配置项信息](#ConfigHistoryInfo) | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/list?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "totalCount": 1, + "pageNumber": 1, + "pagesAvailable": 1, + "pageItems": [ + { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + ] + } + } + ``` + +

查询具体版本的历史配置

+ +### 接口描述 + +获取指定版本的历史配置 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/cs/history` + +### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `nid` | `long` | **是** | 历史配置id | + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|----------|------------| +| `data` | `Object` | 历史配置项 | +| `data.id` | `String` | 配置`id` | +| `data.lastId` | `int` | | +| `data.dataId` | `String` | 配置名 | +| `data.group` | `String` | 配置分组 | +| `data.tenant` | `String` | 租户信息(命名空间) | +| `data.appName` | `String` | 应用名 | +| `data.md5` | `String` | 配置内容的md5值 | +| `data.content` | `String` | 配置内容 | +| `data.srcIp` | `String` | 源ip | +| `data.srcUser` | `String` | 源用户 | +| `data.opType` | `String` | 操作类型 | +| `data.createdTime` | `String` | 创建时间 | +| `data.lastModifiedTime` | `String` | 上次修改时间 | +| `data.encryptedDataKey` | `String` | | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=&nid=203' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +

查询配置上一版本信息

+ +### 接口描述 + +获取指定配置的上一版本 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/cs/history/previous` + +### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `id` | `long` | **是** | 配置id | + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|--------|----------|------------------------------------------------------| +| `data` | `Object` | 历史配置项,参见[历史配置项信息](#ConfigHistoryInfo) | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/previous?id=309135486247505920&dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +

查询指定命名空间下的配置列表

+ +### 接口描述 + +获取指定命名空间下的配置信息列表 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/cs/history/configs` + +### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|----------| +| `namespaceId` | `String` | **是** | 命名空间 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|------------|----------------------| +| `data` | `Object[]` | 配置信息列表 | +| `data.id` | `String` | 配置`id` | +| `data.dataId` | `String` | 配置名 | +| `data.group` | `String` | 配置分组 | +| `data.content` | `String` | 配置内容 | +| `data.md5` | `String` | 配置内容的md5值 | +| `data.encryptedDataKey` | `String` | | +| `data.tenant` | `String` | 租户信息(命名空间) | +| `data.appName` | `String` | 应用名 | +| `data.type` | `String` | 配置文件类型 | +| `data.lastModified` | `long` | 上次修改时间 | + +> 返回数据中的配置信息只有`dataId`, `group`, `tenant`, `appName`, `type`字段有效,其他字段为默认值 + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/configs?namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "id": "0", + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "content": null, + "md5": null, + "encryptedDataKey": null, + "tenant": "", + "appName": "", + "type": "yaml", + "lastModified": 0 + } + ] + } + ``` + +## 服务发现 + +

注册实例

+ +### 接口描述 + +注册一个实例 + +### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/instance` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

注销实例

+ +### 接口描述 + +注销指定实例 + +### 请求方式 + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/instance` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

更新实例

+ +### 接口描述 + +修改实例信息 + +> 通过该接口更新的元数据拥有更高的优先级,且具有记忆能力;会在对应实例删除后,依旧存在一段时间,如果在此期间实例重新注册,该元数据依旧生效;您可以通过`nacos.naming.clean.expired-metadata.expired-time`及`nacos.naming.clean.expired-metadata.interval`对记忆时间进行修改 + +### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/instance` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

查询实例详情

+ +### 接口描述 + +查询某个具体实例的详情信息 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/instance` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------|-----------|--------------| +| `data` | `Object` | 实例详情信息 | +| `data.serviceName` | `String` | 服务名 | +| `data.ip` | `String` | `IP`地址 | +| `data.port` | `int` | 端口号 | +| `data.clusterName` | `String` | 集群名称 | +| `data.weight` | `double` | 实例权重 | +| `data.healthy` | `boolean` | 是否健康 | +| `data.instanceId` | `String` | 实例`id` | +| `data.metadata` | `map` | 实例元数据 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance?namespaceId=public&groupName=&serviceName=test_service&ip=127.0.0.1&port=8080' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "serviceName": "DEFAULT_GROUP@@test_service", + "ip": "127.0.0.1", + "port": 8080, + "clusterName": "DEFAULT", + "weight": 1.0, + "healthy": true, + "instanceId": null, + "metadata": { + "value": "1" + } + } + } + ``` + +

查询指定服务的实例列表

+ +### 接口描述 + +查询指定服务下的实例详情信息列表 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/instance/list` + +### 请求头 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------------|----------|----------|----------------------| +| `User-Agent` | `String` | 否 | 用户代理,默认为空 | +| `Client-Version` | `String` | 否 | 客户端版本,默认为空 | + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|----------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `ip` | `String` | 否 | `IP`地址,默认为空,表示不限制`IP`地址 | +| `port` | `int` | 否 | 端口号,默认为`0`,表示不限制端口号 | +| `healthyOnly` | `boolean` | 否 | 是否只获取健康实例,默认为`false` | +| `app` | `String` | 否 | 应用名,默认为空 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|----------------------------------------|------------|-----------| +| `data` | | 指定服务的实例列表 | +| `data.name` | `String` | 分组名@@服务名 | +| `data.groupName` | `String` | 分组名 | +| `data.clusters` | `String` | 集群名 | +| `data.cacheMillis` | `int` | 缓存时间 | +| `data.hosts` | `Object[]` | 实例列表 | +| `data.hosts.ip` | `String` | 实例`IP` | +| `data.hosts.port` | `int` | 实例端口号 | +| `data.hosts.weight` | `double` | 实例权重 | +| `data.hosts.healthy` | `boolean` | 实例是否健康 | +| `data.hosts.enabled` | `boolean` | 实例是否可用 | +| `data.hosts.ephemeral` | `boolean` | 是否为临时实例 | +| `data.hosts.clusterName` | `String` | 实例所在的集群名称 | +| `data.hosts.serviceName` | `String` | 服务名 | +| `data.hosts.metadata` | `map` | 实例元数据 | +| `data.hosts.instanceHeartBeatTimeOut` | `int` | 实例心跳超时时间 | +| `data.hosts.ipDeleteTimeout` | `int` | 实例删除超时时间 | +| `data.hosts.instanceHeartBeatInterval` | `int` | 实例心跳间隔 | +| `data.lastRefTime` | `int` | 上次刷新时间 | +| `data.checksum` | `int` | 校验码 | +| `data.allIPs` | `boolean` | | +| `data.reachProtectionThreshold` | `boolean` | 是否到达保护阈值 | +| `data.valid` | `boolean` | 是否有效 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance/list?serviceName=test_service&ip=127.0.0.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "name": "DEFAULT_GROUP@@test_service", + "groupName": "DEFAULT_GROUP", + "clusters": "", + "cacheMillis": 10000, + "hosts": [ + { + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "enabled": true, + "ephemeral": true, + "clusterName": "DEFAULT", + "serviceName": "DEFAULT_GROUP@@test_service", + "metadata": { + "value": "1" + }, + "instanceHeartBeatTimeOut": 15000, + "ipDeleteTimeout": 30000, + "instanceHeartBeatInterval": 5000 + } + ], + "lastRefTime": 1662554390814, + "checksum": "", + "allIPs": false, + "reachProtectionThreshold": false, + "valid": true + } + } + ``` + +

批量更新实例元数据

+ +### 接口描述 + +批量更新实例的元数据, + +> 对应元数据的键不存在时,则添加对应元数据 + +### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/instance/metadata/batch` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `consistencyType` | `String` | 否 | 持久化类型,默认为空 | +| `instances` | `JSON格式String` | 否 | 需要更新的实例列表,默认为空 | +| `metadata` | `JSON格式String` | **是** | 实例元数据 | + +### 参数说明 + +> - `consistencyType`: 实例的持久化类型,当为‘`persist`’,表示对持久化实例的元数据进行更新;否则表示对临时实例的元数据进行更新 +> - `instances`: 待更新的实例列表,`json`数组,通过`ip+port+ephemeral+cluster`定位到某一实例,为空则表示更新指定服务下所有实例的元数据 + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

批量删除实例元数据

+ +### 接口描述 + +批量删除实例的元数据, + +> 对应元数据的键不存在时,则不做操作 + +### 请求方式 + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/instance/metadata/batch` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `consistencyType` | `String` | 否 | 持久化类型,默认为空 | +| `instances` | `JSON格式String` | 否 | 需要更新的实例列表,默认为空 | +| `metadata` | `JSON格式String` | **是** | 实例元数据 | + +### 参数说明 + +> - `consistencyType`: 实例的持久化类型,当为‘`persist`’,表示对持久化实例的元数据进行删除;否则表示对临时实例的元数据进行 +> - `instances`: 待更新的实例列表,`json`数组,通过`ip+port+ephemeral+cluster`定位到某一实例,为空则表示更新指定服务下所有实例的元数据 + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

创建服务

+ +### 接口描述 + +创建一个服务 + +> 服务已存在时会创建失败 + +### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/service` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `metadata` | `JSON格式String` | 否 | 服务元数据,默认为空 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例,默认为`false` | +| `protectThreshold` | `float` | 否 | 保护阈值,默认为`0` | +| `selector` | `JSON格式String` | 否 | 访问策略,默认为空 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ephemeral=true' \ + -d 'metadata={"k1":"v1"}' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

删除服务

+ +### 接口描述 + +删除指定服务 + +> 服务不存在时会报错,且服务还存在实例时会删除失败 + +### 请求方式 + +`DELETE` + +### 请求URL + +`/nacos/v2/ns/service` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

修改服务

+ +### 接口描述 + +更新指定服务 + +> 服务不存在时会报错 + +### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/service` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `metadata` | `JSON格式String` | 否 | 服务元数据,默认为空 | +| `protectThreshold` | `float` | 否 | 保护阈值,默认为`0` | +| `selector` | `JSON格式String` | 否 | 访问策略,默认为空 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'metadata={"k1":"v2"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

查询服务详情

+ +### 接口描述 + +查询某个具体服务的详情信息 + +> 服务不存在时会报错 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/service` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|---------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|-----------|----------------| +| `data` | | 服务信息 | +| `data.namespace` | `String` | 命名空间 | +| `data.groupName` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.clusterMap` | `map` | 集群信息 | +| `data.metadata` | `map` | 服务元数据 | +| `data.protectThreshold` | `float` | 保护阈值 | +| `data.selector` | `Object` | 访问策略 | +| `data.ephemeral` | `Boolean` | 是否为临时实例 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "public", + "serviceName": "nacos.test.1", + "groupName": "DEFAULT_GROUP", + "clusterMap": {}, + "metadata": {}, + "protectThreshold": 0, + "selector": { + "type": "none", + "contextType": "NONE" + }, + "ephemeral": false + } + } + ``` + +

查询服务列表

+ +### 接口描述 + +查询符合条件的服务列表 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/service/list` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|-----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `selector` | `JSON格式String` | **是** | 访问策略 | +| `pageNo` | `int` | 否 | 当前页,默认为`1` | +| `pageSize` | `int` | 否 | 页条目数,默认为`20`,最大为`500` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|------------|------------------| +| `data` | | 服务列表信息 | +| `data.count` | `String` | 服务数目 | +| `data.services` | `String[]` | 分页后的服务列表 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "count": 2, + "services": [ + "nacos.test.1", + "nacos.test.2" + ] + } + } + ``` + +

查询系统开关

+ +### 接口描述 + +查询系统开关 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/operator/switches` + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------|----------|--------------| +| `data` | `Object` | 系统开关信息 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/operator/switches' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "masters": null, + "adWeightMap": {}, + "defaultPushCacheMillis": 10000, + "clientBeatInterval": 5000, + "defaultCacheMillis": 3000, + "distroThreshold": 0.7, + "healthCheckEnabled": true, + "autoChangeHealthCheckEnabled": true, + "distroEnabled": true, + "enableStandalone": true, + "pushEnabled": true, + "checkTimes": 3, + "httpHealthParams": { + "max": 5000, + "min": 500, + "factor": 0.85 + }, + "tcpHealthParams": { + "max": 5000, + "min": 1000, + "factor": 0.75 + }, + "mysqlHealthParams": { + "max": 3000, + "min": 2000, + "factor": 0.65 + }, + "incrementalList": [], + "serverStatusSynchronizationPeriodMillis": 2000, + "serviceStatusSynchronizationPeriodMillis": 5000, + "disableAddIP": false, + "sendBeatOnly": false, + "lightBeatEnabled": true, + "doubleWriteEnabled": false, + "limitedUrlMap": {}, + "distroServerExpiredMillis": 10000, + "pushGoVersion": "0.1.0", + "pushJavaVersion": "0.1.0", + "pushPythonVersion": "0.4.3", + "pushCVersion": "1.0.12", + "pushCSharpVersion": "0.9.0", + "enableAuthentication": false, + "overriddenServerStatus": null, + "defaultInstanceEphemeral": true, + "healthCheckWhiteList": [], + "name": "00-00---000-NACOS_SWITCH_DOMAIN-000---00-00", + "checksum": null + } + } + ``` + +

修改系统开关

+ +### 接口描述 + +修改系统开关 + +### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/operator/switches` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------|-----------|-------|---------------------------------------------------------| +| `entry` | `String` | **是** | 开关名 | +| `value` | `String` | **是** | 开关值 | +| `debug` | `boolean` | 否 | 是否只在本机生效,`true`表示本机生效,`false`表示集群生效 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|------------------| +| `data` | `String` | “`ok`”表示执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'entry=pushEnabled' \ + -d 'value=false' \ + -d 'debug=true' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/operator/switches' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "ok" + } + ``` + +

查询系统当前数据指标

+ +### 接口描述 + +查询系统当前数据指标 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/operator/metrics` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------|-----------|----------|--------------------------| +| `onlyStatus` | `boolean` | 否 | 只显示状态,默认为`true` | + +> 当`onlyStatus`设置为`true`时,只返回表示系统状态的字符串 + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|------------------------------------|----------|--------------------| +| `data` | `Object` | 系统当前数据指标 | +| `data.status` | `String` | 系统状态 | +| `data.serviceCount` | `int` | 服务数量 | +| `data.instanceCount` | `int` | 实例数量 | +| `data.subscribeCount` | `int` | 订阅数量 | +| `data.raftNotifyTaskCount` | `int` | `Raft`通知任务数量 | +| `data.responsibleServiceCount` | `int` | | +| `data.responsibleInstanceCount` | `int` | | +| `data.clientCount` | `int` | 客户端数量 | +| `data.connectionBasedClientCount` | `int` | 连接数量 | +| `data.ephemeralIpPortClientCount` | `int` | 临时客户端数量 | +| `data.persistentIpPortClientCount` | `int` | 持久客户端数量 | +| `data.responsibleClientCount` | `int` | | +| `data.cpu` | `float` | `cpu`使用率 | +| `data.load` | `float` | 负载 | +| `data.mem` | `float` | 内存使用率 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/operator/metrics' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "status": "UP", + "serviceCount": 2, + "instanceCount": 2, + "subscribeCount": 2, + "raftNotifyTaskCount": 0, + "responsibleServiceCount": 0, + "responsibleInstanceCount": 0, + "clientCount": 2, + "connectionBasedClientCount": 2, + "ephemeralIpPortClientCount": 0, + "persistentIpPortClientCount": 0, + "responsibleClientCount": 2, + "cpu": 0, + "load": -1, + "mem": 1 + } + } + ``` + +

更新实例健康状态

+ +### 接口描述 + +更新实例的健康状态 + +### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/ns/health/instance` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名,默认为`DEFAULT` | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `healthy` | `boolean` | **是** | 是否健康 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|--------------------| +| `data` | `String` | “`ok`”表示执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ip=127.0.0.1' \ + -d 'port=8080' \ + -d 'healthy=false' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/health/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "ok" + } + ``` + +

查询客户端列表(新)

+ +### 接口描述 + +查询当前所有的客户端列表 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/client/list` + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------|------------|----------------| +| `data` | `String[]` | 客户端`id`列表 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + "10.128.164.35:9956#true", + "1664358687402_127.0.0.1_2300", + "1664358642902_127.0.0.1_2229", + "192.168.139.1:49825#true", + "10.128.164.35:9954#true", + "192.168.139.1:53556#true" + ] + } + ``` + +> 对于不同版本的nacos client,建立客户端的方式不同。 +> +> 对于`1.x`版本,每个实例会建立两个基于`ip+port`的客户端,分别对应实例注册与服务订阅,`clientId`格式为 `ip:port#ephemeral` +> +> 对于`2.x`版本的`nacos client`, 每个实例会建立一个`RPC`连接,对应一个基于`RPC`连接的客户端,兼具注册与订阅功能,`clientId` +> 格式为`time_ip_port` + + + +

查询客户端信息(新)

+ +### 接口描述 + +查询指定客户端的详细信息 + +> 客户端不存在时会报错 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/client` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|------------------------|-----------|----------------| +| `data` | `Object` | 客户端信息 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ephemeral` | `boolean` | 是否为临时实例 | +| `data.lastUpdatedTime` | `int` | 上次更新时间 | +| `data.clientType` | `String` | 客户端类型 | +| `data.clientIp` | `String` | 客户端`IP` | +| `data.clientPort` | `String` | 客户端端口 | +| `data.connectType` | `String` | 连接类型 | +| `data.appName` | `String` | 应用名 | +| `data.Version` | `String` | 客户端版本 | + +> 只有当`clientType`为`connection`时,会显示`connectType`,`appName`和`appName`字段 + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "clientId": "1664527081276_127.0.0.1_4400", + "ephemeral": true, + "lastUpdatedTime": 1664527081642, + "clientType": "connection", + "connectType": "GRPC", + "appName": "-", + "version": "Nacos-Java-Client:v2.1.0", + "clientIp": "10.128.164.35", + "clientPort": "4400" + } + } + ``` + +

查询客户端的注册信息(新)

+ +### 接口描述 + +查询指定客户端的注册信息 + +> 客户端不存在时会报错 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/client/publish/list` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------------------|------------|----------------------| +| `data` | `Object[]` | 客户端注册的服务列表 | +| `data.namespace` | `String` | 命名空间 | +| `data.group` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.registeredInstance` | `Object` | 该服务下注册的实例 | +| `data.registeredInstance.ip` | `String` | `IP`地址 | +| `data.registeredInstance.port` | `int` | 端口号 | +| `data.registeredInstance.cluster` | `String` | 集群名 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/publish/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "registeredInstance": { + "ip": "10.128.164.35", + "port": 9950, + "cluster": "DEFAULT" + } + } + ] + } + ``` + +

查询客户端的订阅信息(新)

+ +### 接口描述 + +查询指定客户端的订阅信息 + +> 客户端不存在时会报错 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/client/subscribe/list` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------------|------------|----------------------| +| `data` | `Object[]` | 客户端订阅的服务列表 | +| `data.namespace` | `String` | 命名空间 | +| `data.group` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.subscriberInfo` | `Object` | 订阅信息 | +| `data.subscriberInfo.app` | `String` | 应用 | +| `data.subscriberInfo.agent` | `String` | 客户端信息 | +| `data.subscriberInfo.addr` | `String` | 地址 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/subscribe/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "subscriberInfo": { + "app": "unknown", + "agent": "Nacos-Java-Client:v2.1.0", + "addr": "10.128.164.35" + } + } + ] + } + ``` + +

查询注册指定服务的客户端信息(新)

+ +### 接口描述 + +查询注册指定服务的客户端信息 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/client/service/publisher/list` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | +| `ip` | `String` | 否 | `IP`地址,默认为空,不限制`IP`地址 | +| `port` | `int` | 否 | 端口号,默认为空,表示不限制端口号 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|----------|------------| +| `data` | | 客户端列表 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ip` | `String` | 客户端`IP` | +| `data.port` | `int` | 客户端端口 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/publisher/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527081276_127.0.0.1_4400", + "ip": "10.128.164.35", + "port": 9950 + }, + { + "clientId": "10.128.164.35:9954#true", + "ip": "10.128.164.35", + "port": 9954 + } + ] + } + ``` + +

查询订阅指定服务的客户端信息(新)

+ +### 接口描述 + +查询订阅指定服务的客户端信息 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/ns/client/service/subscriber/list` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|------------------------------------| +| `namespaceId` | String | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | String | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | String | **是** | 服务名 | +| `ephemeral` | boolean | 否 | 是否为临时实例 | +| `ip` | String | 否 | `IP`地址,默认为空,不限制`IP`地址 | +| `port` | int | 否 | 端口号,默认为空,表示不限制端口号 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|----------|------------| +| `data` | | 客户端列表 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ip` | `String` | 客户端`IP` | +| `data.port` | `int` | 客户端端口 | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/subscriber/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527125645_127.0.0.1_4443", + "ip": "10.128.164.35", + "port": 0 + }, + { + "clientId": "172.24.144.1:54126#true", + "ip": "172.24.144.1", + "port": 54126 + } + ] + } + ``` + +## 命名空间 + +

查询命名空间列表

+ +### 接口描述 + +查询当前所有的命名空间 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/console/namespace/list` + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------------|------------|----------------------| +| `data` | `Object[]` | 命名空间列表 | +| `data.namespace` | `String` | 命名空间`ID` | +| `data.namespaceShowName` | `String` | 命名空间名称 | +| `data.namespaceDesc` | `String` | 命名空间描述 | +| `data.quota` | `int` | 命名空间的容量 | +| `data.configCount` | `int` | 命名空间下的配置数量 | +| `data.type` | `int` | 命名空间类型 | + +> 命名空间分为3种类型,`0 `- 全局命名空间 `1` - 默认私有命名空间 `2 `- 自定义命名空间 + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "", + "namespaceShowName": "public", + "namespaceDesc": null, + "quota": 200, + "configCount": 1, + "type": 0 + } + ] + } + ``` + +

查询具体命名空间

+ +### 接口描述 + +查询具体命名空间的信息 + +> 命名空间不存在时会报错 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/console/namespace` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------------|----------|----------------------| +| `data` | `Object` | 命名空间信息 | +| `data.namespace` | `String` | 命名空间`ID` | +| `data.namespaceShowName` | `String` | 命名空间名称 | +| `data.namespaceDesc` | `String` | 命名空间描述 | +| `data.quota` | `int` | 命名空间的容量 | +| `data.configCount` | `int` | 命名空间下的配置数量 | +| `data.type` | `int` | 命名空间类型 | + +> 命名空间分为3种类型,`0` - 全局命名空间 `1` - 默认私有命名空间 `2` - 自定义命名空间 + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace?namespaceId=test_namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "test_namespace", + "namespaceShowName": "test", + "namespaceDesc": null, + "quota": 200, + "configCount": 0, + "type": 2 + } + } + ``` + +

创建命名空间

+ +### 接口描述 + +创建一个命名空间 + +> 命名空间已存在时会报错 + +### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/console/namespace` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | +| `namespaceName` | `String` | **是** | 命名空间名称 | +| `namespaceDesc` | `String` | 否 | 命名空间描述 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

编辑命名空间

+ +### 接口描述 + +编辑命名空间信息 + +### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/console/namespace` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | +| `namespaceName` | `String` | **是** | 命名空间名称 | +| `namespaceDesc` | `String` | 否 | 命名空间描述 | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test.nacos' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +

删除命名空间

+ +### 接口描述 + +删除指定命名空间 + +### 请求方式 + +`DELETE` + +### 请求URL + +`/nacos/v2/console/namespace` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +## 集群管理 + +

查询当前节点信息

+ +### 接口描述 + +查询当前`nacos`节点信息 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/core/cluster/node/self` + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|----------------------|----------|-----------------------| +| `data` | `Object` | 当前节点信息 | +| `data.ip` | `String` | 节点`IP`地址 | +| `data.port` | `int` | 节点端口 | +| `data.state` | `String` | 节点状态 | +| `data.extendInfo` | `Object` | 节点扩展信息 | +| `data.address` | `String` | 节点地址(`IP:port`) | +| `data.failAccessCnt` | `int` | 失败访问次数 | +| `data.abilities` | `Object` | | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/self' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "ip": "10.128.164.35", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1664521263623, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_persistent_service_v2": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_service_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.1.0" + }, + "address": "10.128.164.35:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + } + } + } + ``` + +

查询集群节点列表

+ +### 接口描述 + +查询集群节点列表 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/core/cluster/node/list` + +### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------|----------|----------|--------------------| +| `address` | `String` | 否 | 节点地址,默认为空 | +| `state` | `String` | 否 | 节点状态,默认为空 | + +> `address `对应于需要查询的节点地址的前缀匹配条件,为空时不做限制 +> +> `state`对应节点状态的筛选条件,为空时不做限制 + +### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------|------------|---------------------------------------| +| `data` | `Object[]` | 节点列表,详情参见[节点详情](#Member) | + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "ip": "10.128.164.35", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1664521263623, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_persistent_service_v2": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + }, + "naming_service_metadata": { + "leader": "10.128.164.35:7848", + "raftGroupMember": [ + "10.128.164.35:7848" + ], + "term": 12 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.1.0" + }, + "address": "10.128.164.35:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + } + } + ] + } + ``` + +

查询当前节点健康状态

+ +### 接口描述 + +查询当前`nacos`节点健康状态 + +### 请求方式 + +`GET` + +### 请求URL + +`/nacos/v2/core/cluster/node/self/health` + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|--------|----------|------------------| +| `data` | `String` | 当前节点健康状态 | + +> 节点共有 `STARTING`, `UP`,`SUSPICIOUS`,`DOWN`,`ISOLATION`五种状态 + +### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/self/health' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "UP" + } + ``` + + +

切换集群寻址模式

+ +### 接口描述 + +切换集群寻址模式 + +### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +### 请求URL + +`/nacos/v2/core/cluster/lookup` + +### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------|----------|----------|----------| +| `type` | `String` | **是** | 寻址模式 | + +> 寻址模式有两种:`file`(文件配置)和 `address-server`(地址服务器) + +### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +### 示例 + +* 请求示例 + + ```shell + curl -d 'type=file' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/core/cluster/lookup' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + + diff --git a/src/content/docs/v3.0/zh-cn/guide/user/other-language.md b/src/content/docs/v3.0/zh-cn/guide/user/other-language.md new file mode 100644 index 00000000000..6f161bb0eb5 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/other-language.md @@ -0,0 +1,23 @@ +--- +title: 其他语言的SDK +keywords: [其他语言,SDK] +description: 其他语言的SDK +sidebar: + order: 2 +--- + +# 其他语言的SDK + +Nacos社区当前仅提供了Java版本的客户端,我们将主要依靠社区的贡献来发展多语言客户端。在未来,我们将向Nacos社区用户推荐那些最被广泛使用的以及支持最好的客户端作为Nacos相应语言的官方版本。 + +* [go](https://github.com/nacos-group/nacos-sdk-go) +* [cpp](https://github.com/nacos-group/nacos-sdk-cpp) +* [python](https://github.com/nacos-group/nacos-sdk-python) +* [nodejs](https://github.com/nacos-group/nacos-sdk-nodejs) +* [c#](https://github.com/nacos-group/nacos-sdk-csharp) +* more ... + +## 其他多语言用法 +> 以下多语言用法由其他社区提供和维护: + +* [PHP](https://github.com/workbunny/webman-nacos) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/guide/user/parameters-check.md b/src/content/docs/v3.0/zh-cn/guide/user/parameters-check.md new file mode 100644 index 00000000000..62bfbf17ce4 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/parameters-check.md @@ -0,0 +1,244 @@ +--- +title: 参数校验 +keywords: [参数校验,使用规则] +description: 参数校验 +date: 2023-10-24 +sidebar: + order: 4 +--- + +> 该文档即将废弃,推荐查看[用户手册-参数校验](../../manual/user/parameters-check.md)。 + +# 参数校验 + +2.3.0版本之前的Nacos的参数校验逻辑分散,由各类请求的处理方法单独进行校验,难以更改维护,经常出现参数校验的遗漏,参数校验的规则也没有明确统一;这使得用户使用时经常会因为一些特殊字符导致功能不符合预期或出现漏洞,甚至导致大量推送,导致带宽打满,内存占用过多,导致应用出现故障。 + +在2.3.0版本中,Nacos明确了参数校验规则,在服务端实现了统一的参数校验逻辑并添加了参数校验层,根据校验规则对客户端向服务端发送的请求进行校验。 + +用户可以选择开启参数校验功能,开启后Nacos将会对客户端向服务端发送的请求中的部分参数进行参数校验,确保参数的合法性,避免由于错误使用,导致的不符合预期以及性能问题。 + +## 参数校验开关 + +### 服务端 + +服务端的参数校验功能**默认开启**,用户可以通过设置`${nacos.home}/conf`目录下的`application.properties`文件中的`nacos.core.param.check.enabled`值选择开启或者关闭服务端参数校验功能。 + +`nacos.core.param.check.enabled=true`时开启Nacos服务端参数校验,`false`关闭服务端参数校验 + +### 客户端 + +待实现 + +## 参数校验规则 + +开启参数校验后OpenAPI文档 和 SDK文档中的所有接口中的相关参数都会接受格式校验,现对相关参数以及校验规则进行说明: + +|参数描述|最大字符长度|校验规则| +|-----|-----|-----| +|命名空间名称|256|禁止`@#$%^&*`,对应正则表达式:`[^@#$%^&*]+$`| +|命名空间ID|64|只允许字母数字下划线以及"-"字符,对应正则表达式:`^[\w-]+`| +|配置名称|256|只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$`| +|服务名称|512|禁止中文和`@@`且禁止以`@`开头,禁止空白字符,对应正则表达式`^(?!@).((?!@@)[^\u4E00-\u9FA5])*$`| +|分组名称|128|只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$`| +|集群名称|64|只允许数字字母和`-_`,对应正则表达式`^[0-9a-zA-Z-_]+$`| +|IP地址|128|禁止中文字符和空白字符,对应正则表达式为`^[^\u4E00-\u9FA5]*$`| +|端口号|-|取值范围为`0~65535`| +|实例元数据|1024|字段名加字段值的总长度小于1024个字符| + +### 1. namespaceShowName + +#### 参数描述 + +命名空间名称 + +#### 校验规则 + +字符长度最大为256,禁止`@#$%^&*`,对应正则表达式:`[^@#$%^&*]+$` + +#### OpenAPI示例 + +- [命名空间](./open-api.md) + +#### 校验失败报错信息 + +- 超出长度:`Param 'namespaceShowName' is illegal, the param length should not exceed 256.` +- 非法字符:`Param 'namespaceShowName' is illegal, illegal characters should not appear in the param.` + +### 2. namespaceId/tenant/namespace + +#### 参数描述 + +命名空间ID(租户空间) + +#### 校验规则 + +字符长度最大为64,只允许字母数字下划线以及"-"字符,对应正则表达式:`^[\w-]+` + +#### OpenAPI示例 + +- [获取配置](./open-api.md) +- [注册实例](./open-api.md) + +#### 校验失败报错信息 + +- 超出长度:`Param 'namespaceId/tenant' is illegal, the param length should not exceed 64.` +- 非法字符:`Param 'namespaceId/tenant' is illegal, illegal characters should not appear in the param.` + +### 3. dataId + +#### 参数描述 + +配置名称 + +#### 校验规则 + +字符长度最大为256,只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$` + +#### OpenAPI示例 + +[发布配置](./open-api.md) + +#### Java SDK示例 + +监听配置:`public void addListener(String dataId, String group, Listener listener) ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'dataId' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'dataId' is illegal, illegal characters should not appear in the param.` + +### 4. service/serviceName + +#### 参数描述 + +服务名称 + +#### 校验规则 + +字符长度最大为512,禁止中文和`@@`且禁止以`@`开头,对应正则表达式`^(?!@).((?!@@)[^\u4E00-\u9FA5])*$` + +#### OpenAPI示例 + +[注册实例](./open-api.md) + +#### Java SDK示例 + +注册实例:`void registerInstance(String serviceName, String ip, int port) throws NacosException; ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'serviceName' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'serviceName' is illegal, illegal characters should not appear in the param.` + +### 5. group/groupName + +#### 参数描述 + +分组名称 + +#### 校验规则 + +字符长度最大为128,只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$` + +#### OpenAPI示例 + +[查询实例列表](./open-api.md) + +#### Java SDK示例 + +删除配置:`public boolean removeConfig(String dataId, String group) throws NacosException ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'group' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'group' is illegal, illegal characters should not appear in the param.` + +### 6. cluster/clusterName + +#### 参数描述 + +集群名称 + +#### 校验规则 + +字符长度最大为64,只允许数字字母和`-_`,对应正则表达式`^[0-9a-zA-Z-_]+$` + +#### OpenAPI示例 + +[更新实例](./open-api.md) + +#### Java SDK示例 + +获取全部实例:`List getAllInstances(String serviceName, List clusters) throws NacosException;` + +#### 校验失败报错信息 + +- 超出长度:`Param 'cluster' is illegal, the param length should not exceed 64.` +- 非法字符:`Param 'cluster' is illegal, illegal characters should not appear in the param.` + +### 7. ip + +#### 参数描述 + +IP地址 + +#### 校验规则 + +字符长度最大为128,禁止中文字符,对应正则表达式为`^[^\u4E00-\u9FA5]*$` + +#### OpenAPI示例 + +[查询指定服务的实例列表](./open-api.md) + +#### Java SDK示例 + +注销实例:`void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;` + +#### 校验失败报错信息 + +- 超出长度:`Param 'ip' is illegal, the param length should not exceed 128.` +- 非法字符:`Param 'ip' is illegal, illegal characters should not appear in the param.` + +### 8. port + +#### 参数描述 + +端口号 + +#### 校验规则 + +取值范围为0~65535 + +#### OpenAPI示例 + +[更新实例](./open-api.md) + +#### Java SDK示例 + +注销实例:`void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;` + +#### 校验失败报错信息 + +端口取值超出范围:`Param 'port' is illegal, the value should be between 0 and 65535` + +### 9. metadata + +#### 参数描述 + +实例元数据 + +#### 校验规则 + +字段名加字段值的总长度小于1024个字符 + +#### OpenAPI示例 + +[更新实例](./open-api.md) + +#### Java SDK示例 + +注册实例:`void registerInstance(String serviceName, Instance instance) throws NacosException;` + +#### 校验失败报错信息 + +实例总长度超出范围:`Param 'Metadata' is illegal, the param length should not exceed %d.` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/guide/user/sdk.md b/src/content/docs/v3.0/zh-cn/guide/user/sdk.md new file mode 100644 index 00000000000..c954ba6db9c --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/guide/user/sdk.md @@ -0,0 +1,659 @@ +--- +title: Java SDK +keywords: [Java,SDK] +description: Java SDK +sidebar: + order: 1 +--- + +> 该文档即将废弃,推荐查看[用户手册-JAVA SDK-JAVA SDK 使用手册](../../manual/user/java-sdk/usage.md)。 + +# Java SDK + +## 概述部分 + +Maven 坐标 +``` + + com.alibaba.nacos + nacos-client + ${version} + +``` + +> 注意:由于Nacos Java SDK在2.0版本后引入了gRPC,为了避免用户业务引入的gRPC版本不同导致冲突,使用了shaded技术将部分依赖直接封装进nacos-client中,导致nacos-client较大。 +> 如果用户未自行引入gRPC或确认版本无冲突,希望使用纯净版的nacos-client以减小依赖,可以使用classifier来指定使用纯净版。 + +```xml + + + 2.1.2 + + + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + pure + + + + ${project.groupId} + nacos-common + ${nacos.version} + + + ${project.groupId} + nacos-api + ${nacos.version} + + +``` + +## 配置管理 +### 获取配置 +#### 描述 + +用于服务启动的时候从 Nacos 获取配置。 +```java +public String getConfig(String dataId, String group, long timeoutMs) throws NacosException +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写产品名:模块名(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| timeout | long | 读取配置超时时间,单位 ms,推荐值 3000。 | + + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值 | + + +#### 请求示例 + +```java +try { + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfig(dataId, group, 5000); + System.out.println(content); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 监听配置 +#### 描述 + +如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。 + +```java +public void addListener(String dataId, String group, Listener listener) +``` + +#### 请求参数 + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
参数名
+
+
参数类型
+
+
描述
+
+
dataId
+
+
string
+
+
配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。 全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"_")。不超过 256 字节。
+
+
group
+
+
string
+
+
配置分组,建议填写产品名:模块名(如 Nacos:Test)保证唯一性。 只允许英文字符和4种特殊字符("."、":"、"-"、"_"),不超过128字节。 +
+
+
listener
+
+
Listener
+
+
监听器,配置变更进入监听器的回调函数。
+
+
+ + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值,初始化或者配置变更的时候通过回调函数返回该值。 | + + +#### 请求示例 + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +String content = configService.getConfig(dataId, group, 5000); +System.out.println(content); +configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("recieve1:" + configInfo); + } + @Override + public Executor getExecutor() { + return null; + } +}); + +// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码 +while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } +} +``` + +### 删除监听 +#### 描述 + +取消监听配置,取消监听后配置不会再推送。 + +```java +public void removeListener(String dataId, String group, Listener listener) +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组 | +| listener | ConfigChangeListenerAdapter | 监听器,配置变更进入监听器的回调函数。 | + + +#### 使用示例 + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +configService.removeListener(dataId, group, yourListener); +``` + +### 发布配置 +#### 描述 + +用于通过程序自动发布 Nacos 配置,以便通过自动化手段降低运维成本。 + +注意:创建和修改配置时使用的同一个发布接口,当配置不存在时会创建配置,当配置已存在时会更新配置。 + +```java +public boolean publishConfig(String dataId, String group, String content) throws NacosException; + +@Since 1.4.1 +public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException; + +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 `package.class`(如 `com.taobao.tc.refund.log.level`)的命名规则保证全局唯一性。建议根据配置的业务含义来定义 class 部分。全部字符均为小写。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 256 字节。 | +| group | string | 配置分组,建议填写`产品名:模块名`(如 Nacos`:Test`)来保证唯一性。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 128 字节。 | +| content | string | 配置内容,不超过 100K 字节。 | +| type | string | @Since 1.4.1. 配置类型,见 `com.alibaba.nacos.api.config.ConfigType`,默认为TEXT | + + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否发布成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); +    ConfigService configService = NacosFactory.createConfigService(properties); + boolean isPublishOk = configService.publishConfig(dataId, group, "content"); + System.out.println(isPublishOk); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 删除配置 +#### 描述 + +用于通过程序自动删除 Nacos 配置,以便通过自动化手段降低运维成本。 + +__注意:__ 当配置已存在时会删除该配置,当配置不存在时会直接返回成功消息。 + + +```java +public boolean removeConfig(String dataId, String group) throws NacosException + +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID | +| group | string | 配置分组 | + + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否删除成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isRemoveOk = configService.removeConfig(dataId, group); + System.out.println(isRemoveOk); +} catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + + + +## 服务发现SDK +### 注册实例 +#### 描述注册一个实例到服务。 +```java +void registerInstance(String serviceName, String ip, int port) throws NacosException; + +void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; + +void registerInstance(String serviceName, Instance instance) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | --- | +| serviceName | 字符串 | 服务名 | +| ip | 字符串 | 服务实例IP | +| port | int | 服务实例port | +| clusterName | 字符串 | 集群名 | +| instance | 参见代码注释 | 实例属性 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.registerInstance("nacos.test.3", "11.11.11.11", 8888, "TEST1"); + +Instance instance = new Instance(); +instance.setIp("55.55.55.55"); +instance.setPort(9999); +instance.setHealthy(false); +instance.setWeight(2.0); +Map instanceMeta = new HashMap<>(); +instanceMeta.put("site", "et2"); +instance.setMetadata(instanceMeta); + +Service service = new Service("nacos.test.4"); +service.setApp("nacos-naming"); +service.sethealthCheckMode("server"); +service.setEnableHealthCheck(true); +service.setProtectThreshold(0.8F); +service.setGroup("CNCF"); +Map serviceMeta = new HashMap<>(); +serviceMeta.put("symmetricCall", "true"); +service.setMetadata(serviceMeta); +instance.setService(service); + +Cluster cluster = new Cluster(); +cluster.setName("TEST5"); +AbstractHealthChecker.Http healthChecker = new AbstractHealthChecker.Http(); +healthChecker.setExpectedResponseCode(400); +healthChecker.setCurlHost("USer-Agent|Nacos"); +healthChecker.setCurlPath("/xxx.html"); +cluster.setHealthChecker(healthChecker); +Map clusterMeta = new HashMap<>(); +clusterMeta.put("xxx", "yyyy"); +cluster.setMetadata(clusterMeta); + +instance.setCluster(cluster); + +naming.registerInstance("nacos.test.4", instance); +``` + +### 注销实例 +#### 描述 +删除服务下的一个实例。 +```java +void deregisterInstance(String serviceName, String ip, int port) throws NacosException; + +void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | :--- | +| serviceName | 字符串 | 服务名 | +| ip | 字符串 | 服务实例IP | +| port | int | 服务实例port | +| clusterName | 字符串 | 集群名 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.deregisterInstance("nacos.test.3", "11.11.11.11", 8888, "DEFAULT"); +``` +### 获取全部实例 +#### 描述 +获取服务下的所有实例。 +```java +List getAllInstances(String serviceName) throws NacosException; + +List getAllInstances(String serviceName, List clusters) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | --- | +| serviceName | 字符串 | 服务名 | +| clusters | List | 集群列表 | + +#### 返回参数 +List<Instance> 实例列表。 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.getAllInstances("nacos.test.3")); +``` + +### 获取健康或不健康实例列表 +#### 描述 +根据条件获取过滤后的实例列表。 +```java +List selectInstances(String serviceName, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | --- | +| serviceName | 字符串 | 服务名 | +| clusters | List | 集群列表 | +| healthy | boolean | 是否健康 | + +#### 返回参数 +List<Instance> 实例列表。 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.selectInstances("nacos.test.3", true)); +``` + +### 获取一个健康实例 +#### 描述 +根据负载均衡算法随机获取一个健康实例。 +```java +Instance selectOneHealthyInstance(String serviceName) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | --- | +| serviceName | 字符串 | 服务名 | +| clusters | List | 集群列表 | + +#### 返回参数 +Instance 实例。 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.selectOneHealthyInstance("nacos.test.3")); +``` + +### 监听服务 +#### 描述 +监听服务下的实例列表变化。 +```java +void subscribe(String serviceName, EventListener listener) throws NacosException; + +void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | --- | +| serviceName | 字符串 | 服务名 | +| clusters | List | 集群列表 | +| listener | EventListener | 回调listener | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.subscribe("nacos.test.3", event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}); +``` + +### 取消监听服务 +#### 描述 +取消监听服务下的实例列表变化。 +```java +void unsubscribe(String serviceName, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | +| :--- | :--- | --- | +| serviceName | 字符串 | 服务名 | +| clusters | List | 集群列表 | +| listener | EventListener | 回调listener | + +#### 返回参数 +无 + +#### 请求示例 +```java + +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +naming.unsubscribe("nacos.test.3", event -> {}); + +``` + +## NacosClientProperties +### 介绍 +从 `2.1.2` 开始引入了 `NacosClientProperties`, 一个类似于 `Spring Environment`用来统一管理客户端的各种配置项. 之前客户端的配置项散落3个地方: 用户传入的 Properties、命令行参数和环境变量. 这种没有一个统一的获取配置的入口,并且不方便做配置的隔离. 基于以上的问题,引入 `NacosClientProperties`. + +### 特点 +- 统一管理 Properties、命令行参数、环境变量和默认值 +- 提供优先级搜索功能, 默认搜索顺序 `properties -> 命令行参数 -> 环境参数 -> 默认值`, 可通过调整优先级来调整搜索顺序, 默认是 `properties` 优先 +- 配置隔离, 每个 `NacosClientProperties` 对象,除去全局性的配置互不影响. + + +### 如何使用 +#### 相关概念 +##### 优先级 +默认优先级是 `properties`, 可通过以下2种方式来调整: +- (命令行参数)-Dnacos.env.first=PROPERTIES|JVM|ENV +- (环境变量)NACOS_ENV_FIRST=PROPERTIES|JVM|ENV + +以上2种方式都指定的情况下,客户端优先使用命令行参数的方式获取优先级参数,若是通过命令行参数的方式没有获取到优先级参数则使用环境变量的方式获取优先级参数.如果以上2种方式都未指定优先级参数默认优先级为`properties` + +默认优先级: +![default_order.png](/img/nacos_client_properties_default_order.png) + +优先级: PROPERTIES +![default_order.png](/img/nacos_client_properties_default_order.png) + +优先级: JVM +![jvm_order.png](/img/nacos_client_properties_jvm_order.png) + +优先级: ENV +![jvm_order.png](/img/nacos_client_properties_env_order.png) + +##### 搜索 +`NacosClientProperties` 会按照指定优先级进行搜索配置, 以默认优先级(`PROPERTIES`)为例, 如果要获取一个 key 为 +`key1`的值, 查找顺序如下: + +![search_order.png](/img/nacos_client_properties_search_order.png) + +`NacosClientProperties` 会按照上图顺序搜索,直到查询到为止. + +#### 配置隔离 +为了应对多注册中心,多配置中心的场景, `NacosClientProperties` 引入配置隔离的概念. 在 `NacosClientProperties` 中总共有4个取值源, 分别是: 用户自定义的properties、命令行参数、 环境变量和默认值, 其中 `命令行参数、 环境变量和默认值` +这3个是全局共享的无法做到隔离, 那么只剩下用户自定义的properties对象是可以进行隔离的, 每个 `NacosClientProperties` 对象中包含不同的 `Properties` 对象, 通过这种方法做到配置互不影响. + +> 注意: 全局共享的配置: 命令行参数、 环境参数和默认值 一旦初始化完毕,后续使用无法更改,使用 `setProperty` 方法,也无法修改. `setProperty` 只能修改`NacosClientProperties` 对象中包含的 `Properties` 对象中的值 + +#### 配置派生 +在配置隔离的概念之上又引入了配置派生的概念, 其目的是让配置能够继承.所有 `NacosClientProperties` 对象都是由 `NacosClientProperties.PROTOTYPE` 对象派生而来. 无法通过其他方式创建 `NacosClientProperties` 对象 +```java +// global properties +NacosClientProperties.PROTOTYPE.setProperty("global-key1", "global-value1"); + +// properties1 +NacosClientProperties properties1 = NacosClientProperties.PROTOTYPE.derive(); +properties1.setProperty("properties1-key1", "properties1-value1"); + +// properties2 +NacosClientProperties properties2 = properties1.derive(); +properties2.setProperty("properties2-key1", "properties2-value1"); +``` +以上代码如下图所示: +![derive.png](/img/nacos_client_properties_derive.png) + +那么搜索会怎么搜索呢? 以默认优先级(PROPERTIES)为例: +```java +// value == global-value1 +String value = properties2.getProperty("global-key1"); + +``` +![derive_search.png](/img/nacos_client_properties_derive_search.png) + + + +#### API +|方法名| 入参内容| 返回内容| 描述| +| - | - | - | - | +|getProperty| key: String | String | 获取 key 对应的 value 值, 不存在返回 null| +|getProperty| key: String, default: String | String | 获取 key 对应的 value 值, 不存在返回默认值| +|getBoolean | key: String | Boolean | 获取 key 对应的 Boolean 值, 不存在则返回 null | +|getBoolean | key: String, default: Boolean | Boolean | 获取 key 对应的 Boolean 值, 不存在返回默认值| +|getInteger | key: String | Integer | 获取 key 对应的 Integer 值, 不存在返回 null | +|getInteger | key: String, default: Integer | Integer | 获取 key 对应的 Integer 值, 不存在返回默认值| +|getLong | key: String | Long | 获取 key 对应的 Long 值, 不存在返回 null| +|getLong | key: String, default: Long | Long | 获取 key 对应的 Long 值, 不存在返回默认值| +|setProperty| key: String, value: String | void | 设置 key-value 到 NacosClientProperties 对象中,已存的值会被覆盖| +|addProperties| properties: Properties | void | 添加 Properties 到 NacosClientProperties 对象中,已存在到值会被覆盖| +|containsKey| key: String | boolean | 判断是否包含指定 key 的值, 包含返回 true 否则 false| +|asProperties| void | Properties | 将 NacosClientProperties 对象转换为 Properties 对象| +|derive| void | NacosClientProperties | 创建一个继承父 NacosClientProperties 所有配置的 NacosClientProperties 对象, 内部包含一个空 Properties | +|derive| Properties | NacosClientProperties | 创建一个继承父 NacosClientProperties 所有配置的 NacosClientProperties 对象, 内部包含指定的 Properties 对象| \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/admin-api.md b/src/content/docs/v3.0/zh-cn/manual/admin/admin-api.md new file mode 100644 index 00000000000..3de19ff807a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/admin-api.md @@ -0,0 +1,974 @@ +--- +title: 运维API +keywords: [Nacos,运维API] +description: Nacos Server的一些运维API,一般给予控制台使用或需要进行自定义Nacos运维工具开发的相关程序和人员使用。 +sidebar: + order: 5 +--- + +# 运维API + +Nacos默认搭载了一整套专为管理控制台和运维人员设计的运维API,赋予运维专家更多的配置权限、更广阔的数据检索能力等。这些API为Nacos的运维团队提供了方便,使他们能够高效地处理故障、排查问题,以确保系统的稳定运行。 + +## 1. Nacos Core 运维 API + +### 1.1. 获取当前节点连接 + +#### 接口描述 + +通过该接口,可以获取连接到当前Nacos Server节点中的gRPC连接详情。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/current` + +#### 请求参数 + +无 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| ${connectionId} | `jsonString` | 每条gRPC连接的连接id | +| ${connectionId}.abilityTable | `jsonString` | 该gRPC连接(即客户端)支持的能力列表 | +| ${connectionId}.metaInfo.clientIp | `String` | 该gRPC连接的来源IP | +| ${connectionId}.metaInfo.localPort | `int` | 该Nacos Server的gRPC端口 | +| ${connectionId}.metaInfo.version | `String` | 该gRPC连接(即客户端)的版本 | +| ${connectionId}.metaInfo.createTime | `String` | 该gRPC连接的连接时间 | +| ${connectionId}.metaInfo.lastActiveTime | `timestamp` | 该gRPC连接的最后一次的心跳时间 | +| ${connectionId}.metaInfo.labels.source | `String` | 该gRPC连接的模块,可选值为`naming`,`config`和`cluster`分别代表注册中心、配置中心以及集群间的连接 | +| ${connectionId}.metaInfo.clusterSource | `boolean` | 该gRPC连接的是否为集群间连接,为`true`时,`${connectionId}.metaInfo.labels.source`为 `cluster` | +| ${connectionId}.metaInfo.sdkSource | `boolean` | 该gRPC连接的是否为客户端来源连接,为`true`时,`${connectionId}.metaInfo.labels.source`为 `naming`或`config`| + + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/current' +``` + +* 返回示例 + +```json +{ + "1709273546779_127.0.0.1_35042": { + "traced": false, + "abilityTable": {}, + "metaInfo": { + "connectType": "GRPC", + "clientIp": "127.0.0.1", + "localPort": 9849, + "version": "Nacos-Java-Client:v2.4.2", + "connectionId": "1709273546779_127.0.0.1_35042", + "createTime": "2024-03-01T14:12:26.800+08:00", + "lastActiveTime": 1710754816373, + "appName": "-", + "tenant": null, + "labels": { + "source": "naming", + "tls.enable": "false" + }, + "tag": null, + "clusterSource": false, + "sdkSource": true + }, + "connected": true, + "labels": { + "source": "naming", + "tls.enable": "false" + } + } +} +``` + +### 1.2. 均衡指定数量的连接 + +#### 接口描述 + +通过该接口,可以指定一定数量的连接到当前Nacos Server节点中的gRPC连接,将这部分连接断开后迁移到其他Nacos Server节点中。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/reloadCurrent` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `count` | `Integer` | **是** | 需要均衡的连接个数 | +| `redirectAddress` | `String` | 否 | 预期均衡的Nacos Server目标,仅提供给客户端参考。 | + +#### 返回数据 + +成功则返回`success`,失败则返回[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式) + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/reloadCurrent?count=100' +``` + +* 返回示例 + +```text +success +``` + +### 1.3. 均衡指定的单个连接 + +#### 接口描述 + +通过该接口,可以指定某一条的连接到当前Nacos Server节点中的gRPC连接,将该连接断开后迁移到其他Nacos Server节点中。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/reloadClient` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `connectionId` | `String` | **是** | 需要均衡的连接Id | +| `redirectAddress` | `String` | 否 | 预期均衡的Nacos Server目标,仅提供给客户端参考。 | + +#### 返回数据 + +成功则返回`success`,失败则返回[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式) + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/reloadClient?connectionId=1709273546779_127.0.0.1_35042' +``` + +* 返回示例 + +```text +success +``` + +### 1.4. 获取集群连接概览信息 + +#### 接口描述 + +通过该接口,查看Nacos Server集群中各节点的连接数概览。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/loader/cluster` + +#### 请求参数 + +无 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| `total` | `Integer` | 该集群中所有节点的连接数总和 | +| `min` | `Integer` | 该集群中所有节点的最小连接数 | +| `avg` | `Integer` | 该集群中所有节点的平均连接数 | +| `max` | `Integer` | 该集群中所有节点的最大连接数 | +| `memberCount` | `Integer` | 该集群中所有节点的个数 | +| `metricsCount` | `Integer` | 该集群中已统计到概览信息的节点个数 | +| `detail` | `jsonArray` | 该集群中所有节点的概览信息,格式见下表 | +| `detail[].address` | `String` | 节点地址 | +| `detail[].metric.load` | `Double` | 节点的负载率,主要对应节点的Load指标,参考值 | +| `detail[].metric.sdkConCount` | `Integer` | 连接到该节点的SDK连接数,主要对应客户端连接数 | +| `detail[].metric.conCount` | `Integer` | 连接到该节点的总连接数,包含了SDK和集群间的连接 | +| `detail[].metric.cpu` | `Double` | 节点的CPU使用率,参考值 | + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/loader/cluster' +``` + +* 返回示例 + +```json +{ + "total": 0, + "min": 0, + "avg": 0, + "max": 0, + "memberCount": 3, + "metricsCount": 3, + "threshold": 0.0, + "detail": [{ + "address": "nacos-node1:8848", + "metric": { + "load": "0.0", + "sdkConCount": "0", + "cpu": "0.0", + "conCount": "2" + } + }, { + "address": "nacos-node2:8848", + "metric": { + "load": "0.03", + "sdkConCount": "0", + "cpu": "-1.0", + "conCount": "2" + } + }, { + "address": "nacos-node3:8848", + "metric": { + "load": "0.0", + "sdkConCount": "0", + "cpu": "-1.0", + "conCount": "2" + } + }], + "completed": true +} +``` + +### 1.5. 获取本节点信息 + +#### 接口描述 + +通过该接口,可以获取Nacos Server集群当前节点的详细信息。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/cluster/node/self` + +#### 请求参数 + +无 + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式),下表只阐述`data`字段中的返回参数。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`ip`|`String` | 节点IP | +|`port`|`Integer` | 节点端口 | +|`state`|`String` | 节点状态,可选值为`UP`、`DOWN`、`SUSPICIOUS` | +|`extendInfo`|`jsonObject` | 节点扩展信息,具体字段见下表 | +|`extendInfo.lastRefreshTime`|`Long` | 节点上一次更新时间戳,单位毫秒 | +|`extendInfo.raftMetaData`|`jsonObject` | 节点的Raft元数据, 包含每个Raft Group的 `leader`, `term`等字段 | +|`extendInfo.raftPort`|`Integer` | 节点的Raft端口 | +|`extendInfo.version`|`String` | 节点的版本 | +|`address`|`String` | 节点地址,格式为`ip:port` | +|`abilities`|`jsonObject` | 该节点所支持的能力 | +|~~extendInfo.readyToUpgrade~~|`Boolean` | 是否ready升级到Nacos2.0,于2.2版本后废弃,即将移除 | + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/self' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": { + "ip": "nacos-node-0", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1709273550501, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service": { + "leader": "nacos-node-1:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service_v2": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 2 + }, + "naming_service_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.4.2" + }, + "address": "nacos-node-0:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true, + "grpcReportEnabled": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + }, + "grpcReportEnabled": true + } +} +``` + +### 1.6. 获取集群所有节点信息 + +#### 接口描述 + +通过该接口,可以获取Nacos Server集群中所有节点的详细信息。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/cluster/node/list` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`address`|`String`|否|过滤的节点地址,支持前缀匹配,不输入时返回所有节点信息| +|`nodeState`|`String`|否|返回的节点状态,可选值为`UP`、`DOWN`、`SUSPICIOUS`,不输入时返回所有节点信息| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式),`data`字段为[获取本节点信息](#返回数据-4)的返回数据的列表。 + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/list' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": [{ + "ip": "nacos-node-0", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1709273550501, + "raftMetaData": { + "metaDataMap": { + "naming_instance_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service": { + "leader": "nacos-node-1:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + }, + "naming_persistent_service_v2": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 2 + }, + "naming_service_metadata": { + "leader": "nacos-node-2:7848", + "raftGroupMember": ["nacos-node-2:7848", "nacos-node-1:7848", "nacos-node-0:7848"], + "term": 3 + } + } + }, + "raftPort": "7848", + "readyToUpgrade": true, + "version": "2.4.2" + }, + "address": "nacos-node-0:8848", + "failAccessCnt": 0, + "abilities": { + "remoteAbility": { + "supportRemoteConnection": true, + "grpcReportEnabled": true + }, + "configAbility": { + "supportRemoteMetrics": false + }, + "namingAbility": { + "supportJraft": true + } + }, + "grpcReportEnabled": true + }, { + "ip": "nacos-node-2", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1710813796567, + "raftMetaData": { + .... + }, + .... + }, + .... + }, { + "ip": "nacos-node-1", + "port": 8848, + "state": "UP", + "extendInfo": { + "lastRefreshTime": 1710813796567, + "raftMetaData": { + .... + }, + .... + }, + .... + }] +} +``` + +### 1.7. 快速查询本节点健康状态 + +#### 接口描述 + +通过该接口,可以快速查询本节点健康状态。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/core/cluster/node/self/health` + +#### 请求参数 + +无 + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|`UP`表示节点健康,`DOWN`表示节点不健康,`SUSPICIOUS`表示节点疑似不健康| + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/core/cluster/node/list' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": "UP" +} +``` + +### 1.8. 动态修改Server集群地址发现方式 + +#### 接口描述 + +通过该接口,可以在不重启Nacos Server的情况下,动态切换Nacos Server集群地址发现的方式,目前支持两种方式:`file`和`address-server`。 + +#### 请求方式 + +`PUT` + +#### 请求URL + +`/nacos/v2/core/cluster/lookup` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`type`|`String`|是|切换到的地址发现方式,可选值为`file`和`address-server`| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`Boolean`|`true`表示更新成功,`false`表示更新失败。| + +#### 示例 + +* 请求示例 + +```shell +curl -X PUT 'http://127.0.0.1:8848/nacos/v2/core/cluster/lookup?type=file' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": true +} +``` + +### 1.9. Raft 相关操作 + +#### 接口描述 + +通过该接口,可以对Nacos Server集群中的Raft协议进行部分运维操作,如执行快照,主动选主等。 + +#### 请求方式 + +`POST` + +#### 请求URL + +`/nacos/v2/core/ops/raft` + +#### 请求参数 + +该API需要以Json的方式,将请求参数放在请求体中,请求体格式如下: + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`command` |`String`|**是**|Raft运维操作指令,具体的命令请参考下表。| +|`value`|`String`|**是**|命令的参数,具体的命令内容请参考下表。| +|`groupId` |`String`|否|Raft集群的groupId,如果不输入则对所有Raft Group生效| + +|command|value|说明| +|---------------|----------|--------| +|`doSnapshot`|`${nacos-server-address}:${raft-port}`|执行快照,参数为要执行快照的节点地址。| +|`transferLeader`|`${nacos-server-address}:${raft-port}`|主动选主,参数为要期望的Leader的节点地址。| +|`restRaftCluster`|`${nacos-server-address}:${raft-port}[,${nacos-server-address}:${raft-port}]`|重置集群状态,参数为要重置节点地址列表,','分割。| +|`removePeer`|`${nacos-server-address}:${raft-port}`|移除Raft Member节点,参数为要移除的节点地址。| +|`removePeers`|`${nacos-server-address}:${raft-port}[,${nacos-server-address}:${raft-port}]`|批量移除Raft Member节点,参数为要批量移除的节点地址列表,','分割。| +|`changePeers`|`${nacos-server-address}:${raft-port}[,${nacos-server-address}:${raft-port}]`|修改Raft Member节点,参数为要修改后的节点地址列表,','分割。| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|固定为`null`。| + +#### 示例 + +* 请求示例 + +```shell +curl -X POST -H 'Content-Type:application/json' 'http://127.0.0.1:8848/nacos/v2/core/ops/raft' -d '{"command":"doSnapshot","value":"nacos-node-0:7848"}' +``` + +* 返回示例 + +```json +{ + "code": 200, + "message": null, + "data": null +} +``` + +### 1.10. 动态修改Nacos Core相关日志级别 + +#### 接口描述 + +通过该接口,可以在不重启Nacos Server的情况下,动态修改Nacos Core相关日志级别的配置。 + +#### 请求方式 + +`PUT` + +#### 请求URL + +`/nacos/v2/core/ops/log` + +#### 请求参数 + +该API需要以Json的方式,将请求参数放在请求体中,请求体格式如下: + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +|`logName` |`String`|**是**|具体的日志文件的名称,具体支持的日志名称见下表。| +|`logLevel`|`String`|**是**|日志的级别,可选值为`ALL`、`TRACE`、`DEBUG`、`INFO`、`WARN`、`ERROR`、`OFF`。| + +|logName|对应的具体日志文件| +|---------------|----------| +|`core-auth`|`core-auth.log`| +|`core-raft`|`protocol-raft.log`| +|`core-distro`|`protocol-distro.log`| +|`core-cluster`|`nacos-cluster.log`| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式)。 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|固定为`null`。| + +#### 示例 + +* 请求示例 + +```shell +curl -X PUT -H 'Content-Type:application/json' 'http://127.0.0.1:8848/nacos/v2/core/ops/log' -d '{"logName":"core-distro","logLevel":"DEBUG"}' +``` + +* 返回示例 + +```json +{ + "code": 200, + "message": null, + "data": null +} +``` + +## 2. Nacos Naming 运维 API + +### 2.1. 查看Naming模块的相关开关 + +#### 接口描述 + +通过该接口,可以查看Nacos Naming模块的相关开关。 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/operator/switches` 或 `/nacos/v2/ns/ops/switches` + +#### 请求参数 + +无 + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式),返回数据`data`字段为json格式,展示各个开关的和配置的具体内容: + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`clientBeatInterval`|`int`|Nacos1.X客户端的默认心跳间隔| +|`defaultCacheMillis`|`int`|客户端订阅的服务列表的默认缓存时间| +|`defaultPushCacheMillis`|`int`|推送的服务列表的默认缓存时间,优先级高于`defaultCacheMillis`| +|`distroEnabled` |`boolean`|是否开启`Distro`协议同步,仅当集群压力过大,影响到集群稳定性时,临时修改为`false`缓解,改为`false`后可能导致部分数据不一致,需要尽快恢复| +|`healthCheckEnabled`|`boolean`|是否开启健康检查,仅当集群压力过大,影响到集群稳定性时,临时修改为`false`缓解,改为`false`后不会因为心跳过期,tcp/http探测超时而修改实例的健康状态,以及不会因过期删除实例,需要尽快恢复| +|`lightBeatEnabled` |`boolean`|是否开启轻量心跳,针对Nacos`1.2.X~1.4.X版本`客户端生效,修改为`false`后,`Nacos1.2.X~1.4.X`版本客户端将使用全量心跳进行续约| +|`pushEnabled`|`boolean`|是否开启推送功能,仅当集群压力过大,影响到集群稳定性时,临时修改为`false`,改为`false`后,Nacos客户端将不再收到服务端的主动推送| +|`push${Language}Version`|`String`|可支持推送的最小客户端版本,当不希望针对小于某些版本进行数据推送时,可以修改该值,比如修改pushJavaVersion为`2.0.0`,则小于2.0.0的Java客户端将不会收到推送数据| +|`${type}HealthParams`|`json`|健康检查参数,设置健康检查的最大/最小间隔,随机间隔系数等,健康检查时将根据这几个值进行下一次健康检查流量的打散。| + +> 注意: 其余未列出的参数,均为Nacos旧版本的开关或配置内容,已废弃或即将废弃,请谨慎使用。 + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/ops/switches' +``` + +* 返回示例 + + +```json +{ + "code": 0, + "data": { + "adWeightMap": {}, + "autoChangeHealthCheckEnabled": true, + "checkTimes": 3, + "checksum": null, + "clientBeatInterval": 5000, + "defaultCacheMillis": 3000, + "defaultInstanceEphemeral": true, + "defaultPushCacheMillis": 10000, + "disableAddIP": false, + "distroEnabled": true, + "distroServerExpiredMillis": 10000, + "distroThreshold": 0.7, + "enableAuthentication": false, + "enableStandalone": true, + "healthCheckEnabled": true, + "healthCheckWhiteList": [], + "httpHealthParams": { + "factor": 0.85, + "max": 5000, + "min": 500 + }, + "incrementalList": [], + "lightBeatEnabled": true, + "limitedUrlMap": {}, + "masters": null, + "mysqlHealthParams": { + "factor": 0.65, + "max": 3000, + "min": 2000 + }, + "name": "00-00---000-NACOS_SWITCH_DOMAIN-000---00-00", + "overriddenServerStatus": null, + "pushCSharpVersion": "0.9.0", + "pushCVersion": "1.0.12", + "pushEnabled": true, + "pushGoVersion": "0.1.0", + "pushJavaVersion": "0.1.0", + "pushPythonVersion": "0.4.3", + "sendBeatOnly": false, + "serverStatusSynchronizationPeriodMillis": 2000, + "serviceStatusSynchronizationPeriodMillis": 5000, + "tcpHealthParams": { + "factor": 0.75, + "max": 5000, + "min": 1000 + } + }, + "message": "success" +} +``` + +### 2.2. 修改Naming模块的相关开关 + +#### 接口描述 + +通过该接口,可以修改Nacos Naming模块的相关开关。 + +#### 请求方式 + +`PUT` + +#### 请求URL + +`/nacos/v2/ns/operator/switches` 或 `/nacos/v2/ns/ops/switches` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|--------|----------|----|----------| +|`entry`|`String`|**是**|修改的开关或配置名称| +|`value`|`Object`|**是**|开关或配置的新值,不同的开关或配置的类型不同,具体请参考[开关和配置参数](#返回数据-10)| +|`debug`|`boolean`|否|是否开启调试模式,开启后,修改的配置不会同步到集群其他节点中,仅在本节点生效,默认为`false`| + +#### 返回数据 + +返回体遵循[Nacos open API 统一返回体格式](../user/open-api/#11-api-统一返回体格式): + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +|`data`|`String`|成功为`ok`,否则为`null`| + +#### 示例 + +* 请求示例 + +```shell +curl -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/ops/switches?entry=pushEnabled&value=false' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": "ok" +} +``` + +### 2.3. 查询系统当前数据指标 + +#### 接口描述 + +通过该接口,可以查询系统当前数据指标 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/operator/metrics` 或 `/nacos/v2/ns/ops/metrics` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------|-----------|----------|--------------------------| +| `onlyStatus` | `boolean` | 否 | 只显示状态,默认为`true` | + +> 当`onlyStatus`设置为`true`时,只返回表示系统状态的字符串 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|------------------------------------|----------|--------------------| +| `data` | `Object` | 系统当前数据指标 | +| `data.status` | `String` | 系统状态 | +| `data.serviceCount` | `int` | 服务数量 | +| `data.instanceCount` | `int` | 实例数量 | +| `data.subscribeCount` | `int` | 订阅数量 | +| `data.raftNotifyTaskCount` | `int` | `Raft`通知任务数量 | +| `data.responsibleServiceCount` | `int` | | +| `data.responsibleInstanceCount` | `int` | | +| `data.clientCount` | `int` | 客户端数量 | +| `data.connectionBasedClientCount` | `int` | 连接数量 | +| `data.ephemeralIpPortClientCount` | `int` | 临时客户端数量 | +| `data.persistentIpPortClientCount` | `int` | 持久客户端数量 | +| `data.responsibleClientCount` | `int` | | +| `data.cpu` | `float` | `cpu`使用率 | +| `data.load` | `float` | 负载 | +| `data.mem` | `float` | 内存使用率 | + +#### 示例 + +* 请求示例 + +```shell +curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/operator/metrics' +``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": { + "status": "UP", + "serviceCount": 2, + "instanceCount": 2, + "subscribeCount": 2, + "raftNotifyTaskCount": 0, + "responsibleServiceCount": 0, + "responsibleInstanceCount": 0, + "clientCount": 2, + "connectionBasedClientCount": 2, + "ephemeralIpPortClientCount": 0, + "persistentIpPortClientCount": 0, + "responsibleClientCount": 2, + "cpu": 0, + "load": -1, + "mem": 1 + } +} +``` + +## 3. Nacos Config 运维 API + +### 3.1. 查询指定命名空间下的配置列表 + +#### 接口描述 + +获取指定命名空间下的配置信息列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history/configs` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|----------| +| `namespaceId` | `String` | **是** | 命名空间 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|------------|----------------------| +| `data` | `Object[]` | 配置信息列表 | +| `data.id` | `String` | 配置`id` | +| `data.dataId` | `String` | 配置名 | +| `data.group` | `String` | 配置分组 | +| `data.content` | `String` | 配置内容 | +| `data.md5` | `String` | 配置内容的md5值 | +| `data.encryptedDataKey` | `String` | | +| `data.tenant` | `String` | 租户信息(命名空间) | +| `data.appName` | `String` | 应用名 | +| `data.type` | `String` | 配置文件类型 | +| `data.lastModified` | `long` | 上次修改时间 | + +> 返回数据中的配置信息只有`dataId`, `group`, `tenant`, `appName`, `type`字段有效,其他字段为默认值 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/configs?namespaceId=' + ``` + +* 返回示例 + +```json +{ + "code": 0, + "message": "success", + "data": [ + { + "id": "0", + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "content": null, + "md5": null, + "encryptedDataKey": null, + "tenant": "", + "appName": "", + "type": "yaml", + "lastModified": 0 + } + ] +} +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/auth.mdx b/src/content/docs/v3.0/zh-cn/manual/admin/auth.mdx new file mode 100644 index 00000000000..b48d2b7d5e0 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/auth.mdx @@ -0,0 +1,167 @@ +--- +title: 权限校验 +keywords: [Authorization] +description: 本文介绍了如何开启Nacos的鉴权(访问控制)功能,及一些示例。 +sidebar: + order: 6 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# 权限校验 + +> 注意 +> - Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。 +> - Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。 +> - 如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做进行[自定义插件开发](../../plugin/auth-plugin.md)。 + +## 1. 相关参数说明 + +|参数名|默认值|对应docker镜像环境变量|说明| +|-----|------|------|----| +|nacos.core.auth.enabled|false|NACOS_AUTH_ENABLE|是否开启鉴权功能| +|nacos.core.auth.system.type|nacos|NACOS_AUTH_SYSTEM_TYPE|鉴权类型| +|nacos.core.auth.plugin.nacos.token.secret.key|无默认值|NACOS_AUTH_TOKEN|默认鉴权插件用于生成用户登陆临时accessToken所使用的密钥,**使用老版本默认值有安全风险**| +|nacos.core.auth.plugin.nacos.token.expire.seconds|18000|NACOS_AUTH_TOKEN_EXPIRE_SECONDS|用户登陆临时accessToken的过期时间| +|nacos.core.auth.server.identity.key|无默认值|NACOS_AUTH_IDENTITY_KEY|用于服务端之间请求的身份识别key,**使用老版本默认值有安全风险**| +|nacos.core.auth.server.identity.value|无默认值|NACOS_AUTH_IDENTITY_VALUE|用于服务端之间请求的身份识别value,**使用老版本默认值有安全风险**| +|~~nacos.core.auth.enable.userAgentAuthWhite~~|false|NACOS_AUTH_USER_AGENT_AUTH_WHITE_ENABLE|是否使用useragent白名单,主要用于适配老版本升级,**置为true时有安全风险**| +|nacos.core.auth.plugin.nacos.token.cache.enable|false|NACOS_AUTH_CACHE_ENABLE|是否开启Token缓存,默认关闭,开启后accessToken会缓存到内存中,但权限更新时可能存在15s左右的延迟。| + +## 2. 默认控制台登录页 + +在用户开启鉴权后,控制台才需要进行登录访问。 同时针对不同的鉴权插件,提供新的接口方法,用于提示控制台是否开启登录页;同时Nacos支持关闭开源控制台,并引导到用户自定义的Nacos控制台,详情可查看[Nacos鉴权插件-服务端插件](../../plugin/auth-plugin.md)及[控制台手册-关闭登录功能](./console.md#32-关闭登录功能) + +## 3. 服务端如何开启鉴权 + + + + - 配置`token.secret.key` + + 开启鉴权之前,你需要配置自定义的用于生成JWT令牌的密钥,在application.properties中的配置信息,自定义密钥时,推荐将配置项设置为**Base64编码**的字符串,且**原始密钥长度不得低于32字符**: + + > 注意: + > 1. 密钥需要保持节点间一致,长时间不一致可能导致403 invalid token错误。 + > 2. 修改`token.secret.key`时,请确保token是有效的,如果修改成无效值,会导致后续无法登录,请求访问异常。 + + ```properties + ### 用于生成JWT令牌的密钥(Base64 String): + nacos.core.auth.default.token.secret.key=$custom_base64_token_secret_key + ``` + + - 配置`server.identity` + + 开启鉴权之前,还需要配置用于服务端之间请求的身份识别信息,在application.properties中的配置信息: + + > 注意:身份识别信息保持节点间一致,长时间不一致可能导致节点见数据不一致。 + + ```properties + ### 配置自定义身份识别的key(不可为空)和value(不可为空) + nacos.core.auth.server.identity.key=$custom_server_identity_key + nacos.core.auth.server.identity.value=$custom_server_identity_value + ``` + + - 打开鉴权开关 + + 开启鉴权开关,需要修改application.properties中的配置信息为: + ```properties + ### If turn on auth system: + nacos.core.auth.system.type=nacos + nacos.core.auth.enabled=true + ``` + + > 注意:鉴权开关是修改之后立马生效的,不需要重启服务端。 + + + + + 使用官方镜像,请在启动docker容器时,添加如下环境变量: + ```powershell + NACOS_AUTH_ENABLE=true + NACOS_AUTH_TOKEN=${custom_base64_token_secret_key} + NACOS_AUTH_IDENTITY_KEY=$custom_server_identity_key + NACOS_AUTH_IDENTITY_VALUE=$custom_server_identity_value + ``` + + 例如,可以通过如下命令运行开启了鉴权的容器: + + ```powershell + docker run --env PREFER_HOST_MODE=hostname --env MODE=standalone --env NACOS_AUTH_ENABLE=true --env NACOS_AUTH_TOKEN=$custom_base64_token_secret_key --env NACOS_AUTH_IDENTITY_KEY=$custom_server_identity_key --env NACOS_AUTH_IDENTITY_VALUE=$custom_server_identity_value -p 8848:8848 -p 9848:9848 nacos/nacos-server + ``` + + + +### 3.1. 设置管理员密码 + +自2.4.0版本开始,Nacos构建时不再提供管理员用户`nacos`的默认密码,需要在首次开启鉴权后,通过API或Nacos控制台进行管理员用户`nacos`的密码初始化步骤,具体操作如下: + + + + 通过Admin API,指定或随机生成一个管理员用户`nacos`的密码,当密码指定或生成后,该API将无法再进行调用。 + + ```powelshell + curl -X POST 'http://$nacos_server_host:$nacos_server_port/nacos/v1/auth/users/admin' -d 'password=$your_password' + ``` + + > 注意:若输入的`$your_password`为空字符串,或未带有`password`参数,Nacos将会生成随机密码,请保存好生成的随机密码。 + + 此时若指定或生成随机密码成功后,会返回如下: + + ```json + {"username":"nacos", "password":"$your_password"} + ``` + + + + 当Nacos集群开启鉴权后访问Nacos控制台时,会校验是否已经初始化过管理员用户`nacos`的密码,若发现未初始化密码时,则会跳转至初始化密码的页面进行初始化。在该页面密码文本框内输入自定义密码,然后点击提交即可; + + > 注意:若密码文本框内未输入自定义密码或输入空白密码,Nacos将会生成随机密码,请保存好生成的随机密码。 + + 初始化成功后会弹窗提示初始化成功,并展示指定的密码或随机生成的密码,请保存好此密码。 + + + + +### 3.2. 密码强度注意事项 + +> 重要:在Nacos在初始化管理员用户`nacos`、创建或用户密码更新操作期间不强制执行特定密码长度。Nacos集群管理员有责任对自行构建的密码强度进行控制。为了避免与密码强度相关的安全风险,强烈建议初始化时使用强度较高的密码。 + +### 3.3. 升级集群注意事项 + +> 若Nacos集群升级自旧版本的Nacos,Nacos会认为当前已经存在管理员用户`nacos`,因此并不会要求进行管理员用户`nacos`的初始化流程,管理员用户`nacos`的密码仍然会保留旧版本时所使用的密码;因此若是使用旧版本时未修改过管理员用户`nacos`的默认密码,强烈建议升级后尽快修改管理员用户`nacos`的密码。 + +## 4. 客户端如何进行鉴权 + +请参考[用户手册-配置鉴权](../user/auth.mdx) + +## 5. 开启Token缓存功能 + +无论是客户端SDK还是OpenAPI,在调用login接口获取accessToken之后,携带accessToken访问服务端,服务端解析Token进行鉴权。解析的动作比较耗时,如果想要提升接口的性能,可以考虑开启缓存Token的功能,用字符串比较代替Token解析。 + +默认鉴权插件模块支持token缓存功能,可参见[ISSUE #9906](https://github.com/alibaba/nacos/issues/9906) + +### 5.1. 开启方式 + + + + + 在application.properties中的配置信息: + ```properties + nacos.core.auth.plugin.nacos.token.cache.enable=true + ``` + + + + 镜像暂不支持该功能 + + + + +### 5.2. 注意事项 +在开启Token缓存功能之前,服务端对每一个携带用户名密码访问login接口的请求都会生成新的token,接口的返回值中的tokenTtl字段跟服务端配置文件中设置的值相等,配置如下: +``` +nacos.core.auth.plugin.nacos.token.expire.seconds=18000 +// 对应的Docker镜像变量为NACOS_AUTH_TOKEN_EXPIRE_SECONDS +``` +在开启Token缓存功能之后,服务端对每一个携带用户名密码访问login接口的请求,会先检查缓存中是否存在该用户名对应的token。若不存在,生成新的Token,插入缓存再返回;若存在,返回该token,此时tokenTtl字段的值为配置文件中设置的值减去该Token在缓存中存留的时长。 +如果Token在缓存中存留的时长超过配置文件设置的值的90%,当login接口收到请求时,尽管缓存中存在该用户名对应的Token,服务端会重新生成Token返回给请求方,并更新缓存。因此,最差情况下,请求方收到的tokenTtl只有配置文件设置的值的10%。 diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/console.md b/src/content/docs/v3.0/zh-cn/manual/admin/console.md new file mode 100644 index 00000000000..22bf466849c --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/console.md @@ -0,0 +1,177 @@ +--- +title: 控制台手册 +keywords: [控制台,手册] +description: Nacos 控制台主要旨在于增强对于服务列表,健康状态管理,服务治理,分布式配置管理等方面的管控能力。 +sidebar: + order: 9 +--- + +# 控制台手册 + +[Nacos 控制台](http://console.nacos.io/nacos/index.html)主要旨在于增强对于服务列表,健康状态管理,服务治理,分布式配置管理等方面的管控能力,以便进一步帮助用户降低管理微服务应用架构的成本,将提供包括下列基本功能: + +* 服务管理 + * 服务列表及服务健康状态展示 + * 服务元数据存储及编辑 + * 服务流量权重的调整 + * 服务优雅上下线 +* 配置管理 + * 多种配置格式编辑 + * 编辑DIFF + * 示例代码 + * 推送状态查询 + * 配置版本及一键回滚 +* 命名空间 +* 登录管理 + +## 1. 特性详解 + +### 1.1. 服务管理 + +开发者或者运维人员往往需要在服务注册后,通过友好的界面来查看服务的注册情况,包括当前系统注册的所有服务和每个服务的详情。并在有权限控制的情况下,进行服务的一些配置的编辑操作。Nacos在这个版本开放的控制台的服务发现部分,主要就是提供用户一个基本的运维页面,能够查看、编辑当前注册的服务。 + +#### 1.1.1. 服务列表管理 + +服务列表帮助用户以统一的视图管理其所有的微服务以及服务健康状态。整体界面布局是左上角有服务的搜索框和搜索按钮,页面中央是服务列表的展示。服务列表主要展示服务名、集群数目、实例数目、健康实例数目和详情按钮五个栏目。 + +![image.png | left | 747x281](https://cdn.nlark.com/lark/0/2018/png/15356/1540536911804-3660f0e9-855f-4439-ac23-e76f6f644360.png "") + +在服务列表页面点击详情,可以看到服务的详情。可以查看服务、集群和实例的基本信息。 + +#### 1.1.2. 服务流量权重支持及流量保护 + +Nacos 为用户提供了流量权重控制的能力,同时开放了服务流量的阈值保护,以帮助用户更好的保护服务服务提供者集群不被意外打垮。如下图所以,可以点击实例的编辑按钮,修改实例的权重。如果想增加实例的流量,可以将权重调大,如果不想实例接收流量,则可以将权重设为0。 + +![image.png | left | 747x266](https://cdn.nlark.com/lark/0/2018/png/15356/1540537029452-dffbb078-4ae5-4397-9f70-083e0ebbb5be.png "") + +#### 1.1.3. 服务元数据管理 + +Nacos提供多个维度的服务元数据的暴露,帮助用户存储自定义的信息。这些信息都是以K-V的数据结构存储,在控制台上,会以k1=v1,k2=v2这样的格式展示。类似的,编辑元数据可以通过相同的格式进行。例如服务的元数据编辑,首先点击服务详情页右上角的“编辑服务”按钮,然后在元数据输入框输入:version=1.0,env=prod。 + +![image.png | left | 747x271](https://cdn.nlark.com/lark/0/2018/png/15356/1540537359751-217d7500-c19c-4bad-8508-27f347f48a2f.png "") + +点击确认,就可以在服务详情页面,看到服务的元数据已经更新了。 + +![image.png | left | 747x145](https://cdn.nlark.com/lark/0/2018/png/15356/1540537452673-01dc6c92-329a-4b6f-a616-36dc546c3355.png "") + +#### 1.1.4. 服务优雅上下线 + +Nacos还提供服务实例的上下线操作,在服务详情页面,可以点击实例的“上线”或者“下线”按钮,被下线的实例,将不会包含在健康的实例列表里。 + +![image.png | left | 747x142](https://cdn.nlark.com/lark/0/2018/png/15356/1540537640435-b28cb279-75af-4965-8a9a-54cee213f1a5.png "") + +### 1.2. 配置管理 + +Nacos支持基于Namespace和Group的配置分组管理,以便用户更灵活的根据自己的需要按照环境或者应用、模块等分组管理微服务以及Spring的大量配置,在配置管理中主要提供了配置历史版本、回滚、订阅者查询等核心管理能力。 + +![image.png | left | 747x297](https://cdn.nlark.com/lark/0/2018/png/9687/1540458893745-219a46a8-ebd9-405b-9e8f-226f3f0c7e76.png "") + +#### 1.2.1. 多配置格式编辑器 + +Nacos支持 YAML、Properties、TEXT、JSON、XML、HTML 等常见配置格式在线编辑、语法高亮、格式校验,帮助用户高效编辑的同时大幅降低格式错误带来的风险。 + +Nacos支持配置标签的能力,帮助用户更好、更灵活的做到基于标签的配置分类及管理。同时支持用户对配置及其变更进行描述,方便多人或者跨团队协作管理配置。 + +![image.png | left | 747x426](https://cdn.nlark.com/lark/0/2018/png/9687/1540458995051-b3e67fd4-c905-4552-9e52-f54b6ef59941.png "") + +#### 1.2.2. 编辑DIFF + +Nacos支持编辑DIFF能力,帮助用户校验修改内容,降低改错带来的风险。 + +![image.png | left | 747x338](https://cdn.nlark.com/lark/0/2018/png/9687/1540457990344-a60e1db3-ca1a-47ed-a03e-f92e37745247.png "") + +#### 1.2.3. 示例代码 + +Nacos提供示例代码能力,能够让新手快速使用客户端编程消费该配置,大幅降低新手使用门槛。 + +![image.png | left | 747x223](https://cdn.nlark.com/lark/0/2018/png/9687/1540456991412-01acc11c-8b48-48d8-9032-589ebb9388d9.png "") + +![image.png | left | 747x380](https://cdn.nlark.com/lark/0/2018/png/9687/1540532899571-ccea6b6f-a1e1-44d1-a130-f9afaba01c51.png "") + +#### 1.2.4. 监听者查询 + +Nacos提供配置订阅者即监听者查询能力,同时提供客户端当前配置的MD5校验值,以便帮助用户更好的检查配置变更是否推送到 Client 端。 + +![image.png | left | 747x185](https://cdn.nlark.com/lark/0/2018/png/9687/1540459212236-0abdc558-68b9-4585-b11e-c9a1924ce7ef.png "") + +#### 1.2.5. 配置的版本及一键回滚 + +Nacos通过提供配置版本管理及其一键回滚能力,帮助用户改错配置的时候能够快速恢复,降低微服务系统在配置管理上的一定会遇到的可用性风险。 + +![image.png | left | 747x242](https://cdn.nlark.com/lark/0/2018/png/9687/1540459226967-a258b9a7-f95f-41b0-874f-2a0a5da2fc5c.png "") + +![image.png | left | 747x493](https://cdn.nlark.com/lark/0/2018/png/9687/1540459237821-d4c06d16-b356-4953-a6e7-da949b1f3aec.png "") + +## 2. 命名空间管理 + +Nacos 基于Namespace 帮助用户逻辑隔离多个命名空间,这可以帮助用户更好的管理测试、预发、生产等多环境服务和配置,让每个环境的同一个配置(如数据库数据源)可以定义不同的值。 + +![image.png | left | 747x298](https://cdn.nlark.com/lark/0/2018/png/9687/1540519411777-74908cc2-29bc-4270-be58-aed62605228f.png "") + +![image.png | left | 747x206](https://cdn.nlark.com/lark/0/2018/png/9687/1540519427066-effd5153-02c9-4e21-ae9f-1a2e9ae7713e.png "") + +## 3. 登录管理 + +Nacos支持简单登录功能,在开启[鉴权](./auth.mdx)功能后启用,管理员用户名为: `nacos`, 密码需要在首次开启控制台时进行初始化。 + +![login](https://cdn.nlark.com/yuque/0/2019/jpeg/338441/1561262748106-4fc05174-bf70-4806-bcbd-90296c5bcbaa.jpeg) + +### 3.1. 修改默认用户名/密码方法 + +#### 3.1.1. 初始化时生成 + +当Nacos集群开启鉴权后访问Nacos控制台时,会校验是否已经初始化过管理员用户`nacos`的密码,若发现未初始化密码时,则会跳转至初始化密码的页面进行初始化。在该页面密码文本框内输入自定义密码,然后点击提交即可; + +> 注意:若密码文本框内未输入自定义密码或输入空白密码,Nacos将会生成随机密码,请保存好生成的随机密码。 + +初始化成功后会弹窗提示初始化成功,并展示指定的密码或随机生成的密码,请保存好此密码。 + +#### 3.1.2. 控制台上修改 + +1. 登录控制台,选择`权限控制` -> `用户列表`。 +2. 找到nacos用户,并在该用户对应的`操作`列表中点击`修改`按钮,进行密码修改 + +### 3.2. 关闭登录功能 + +Nacos默认控制台在`2.2.2`版本前,无论是否开启[鉴权](../../manual/admin/auth.mdx)功能,默认控制台都会跳转到登录页,导致用户被误导认为控制台存在鉴权功能,实际没有开启鉴权,存在安全隐患。 + +经过社区和安全工程师协商讨论,需要在使用Nacos默认控制台时,鉴权开关关闭时将会自动关闭控制台登录功能。 + +当鉴权开关`nacos.core.auth.enabled`关闭时,Nacos默认控制台将不再跳转登录页,同时添加页面提示,提示当前集群未开启鉴权功能。 + +同时针对自定义的[鉴权插件](../../plugin/auth-plugin.md)添加新接口`com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService#isLoginEnabled(默认返回false)`来对自定义插件进行登录页控制。 + +### 3.3. 关闭默认控制台 + +部分公司或用户希望关闭默认控制台,使用公司的统一平台进行Nacos的配置和服务管理;或将控制台鉴权和客户端访问的鉴权分离,即控制台操作进行鉴权但客户端请求不进行鉴权。 + +可以通过`${nacoshome}/conf/application.properties`中的`nacos.console.ui.enabled`来开启或关闭Nacos默认控制台,默认为开启。 + +同时在关闭默认控制台时,默认控制台会读取`${nacoshome}/conf/console-guide.conf`文件中的内容,并在默认控制台中生成引导页,让维护者自定义将使用默认控制台的用户引导向自定义的统一平台上进行操作。 + +### 3.4. 会话时间 + +默认会话保持时间为30分钟。30分钟后需要重新登录认证。可通过`${nacoshome}/conf/application.properties`中的`nacos.core.auth.plugin.nacos.token.expire.seconds`来设置会话保持时间。 + +## 4. 社区参与的前端共建 + +在Nacos前端风格、布局的讨论中,社区踊跃投票,最终选择了这套经典黑白蓝风格的皮肤,并且通过我们UED程瑶同学的设计,布局,让交互变得十分自然流畅。 后续又由社区添加了深色主题风格,可以让Nacos控制台在两种样式主题中切换。 + +在控制台的开发之前,我们通过社区招募到了很多前端同学一起参与了前端代码的开发,在此尤其感谢李晨、王庆、王彦民等同学在Nacos前端开发过程中的大力支持! + +## 5. 坚持社区化发展,欢迎加入并贡献社区 + +> 比吐槽更重要的是搭把手,参与社区一起发展Nacos! + +要加入Nacos 社区群进行讨论和参与贡献,请扫描下方二维码加入社区群。 + +![image.png](https://cdn.nlark.com/yuque/0/2023/png/1577777/1679276899363-83081d59-67c6-4501-9cf8-0d84ba7c6d7e.png#averageHue=%23c1c2c2&clientId=u9dfeac18-3281-4&from=paste&height=551&id=ubcf45e51&name=image.png&originHeight=1102&originWidth=854&originalType=binary&ratio=2&rotation=0&showTitle=false&size=155261&status=done&style=none&taskId=ud6bea1fe-b003-441b-a810-84435d2aeff&title=&width=427) + +更多与 Nacos 相关的开源项目信息: + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Spring Project](https://github.com/nacos-group/nacos-spring-project) +* [Nacos Spring Boot](https://github.com/nacos-group/nacos-spring-boot-project) +* [Spring Cloud Alibaba](https://github.com/alibaba/spring-cloud-alibaba) +* [Dubbo](https://github.com/apache/dubbo) +* [Sentinel](https://github.com/alibaba/Sentinel) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-cluster.md b/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-cluster.md new file mode 100644 index 00000000000..ccee57ec7a9 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-cluster.md @@ -0,0 +1,365 @@ +--- +title: 集群模式部署 +keywords: [Nacos,部署,集群模式] +description: Nacos集群模式部署手册,参考本文档可进行Nacos集群模式多种方式部署。 +sidebar: + order: 3 +--- + +# Nacos集群模式 + +本部署手册是帮忙您快速在你的电脑上,下载安装并使用Nacos,部署生产使用的集群模式。 + +### 集群部署架构图 + +无论采用何种部署方式,推荐用户把Nacos集群中所有服务节点放到一个vip下面,然后挂到一个域名下面。 + +`` 直连ip模式,机器挂则需要修改ip才可以使用。 + +`` 挂载SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),直连SLB即可,下面挂server真实ip,可读性不好。 + +`` 域名 + SLB模式(内网SLB,不可暴露到公网,以免带来安全风险),可读性好,而且换ip方便,推荐模式 + +![deployDnsVipMode.jpg](/img/doc/manual/admin/deployment/deploy-dns-vip-mode.svg) + +在使用VIP时,需要开放Nacos服务的主端口(默认8848)以及gRPC端口(默认9848)、同时如果对Nacos的主端口有所修改的话,需要对vip中的端口映射作出配置,具体端口的映射方式参考[部署手册概览-Nacos部署架构](./deployment-overview/#1-Nacos部署架构) + +## 1. 发行版部署 + +### 1.1. 使用MySQL数据库(推荐) + +#### 1.1.1. 环境准备 + +参考[快速开始](../../../quickstart/quick-start.mdx)中,进行Nacos的环境准备、发行版的下载等。 + +同时在使用MySQL数据源部署Nacos单机模式时,需要自行准备MySQL数据库: + +- 1.安装数据库,版本要求:5.6.5+ +- 2.初始化mysql数据库,数据库初始化文件:[mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +#### 1.1.2. 配置集群配置文件 + +在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点) + +```plain +# ip:port +200.8.9.16:8848 +200.8.9.17:8848 +200.8.9.18:8848 +``` + +#### 1.1.3. 修改配置文件 + +然后修改`${nacos.home}/conf/application.properties`文件,增加支持MySQL数据源配置,添加MySQL数据源的url、用户名和密码。 + +``` +spring.sql.init.platform=mysql + +db.num=1 +db.url.0=jdbc:mysql://${mysql_host}:${mysql_port}/${nacos_database}?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true +db.user=${mysql_user} +db.password=${mysql_password} +``` + +##### 1.1.3.1. 开启默认鉴权插件(可选,推荐) + +修改`conf`目录下的`application.properties`文件。 + +设置其中 + +```properties +nacos.core.auth.enabled=true +nacos.core.auth.system.type=nacos +nacos.core.auth.plugin.nacos.token.secret.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.value=${自定义,保证所有节点一致} +``` + +上述内容详情可查看[权限认证](../../../plugin/auth-plugin.md). + +> 注意,文档中的默认值`SecretKey012345678901234567890123456789012345678901234567890123456789`和`VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=`为公开默认值,可用于临时测试,实际使用时请**务必**更换为自定义的其他有效值。 + +#### 1.1.4. 启动Nacos集群 + +在每个部署节点上,执行如下命名,逐台或同时启动Nacos节点。 + +```bash +# Linux/Unix/Mac +sh startup.sh + +# Ubuntu + +bash startup.sh + +# Windows +startup.cmd +``` + +### 1.2. 使用Derby数据库 + +> 注意:Derby数据库为本地内置数据库,本身不支持集群模式,Nacos通过Raft协议将各个节点的Derby数据库组成逻辑集群,因此使用此模式部署集群模式的Nacos是,需要对Raft协议较为熟悉,能够进行问题排查、恢复等,建议使用MySQL数据库进行部署。 + +#### 1.2.1. 环境准备 + +参考[快速开始](../../../quickstart/quick-start.mdx)中,进行Nacos的环境准备、发行版的下载等。 + +#### 1.2.2. 配置集群配置文件 + +在nacos的解压目录nacos/的conf目录下,有配置文件cluster.conf,请每行配置成ip:port。(请配置3个或3个以上节点) + +```plain +# ip:port +200.8.9.16:8848 +200.8.9.17:8848 +200.8.9.18:8848 +``` + +#### 1.2.3. 开启默认鉴权插件(可选,推荐) + +修改`conf`目录下的`application.properties`文件。 + +设置其中 + +```properties +nacos.core.auth.enabled=true +nacos.core.auth.system.type=nacos +nacos.core.auth.plugin.nacos.token.secret.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.key=${自定义,保证所有节点一致} +nacos.core.auth.server.identity.value=${自定义,保证所有节点一致} +``` + +上述内容详情可查看[权限认证](../../../plugin/auth-plugin.md). + +> 注意,文档中的默认值`SecretKey012345678901234567890123456789012345678901234567890123456789`和`VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=`为公开默认值,可用于临时测试,实际使用时请**务必**更换为自定义的其他有效值。 + +#### 1.2.4. 启动Nacos集群 + +在每个部署节点上,执行如下命名,逐台或同时启动Nacos节点。 + +```bash +# Linux/Unix/Mac +sh startup.sh -p embedded + +# Ubuntu + +bash startup.sh -p embedded + +# Windows +startup.cmd -p embedded +``` + +### 1.3. 高级使用 + +#### 1.3.1. 自定义配置 + +Nacos提供了丰富的可配置项,帮助您调整Nacos的性能、控制Nacos提供的功能能力,例如鉴权、监控、数据库、连接、日志等;详情请参考[系统参数](../system-configurations.md)。 + +## 2. Docker部署 + +### 2.1. 使用MySQL数据库(推荐) + +参考[快速开始 Docker](../../../quickstart/quick-start-docker.mdx)中,进行`nacos-docker`项目的下载,然后执行如下命令,即可启动Nacos集群。 + +```powershell +docker-compose -f example/cluster-hostname.yaml up +``` + +### 2.2. 使用Derby数据库 + +> 注意:Derby数据库为本地内置数据库,本身不支持集群模式,Nacos通过Raft协议将各个节点的Derby数据库组成逻辑集群,因此使用此模式部署集群模式的Nacos是,需要对Raft协议较为熟悉,能够进行问题排查、恢复等,建议使用MySQL数据库进行部署。 + +参考[快速开始 Docker](../../../quickstart/quick-start-docker.mdx)中,进行`nacos-docker`项目的下载,然后执行如下命令,即可启动Nacos集群。 + +```powershell +docker-compose -f example/cluster-embedded.yaml up +``` + +### 2.3 高级配置 + +如果你有很多自定义配置的需求,可以通过指定[系统参数-镜像环境变量](../system-configurations/#2-镜像环境变量)的方式进行配置,例如需要开启鉴权时: + +```powershell +docker run --name nacos-cluster-auth -e MODE=cluster -e NACOS_AUTH_ENABLE=true -e NACOS_AUTH_TOKEN=${customToken} -e NACOS_AUTH_IDENTITY_KEY=${customKey} NACOS_AUTH_IDENTITY_VALUE=${customValue} -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +同时,可以通过对application.properties文件进行挂卷定义的方式,将更多复杂的自定义配置导入Nacos容器中,强烈建议在生产环境中使用方式,例如: + +```powershell +docker run --name nacos-cluster -e MODE=cluster -v /path/application.properties:/home/nacos/conf/application.properties -v /path/cluster.conf:/home/nacos/conf/cluster.conf -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +如果仍然无法满足自定义需求,可以基于nacos-docker项目中的`Dockerfile`自行构建镜像。 + +## 3. Kubernetes部署 + +通过[快速开始 Kubernetes](../../../quickstart/quick-start-kubernetes.mdx)文档,已经能够部署使用MySQL数据库的Nacos的集群模式。 + +但快速开始所部署的Nacos集群没有使用持久化卷的,可能存在数据丢失风险;因此推荐使用PVC持久卷方式进行部署,本例中将使用的是NFS来使用PVC。 + +#### Tips + +* 推荐使用[Nacos Operator](https://github.com/nacos-group/nacos-k8s/blob/master/operator/README-CN.md)在Kubernetes部署Nacos Server. + +### 3.1. 部署 NFS + +* 创建角色 + +```shell +kubectl create -f deploy/nfs/rbac.yaml +``` + +> 如果的K8S命名空间不是**default**,请在部署RBAC之前执行以下脚本: + +```shell +# Set the subject of the RBAC objects to the current namespace where the provisioner is being deployed +$ NS=$(kubectl config get-contexts|grep -e "^\*" |awk '{print $5}') +$ NAMESPACE=${NS:-default} +$ sed -i'' "s/namespace:.*/namespace: $NAMESPACE/g" ./deploy/nfs/rbac.yaml + +``` + +* 创建 `ServiceAccount` 和部署 `NFS-Client Provisioner` + +```shell +kubectl create -f deploy/nfs/deployment.yaml +``` + +* 创建 NFS StorageClass + +```shell +kubectl create -f deploy/nfs/class.yaml +``` + +* 验证NFS部署成功 + +```shell +kubectl get pod -l app=nfs-client-provisioner +``` + +### 3.2. 部署数据库 + +```shell + +cd nacos-k8s + +kubectl create -f deploy/mysql/mysql-nfs.yaml +``` + +* 验证数据库是否正常工作 + +```shell + +kubectl get pod +NAME READY STATUS RESTARTS AGE +mysql-gf2vd 1/1 Running 0 111m + +``` +### 3.3. 执行数据库初始化语句 + +数据库初始化语句位置 [mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + +### 3.4. 部署Nacos + +* 修改 **deploy/nacos/nacos-pvc-nfs.yaml** + +```yaml +data: + mysql.host: "数据库地址" + mysql.db.name: "数据库名称" + mysql.port: "端口" + mysql.user: "用户名" + mysql.password: "密码" +``` + +* 创建 Nacos + +``` shell +kubectl create -f nacos-k8s/deploy/nacos/nacos-pvc-nfs.yaml +``` + +* 验证Nacos节点启动成功 + +```shell +kubectl get pod -l app=nacos + + +NAME READY STATUS RESTARTS AGE +nacos-0 1/1 Running 0 19h +nacos-1 1/1 Running 0 19h +nacos-2 1/1 Running 0 19h +``` + +### 3.5. 扩容测试 + +* 在扩容前,使用 [`kubectl exec`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands/#exec)获取在pod中的Nacos集群配置文件信息 + +```powershell +for i in 0 1; do echo nacos-$i; kubectl exec nacos-$i cat conf/cluster.conf; done +``` + +StatefulSet控制器根据其序数索引为每个Pod提供唯一的主机名。 主机名采用 - 的形式。 因为nacos StatefulSet的副本字段设置为2,所以当前集群文件中只有两个Nacos节点地址 + +![k8s](https://cdn.nlark.com/yuque/0/2019/gif/338441/1562846123635-e361d2b5-4bbe-4347-acad-8f11f75e6d38.gif) + +* 使用kubectl scale 对Nacos动态扩容 + +```bash +kubectl scale sts nacos --replicas=3 +``` + +![scale](https://cdn.nlark.com/yuque/0/2019/gif/338441/1562846139093-7a79b709-9afa-448a-b7d6-f57571d3a902.gif) + +* 在扩容后,使用 [`kubectl exec`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands/#exec)获取在pod中的Nacos集群配置文件信息 + +```bash +for i in 0 1 2; do echo nacos-$i; kubectl exec nacos-$i cat conf/cluster.conf; done +``` + +![get_cluster_after](https://cdn.nlark.com/yuque/0/2019/gif/338441/1562846177553-c1c7f379-1b41-4026-9f0b-23e15dde02a8.gif) + +* 使用 [`kubectl exec`](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands/#exec)执行Nacos API 在每台节点上获取当前**Leader**是否一致 + +```bash +for i in 0 1 2; do echo nacos-$i; kubectl exec nacos-$i curl -X GET "http://localhost:8848/nacos/v1/ns/raft/state"; done +``` + +到这里你可以发现新节点已经正常加入Nacos集群当中。 + +### 3.6. 配置属性 + +* `nacos-pvc-nfs.yaml` or `nacos-quick-start.yaml` + +| 名称 | 必要 | 描述 | +| ----------------------- | -------- | --------------------------------------- | +| `mysql.host` | Y | 自建数据库地址,使用外部数据库时必须指定 | +| `mysql.db.name` | Y | 数据库名称 | +| `mysql.port` | N | 数据库端口 | +| `mysql.user` | Y | 数据库用户名(请不要含有符号```,```) | +| `mysql.password` | Y | 数据库密码(请不要含有符号```,```) | +| `SPRING_DATASOURCE_PLATFORM` | Y | 数据库类型,默认为embedded嵌入式数据库,参数只支持mysql或embedded | +| `NACOS_REPLICAS` | N | 确定执行Nacos启动节点数量,如果不适用动态扩容插件,就必须配置这个属性,否则使用扩容插件后不会生效 | +| `NACOS_SERVER_PORT` | N | Nacos 端口 为peer_finder插件提供端口| +| `NACOS_APPLICATION_PORT` | N | Nacos 端口| +| `PREFER_HOST_MODE` | Y | 启动Nacos集群按域名解析 | + +* **nfs** `deployment.yaml` + +| 名称 | 必要 | 描述 | +| ------------ | --------| ------------------------ | +| `NFS_SERVER` | Y | NFS 服务端地址 | +| `NFS_PATH` | Y | NFS 共享目录 | +| `server` | Y | NFS 服务端地址 | +| `path` | Y | NFS 共享目录 | + +* mysql + +| 名称 | 必要 | 描述 | +| -------------------------- | -------- | ----------------------------------------------------------- | +| `MYSQL_ROOT_PASSWORD` | N | ROOT 密码 | +| `MYSQL_DATABASE` | Y | 数据库名称 | +| `MYSQL_USER` | Y | 数据库用户名 | +| `MYSQL_PASSWORD` | Y | 数据库密码 | +| `MYSQL_REPLICATION_USER` | Y | 数据库复制用户 | +| `MYSQL_REPLICATION_PASSWORD` | Y | 数据库复制用户密码 | +| `Nfs:server` | N | NFS 服务端地址,如果使用本地部署不需要配置 | +| `Nfs:path` | N | NFS 共享目录,如果使用本地部署不需要配置 | diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-overview.md b/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-overview.md new file mode 100644 index 00000000000..a9affd8f35e --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-overview.md @@ -0,0 +1,91 @@ +--- +title: 部署手册概览 +keywords: [Nacos,部署模式] +description: Nacos支持三种部署模式 +sidebar: + order: 1 +--- + +# Nacos部署手册 + +> Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,强烈不建议部署在公共网络环境。 +> +> 以下文档中提及的VIP,网卡等所有网络相关概念均处于内部网络环境。 + +## 1. Nacos部署架构 + +Nacos2.X版本新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port,默认8848)基础上,进行一定偏移量自动生成,具体端口内容及偏移量请参考如下: + +|端口|与主端口的偏移量|描述| +|--|--|--| +|9848|1000|客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求| +|9849|1001|服务端gRPC请求服务端端口,用于服务间同步等| +|7848|-1000|Jraft请求服务端端口,用于处理服务端间的Raft相关请求| + +> **使用VIP/nginx请求时,需要配置成TCP转发,不能配置http2转发,否则连接会被nginx断开。** +> +> **对外暴露端口时,只需要暴露主端口(默认8848)和gRPC端口(默认9848),其他端口为服务端之间的通信端口,请勿暴露其他端口,同时建议所有端口均不暴露在公网下。** + +![nacos2_port_exposure.png](/img/doc/manual/admin/deployment/deploy-port-export.svg) + +客户端拥有相同的计算逻辑,用户如同1.X的使用方式,配置主端口(默认8848),通过相同的偏移量,计算对应gRPC端口(默认9848)。 + +因此如果客户端和服务端之前存在端口转发,或防火墙时,需要对端口转发配置和防火墙配置做相应的调整。 + +## 2. Nacos支持三种部署模式 + +* 单机模式 - 又称单例模式,主要用于测试和单机试用。 +* 集群模式 - 主要用于生产环境,确保高可用。 +* 多集群模式(TODO) - 用于多数据中心场景。 + +![Nacos部署模式图](/img/doc/overview/deploy-structure.svg) + +### 2.1. 单机模式 + +单机模式又称`单例模式`, 拥有所有Nacos的功能及特性,具有极易部署、快速启动等优点。但无法与其他节点组成集群,无法在节点或网络故障时提供高可用能力。单机模式同样可以使用内置Derby数据库(默认)和外置数据库进行存储。 + +单机模式主要适合于工程师于本地搭建或于测试环境中搭建Nacos环境,主要用于开发调试及测试使用;也能够兼顾部分对稳定性和可用性要求不高的业务场景。 + +单机模式的部署参考文档: [单机模式部署](./deployment-standalone.mdx) + +### 2.2. 集群模式 + +集群模式通过自研一致性协议Distro以及Raft协议,将多个Nacos节点构建成了高可用的Nacos集群。数据将在集群中各个节点进行同步,保证数据的一致性。集群模式具有高可用、高扩展、高并发等优点,确保在故障发生时不影响业务的运行。集群模式**默认**采用外置数据库进行存储,但也可以通过内置数据库进行存储。 + +该模式主要适合于生产环境,也是社区最为推荐的部署模式。 + +集群模式的部署参考文档: [集群模式部署](./deployment-cluster.md) + +### 2.3. 多集群模式(TODO) + +Nacos支持NameServer路由请求模式,通过它您可以设计一个有用的映射规则来控制请求转发到相应的集群,在映射规则中您可以按命名空间或租户等分片请求... + +## 3. 多网卡IP选择 + +当本地环境比较复杂的时候,Nacos服务在启动的时候需要选择运行时使用的IP或者网卡。Nacos从多网卡获取IP参考Spring Cloud设计,通过nacos.inetutils参数,可以指定Nacos使用的网卡和IP地址。目前支持的配置参数有: + +- ip-address参数可以直接设置nacos的ip + +``` +nacos.inetutils.ip-address=10.11.105.155 +``` + +- use-only-site-local-interfaces参数可以让nacos使用局域网ip,这个在nacos部署的机器有多网卡时很有用,可以让nacos选择局域网网卡 + +``` +nacos.inetutils.use-only-site-local-interfaces=true +``` + +- ignored-interfaces支持网卡数组,可以让nacos忽略多个网卡 + +``` +nacos.inetutils.ignored-interfaces[0]=eth0 +nacos.inetutils.ignored-interfaces[1]=eth1 +``` + +- preferred-networks参数可以让nacos优先选择匹配的ip,支持正则匹配和前缀匹配 + +``` +nacos.inetutils.preferred-networks[0]=30.5.124. +nacos.inetutils.preferred-networks[0]=30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))),30.5.124.(25[0-5]|2[0-4]\\d|((1d{2})|([1-9]?\\d))) +``` diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-standalone.mdx b/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-standalone.mdx new file mode 100644 index 00000000000..8911e21bd89 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/deployment/deployment-standalone.mdx @@ -0,0 +1,88 @@ +--- +title: 单机模式部署 +keywords: [Nacos,部署,单机模式,单例模式] +description: Nacos单机模式部署手册,参考本文档可进行Nacos单机模式多种方式部署。 +sidebar: + order: 2 +--- +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Nacos单机模式 + +## 1. 发行版部署 + +### 1.1. 部署步骤 + + + + 在[快速开始](../../../quickstart/quick-start.mdx)中,我们使用了内置的数据库Derby,快速部署了Nacos的单机模式,可参考该文档进行使用Derby数据库的Nacos单机模式部署。 + + + 参考[快速开始](../../../quickstart/quick-start.mdx)中,进行Nacos的环境准备、发行版的下载等。 + + 同时在使用MySQL数据源部署Nacos单机模式时,需要自行准备MySQL数据库: + + - 1.安装数据库,版本要求:5.6.5+ + - 2.初始化mysql数据库,数据库初始化文件:[mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql) + + 然后修改`${nacos.home}/conf/application.properties`文件,增加支持MySQL数据源配置,添加MySQL数据源的url、用户名和密码。 + + ``` + spring.sql.init.platform=mysql + + db.num=1 + db.url.0=jdbc:mysql://${mysql_host}:${mysql_port}/${nacos_database}?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true + db.user=${mysql_user} + db.password=${mysql_password} + ``` + + 然后使用[快速开始-启动服务器](../../../quickstart/quick-start/#4启动服务器)中的操作,启动Nacos即可。 + + + +### 1.2. 高级配置 + +Nacos提供了丰富的可配置项,帮助您调整Nacos的性能、控制Nacos提供的功能能力,例如鉴权、监控、数据库、连接、日志等;详情请参考[系统参数](../system-configurations.md)。 + +## 2. Docker部署 + +### 2.1. 部署步骤 + + + + 在[快速开始 Docker](../../../quickstart/quick-start-docker.mdx)中,我们通过`Docker`使用了内置的数据库Derby,快速部署了Nacos的单机模式,可参考该文档进行使用Derby数据库的Nacos单机模式部署。 + + + 执行 docker-compose 命令启动Nacos + + ```powershell + docker-compose -f example/standalone-mysql-8.yaml up + ``` + + **如果希望使用MySQL5.7** + + ```powershell + docker-compose -f example/standalone-mysql-5.7.yaml up + ``` + + + +### 2.2. 高级配置 + +如果你有很多自定义配置的需求,可以通过指定[系统参数-镜像环境变量](../system-configurations/#2-镜像环境变量)的方式进行配置,例如需要开启鉴权时: + +```powershell +docker run --name nacos-standalone-auth -e MODE=standalone -e NACOS_AUTH_ENABLE=true -e NACOS_AUTH_TOKEN=${customToken} -e NACOS_AUTH_IDENTITY_KEY=${customKey} NACOS_AUTH_IDENTITY_VALUE=${customValue} -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +同时,可以通过对application.properties文件进行挂卷定义的方式,将更多复杂的自定义配置导入Nacos容器中,强烈建议在生产环境中使用方式,例如: + +```powershell +docker run --name nacos-standalone -e MODE=standalone -v /path/application.properties:/home/nacos/conf/application.properties -p 8848:8848 -d -p 9848:9848 nacos/nacos-server:latest +``` + +如果仍然无法满足自定义需求,可以基于nacos-docker项目中的`Dockerfile`自行构建镜像。 + +## 3. Kubernetes部署 + +Kubernetes暂时不提供单例模式的部署,推荐使用集群模式部署。 \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/monitor.md b/src/content/docs/v3.0/zh-cn/manual/admin/monitor.md new file mode 100644 index 00000000000..e4b5903beed --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/monitor.md @@ -0,0 +1,170 @@ +--- +title: 监控手册 +keywords: [Nacos,监控] +description: Nacos 如何开启和部署监控 +sidebar: + order: 8 +--- + +# 监控手册 + +## 1. Nacos 集群监控 + +Nacos 支持通过暴露metrics数据接入第三方监控系统监控Nacos运行状态,目前支持prometheus、elastic search和influxdb,下面结合prometheus和grafana为例,介绍如何监控Nacos。与elastic search和influxdb结合可自己查找相关资料。 + +### 1.1. 开启Nacos集群metrics数据暴露 + +按照[部署文档](./deployment/deployment-overview.mdx)搭建好Nacos集群后,需要在Nacos集群的每个节点上修改如下参数: + +`application.properties`文件,暴露metrics数据 + +``` +management.endpoints.web.exposure.include=prometheus +``` + +重启后,访问`{ip}:8848/nacos/actuator/prometheus`,即可访问到Nacos集群的metrics数据。 + +### 1.2. 搭建prometheus采集Nacos metrics数据 + +请参考[FIRST STEPS WITH PROMETHEUS](https://prometheus.io/docs/introduction/first_steps/)部署prometheus + +其中,需要将prometheus的配置文件`prometheus.yml`关于采集目标相关的配置,修改为如下内容 +``` + metrics_path: '/nacos/actuator/prometheus' + static_configs: + - targets: ['{nacos.ip1}:8848','{nacos.ip2}:8848','{nacos.ip3}:8848',...] +``` + +搭建并启动完成prometheus后,即可通过浏览器访问`http://{prometheus_ip}:9090/graph`可以看到prometheus的采集数据,在搜索栏搜索nacos_monitor可以搜索到Nacos数据说明采集数据成功。 + +### 1.3. 搭建grafana图形化展示Nacos metrics数据 + +参考[Install Grafana](https://grafana.com/docs/grafana/latest/setup-grafana/installation/) 部署grafana。 + +之后在浏览器中访问 `http://{grafana_ip}:3000` + +然后参考[Configure Prometheus](https://grafana.com/docs/grafana/latest/datasources/prometheus/configure-prometheus-data-source/),将刚才部署的prometheus作为Grafana的数据源。 + +随后参考[Import dashboards](https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/import-dashboards/)导入Nacos grafana监控[模版](https://github.com/nacos-group/nacos-template/blob/master/nacos-grafana-upper-2.4.json) + +导入后可看见Nacos监控模版,Nacos监控主要分为三个模块: + +1. nacos overview: 展示Nacos集群当前的概览信息,如节点个数,服务数,服务提供者数、配置数、连接数,CPU使用率等。 +![nacos overview](/img/doc/manual/admin/monitor-overview.jpg) + +2. nacos core monitor: 展示Nacos集群核心的监控指标,如服务提供者数,配置数,ops,rt等,并能够查看一定时间内的变化趋势。 +![nacos core monitor](/img/doc/manual/admin/monitor-core-monitor.jpg) + +3. nacos basic monitor: 展示Nacos集群基础的监控指标,如CPU使用率,内存使用率,线程池使用情况等,并能够查看一定时间内的变化趋势。 +![nacos basic monitor](/img/doc/manual/admin/monitor-basic-monitor.jpg) + +### 1.4. 配置Nacos集群告警 + +#### 1.4.1. 配置Grafana告警 + +可以参考[Configure Grafana-managed alert rules](https://grafana.com/docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/),配置自定义的Nacos相关告警。 + +也可以通过大盘中的对应指标内容,快速配置告警: + +1. 选择需要配置告警的指标,例如`cpu usage`; +2. 在指标的右上角,点击`Menu`(展示为竖直的3个`.`),选择`编辑(Edit)`; + ![nacos grafana edit](/img/doc/manual/admin/monitor-fast-alert-edit.jpg) +3. 在Panel页面中,选择`Alert`,点击`New alert rule`,配置告警规则。 + ![nacos grafana alert](/img/doc/manual/admin/monitor-fast-alert-new.jpg) +4. 之后同样参考[Configure Grafana-managed alert rules](https://grafana.com/docs/grafana-cloud/alerting-and-irm/alerting/alerting-rules/create-grafana-managed-rule/),配置自定义的Nacos相关告警。 + +#### 1.4.2. 配置Grafana告警通知 + +参考[Configure contact points](https://grafana.com/docs/grafana-cloud/alerting-and-irm/alerting/configure-notifications/manage-contact-points/) 来配置Grafana告警时的通知方式。例如配置邮件通知、钉钉WebHook通知等。 + +### 1.5. Nacos 指标含义 + +#### 1.5.1. Nacos 系统基础资源指标 + +指标|含义 +---|--- +system_cpu_usage|CPU使用率 +system_load_average_1m|load +jvm_memory_used_bytes|JVM内存使用字节,包含各种内存区 +jvm_memory_max_bytes|JVM内存最大字节,包含各种内存区 +jvm_gc_pause_seconds_count|gc次数,包含各种gc +jvm_gc_pause_seconds_sum|gc耗时,包含各种gc +jvm_threads_daemon|线程数 + +#### 1.5.2. Nacos 集群应用指标 + +指标|含义 +---|--- +http_server_requests_seconds_count|http请求次数,包括多种(url,方法,code) +http_server_requests_seconds_sum|http请求总耗时,包括多种(url,方法,code) +grpc_server_requests_seconds_count|Nacos grpc请求次数,包括多种(requestClass,code) +grpc_server_requests_seconds_sum|Nacos grpc请求总耗时,包括多种(requestClass,code) +nacos_timer_seconds_sum|Nacos config水平通知耗时 +nacos_timer_seconds_count|Nacos config水平通知次数 +grpc_server_executor{name='maximumPoolSize'}|Nacos grpc服务器线程池的最大线程数 +grpc_server_executor{name='corePoolSize'}|Nacos grpc服务器线程池的核心线程数 +grpc_server_executor{name='taskCount'}|Nacos grpc服务器线程池的任务数量 +grpc_server_executor{name='poolSize'}|Nacos grpc服务器线程池当前线程数量 +grpc_server_executor{name='activeCount'}|Nacos grpc服务器线程池当前活跃的线程数量 +grpc_server_executor{name='completedTaskCount'}|Nacos grpc服务器线程池完成的任务数量 +grpc_server_executor{name='inQueueTaskCount'}|Nacos grpc服务器线程池在任务队列中的任务数量 +nacos_monitor{name='longPolling'}|Nacos config长连接数 +nacos_monitor{name='configCount'}|Nacos config配置个数 +nacos_monitor{name='dumpTask'}|Nacos config配置落盘任务堆积数 +nacos_monitor{name='notifyTask'}|Nacos config配置水平通知任务堆积数 +nacos_monitor{name='getConfig'}|Nacos config读配置统计数 +nacos_monitor{name='publish'}|Nacos config写配置统计数 +nacos_monitor{name='ipCount'}|Nacos naming ip个数 +nacos_monitor{name='serviceCount'}|Nacos naming域名个数 +nacos_monitor{name='failedPush'}|Nacos naming推送失败数 +nacos_monitor{name='avgPushCost'}|Nacos naming平均推送耗时(ms) +nacos_monitor{name='leaderStatus'}|Nacos naming角色状态 +nacos_monitor{name='maxPushCost'}|Nacos naming最大推送耗时(ms) +nacos_monitor{name='mysqlhealthCheck'}|Nacos naming mysql健康检查次数 +nacos_monitor{name='httpHealthCheck'}|Nacos naming http健康检查次数 +nacos_monitor{name='tcpHealthCheck'}|Nacos naming tcp健康检查次数 +nacos_monitor{name='longConnection'}|Nacos基于模块划分的连接数量 +nacos_naming_subscriber{version='v1/v2'}|Nacos naming服务订阅者数量,v1/v2表示订阅者的客户端版本 +nacos_config_subscriber{version='v1/v2'}|Nacos config配置监听者数量,v1/v2表示订阅者的客户端版本 +nacos_naming_publisher{version='v1/v2'}|Nacos naming服务提供者数量,v1/v2表示订阅者的客户端版本 + +## 2. Nacos-Sync监控 + +Nacos-Sync 同样支持了第三方监控系统,能通过metrics数据观察Nacos-Sync服务的运行状态,提升了Nacos-Sync的在生产环境的监控能力。 +整体的监控体系的搭建参考上文的[Nacos 集群监控](#1-nacos-集群监控),进行prometheus和grafana的部署即可。 + +### 2.1. grafana监控Nacos-Sync +和Nacos监控一样,Nacos-Sync也提供了监控模版,导入监控[模版](https://github.com/nacos-group/nacos-template/blob/master/nacos-sync-grafana) + +Nacos-Sync监控同样也分为三个模块: +- nacos-sync monitor展示核心监控项 + ![monitor](https://img.alicdn.com/tfs/TB1GeNWKmzqK1RjSZFHXXb3CpXa-2834-1588.png) +- nacos-sync detail和alert展示监控曲线和告警 + ![detail](https://img.alicdn.com/tfs/TB1kP8UKbvpK1RjSZPiXXbmwXXa-2834-1570.png) + +### 2.2. Nacos-Sync 指标含义 + +Nacos-Sync的metrics分为jvm层和应用层 + +#### 2.2.1. jvm 指标 + +指标|含义 +---|--- +system_cpu_usage|CPU使用率 +system_load_average_1m|load +jvm_memory_used_bytes|内存使用字节,包含各种内存区 +jvm_memory_max_bytes|内存最大字节,包含各种内存区 +jvm_gc_pause_seconds_count|gc次数,包含各种gc +jvm_gc_pause_seconds_sum|gc耗时,包含各种gc +jvm_threads_daemon|线程数 + +#### 2.2.2. 应用层 指标 + +指标|含义 +---|--- +nacosSync_task_size|同步任务数 +nacosSync_cluster_size|集群数 +nacosSync_add_task_rt|同步任务执行耗时 +nacosSync_delete_task_rt|删除任务耗时 +nacosSync_dispatcher_task|从数据库中分发任务 +nacosSync_sync_task_error|所有同步执行时的异常 \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/system-configurations.md b/src/content/docs/v3.0/zh-cn/manual/admin/system-configurations.md new file mode 100644 index 00000000000..4a9627cd90f --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/system-configurations.md @@ -0,0 +1,250 @@ +--- +title: 系统参数 +keywords: [Nacos,系统参数] +description: Nacos系统参数介绍 +sidebar: + order: 4 +--- + +# Nacos 系统参数介绍 + +## 1. Nacos Server + +对于Server端来说,一般是设置在`{nacos.home}/conf/application.properties`里,如果参数名后标注了(-D)的,则表示是 JVM 的参数,需要在`{nacos.home}/bin/startup.sh`里进行相应的设置。例如像设置 nacos.home 的值,可以在`{nacos.home}/bin/startup.sh`进行如下设置: + +``` +JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}" +``` + +若没有标注(-D)的参数,则同时可以在`{nacos.home}/conf/application.properties`里和JVM参数中配置,如果同时配置了JVM参数和`{nacos.home}/conf/application.properties`,那么JVM参数的优先级更高。 + +### 1.1. 全局参数 + +#### 1.1.1. 基础参数 + +|参数名 | 含义 | 可选值 | 默认值| +|------|--------------------------------------------------------------------------------------------|-----------|-----------------| +|nacos.home(-D)| Nacos的根目录 | 目录路径| Nacos安装的目录 | +|nacos.standalone(-D)| 是否在单机模式 | true/false | false | +|nacos.functionMode(-D)| 启动模式,支持只启动某一个模块,不设置时所有模块都会启动 | config/naming/空 | 空 | +|nacos.server.ip(-D)| Nacos服务端的IP,优先级比`nacos.inetutils.ip-address`更高,如果配置了该参数,则`nacos.inetutils.ip-address`不再生效 | 本机IP | null | +|nacos.inetutils.prefer-hostname-over-ip| 节点优先使用hostname作为本机ip,若为`true`时,`cluster.conf`里是否应该填`hostname` | true/false| false | +|nacos.inetutils.ip-address | 本机IP,该参数设置后,将会使用这个IP去`cluster.conf`里进行匹配,请确保这个IP的值在`cluster.conf`里是存在的 | 本机IP| null | +|nacos.core.sys.basic.processors| 指定服务端的处理器个数,用于部分虚拟化场景,防止读取CPU个数时读取到错误的值,导致线程数过多或过少 |正整数| CPU个数| +|nacos.core.monitor.topn.enabled| Nacos Server topN 监控统计能力开关 | true/false | true | +|nacos.core.monitor.topn.count| Nacos Server topN 监控统计 top的个数,如如配置为10,表示top10的配置和服务 | 正整数 | 10 | +|nacos.core.snowflake.worker-id| Nacos Server 的snowflake workerId | 正整数 | -1 | +|nacos.core.param.check.enabled| Nacos Server 参数校验能力开关,开启后将会校验请求时的参数是否符合规范,不符合将被拦截,详情查看 [参数校验](../user/parameters-check.md) | true/false | true | +|server.port| Nacos Server 的端口 | 正整数 | 8848 | +|server.servlet.context-path| Nacos Server 的Servlet上下文路径 | 正则表达式 | /nacos | +|spring.config.additional-location| Nacos Server 的额外配置文件路径,除`{nacos.home}/conf/application.properties`外,用户可以添加额外的配置文件 | 文件路径,多个文件路径用逗号分隔 | null | + +#### 1.1.2. 数据库 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|spring.sql.init.platform|Nacos Server 使用的数据库类型 | mysql/空,指定为空时会根据`nacos.standalone`判断使用derby数据库还是mysql数据库;在使用[数据源插件](../../plugin/datasource-plugin.md)时,可以指定为插件对应的数据库值,比如oracle或postgresql | null | +|~~spring.datasource.platform~~|Nacos Server 使用的数据库类型,即将被废弃,请使用`spring.sql.init.platform`代替 | mysql/空 | null | +|db.num| 数据库数目 | 正整数 | 0 | +|db.url.0| 第一个数据库的URL | 字符串 | 空 | +|db.url.1| 第二个数据库的URL,当db.num=2时生效 | 字符串 | 空 | +|db.user| 数据库连接的用户名 | 字符串 | 空 | +|db.password| 数据库连接的密码 | 字符串 | 空 | +|db.pool.config.xxx| 数据库连接池参数,使用的是hikari连接池,参数与hikari连接池相同,如`db.pool.config.connectionTimeout`或`db.pool.config.maximumPoolSize`|字符串|同hikariCp对应默认配置| + +当前数据库配置支持多数据源。通过`db.num`来指定数据源个数,`db.url.index`为对应的数据库的链接。`db.user`以及`db.password`没有设置`index`时,所有的链接都以`db.user`和`db.password`用作认证。如果不同数据源的用户名称或者用户密码不一样时,可以通过符号`,`来进行切割,或者指定`db.user.index`,`db.user.password`来设置对应数据库链接的用户或者密码。需要注意的是,当`db.user`和`db.password`没有指定下标时,因为当前机制会根据`,`进行切割。所以当用户名或者密码存在`,`时,会把`,`切割后前面的值当成最后的值进行认证,会导致认证失败。 + +Nacos从1.3版本开始使用HikariCP连接池,但在1.4.1版本前,连接池配置由系统默认值定义,无法自定义配置。在1.4.1后,提供了一个方法能够配置HikariCP连接池。 +`db.pool.config`为配置前缀,`xxx`为实际的hikariCP配置,如`db.pool.config.connectionTimeout`或`db.pool.config.maximumPoolSize`等。更多hikariCP的配置请查看[HikariCP](https://github.com/brettwooldridge/HikariCP) +需要注意的是,url,user,password会由`db.url.n`,`db.user`,`db.password`覆盖,driverClassName则是默认的MySQL8 driver(该版本mysql driver支持mysql5.x) + +#### 1.1.2. Remoting + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.remote.server.grpc.sdk.max-inbound-message-size|Nacos Server gRPC 能接收的单次最大客户端请求大小,单位byte| 正整数 |10 * 1024 * 1024| +|nacos.remote.server.grpc.cluster.max-inbound-message-size|Nacos Server gRPC 能接收的单次最大集群间请求大小,单位byte| 正整数 |10 * 1024 * 1024| +|nacos.metric.grpc.server.executor.enabled| Nacos Server gRPC线程池 监控能力开关 | true/false | true | +|nacos.metric.grpc.server.executor.interval| Nacos Server gRPC线程池的间隔时间,单位为毫秒 | 正整数 | 15000 | +|nacos.metric.grpc.server.connection.enabled| Nacos Server gRPC长连接 监控能力开关 | true/false | true | +|nacos.metric.grpc.server.connection.interval| Nacos Server gRPC长连接的间隔时间,单位为**秒** | 正整数 | 15 | +|remote.executor.times.of.processors(-D)| 服务端,处理请求的线程池大小的倍数, 例如配置为2,表示线程池大小为2 * CPU | 正整数 | 16 | +|remote.executor.queue.size(-D)|服务端,处理请求的线程池队列大小 | 正整数 | 16384 | + +#### 1.1.3. 集群列表 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.member.list|Nacos Server 地址列表,在`cluster.conf`不存在时生效 | `ip1:port1,ip2:port2` | null | +|nacos.member-change-event.queue.size| Nacos Server 集群节点变更事件队列的大小,当集群节点通过`cluster.conf`或地址服务器变更时,会将变更事件放入该队列,该队列会异步通知Server中的一些机制,比如Distro/Raft协议。 | 正整数 | 128 | +|nacos.core.member.lookup.type| Nacos Server 集群节点的发现方式,支持配置文件`cluster.conf`和地址服务器模式| file/address-server | file| +|nacos.core.address-server.retry|当`nacos.core.member.lookup.type`指定为`address-server`时生效,请求地址服务器的重试次数,超过重试次数后不再尝试从地址服务器获取NacosServer的集群列表| 正整数 | 5 | +|address.server.domain|当`nacos.core.member.lookup.type`指定为`address-server`时生效,地址服务器的域名 | 域名 | jmenv.tbsite.net| +|address.server.port|当`nacos.core.member.lookup.type`指定为`address-server`时生效,地址服务器的端口 | 0~65535 | 8080 | +|address.server.url|当`nacos.core.member.lookup.type`指定为`address-server`时生效,地址服务器的url | 字符串 | /serverlist | + +### 1.2. Distro 协议 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.protocol.distro.data.sync.delayMs|Distro协议同步数据的延迟时间,同一份数据处于延迟时间内多次变更时,会被合并为一次同步,单位为毫秒 | 正整数 | 1000 | +|nacos.core.protocol.distro.data.sync.timeoutMs|Distro协议同步数据的超时时间,同步到目标节点时超过该时间,则会认为同步失败,进行延迟后重试,单位为毫秒 | 正整数 | 3000 | +|nacos.core.protocol.distro.data.sync.retryDelayMs|Distro协议同步数据的重试间隔,当数据同步到目标节点失败时,进行该值时间的延迟后再重试,避免同步重试风暴,单位为毫秒 | 正整数 | 3000 | +|nacos.core.protocol.distro.data.verify.intervalMs|Distro协议数据验证的间隔,已经同步过的数据,会定期进行数据有效性验证,验证失败会重新发起该数据的同步,单位为毫秒 | 正整数 | 5000 | +|nacos.core.protocol.distro.data.verify.timeoutMs|Distro协议数据验证的超时时间,单位为毫秒 | 正整数 | 3000 | +|nacos.core.protocol.distro.data.load.retryDelayMs|Distro协议快照数据加载的重试间隔,在节点刚启动时生效,单位为毫秒 | 正整数 | 30000 | +|nacos.core.protocol.distro.data.load.timeoutMs|Distro协议快照数据加载的超时时间,超过该时间未读取到其他节点的快照数据,则认为加载快照失败,单位为毫秒 | 正整数 | 30000 | + +### 1.3 Raft 协议 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.protocol.raft.election_timeout_ms|Raft协议选举超时时间,单位ms|正整数| 5000| +|nacos.core.protocol.raft.snapshot_interval_secs|Raft协议快照写入间隔时间,单位s|正整数| 3600| +|nacos.core.protocol.raft.core_thread_num|Raft协议的核心线程数,用于处理Raft同步的请求线程数|正整数| 8| +|nacos.core.protocol.raft.cli_service_thread_num|Raft协议的核心线程数,用于发起Raft同步数据的请求线程数|正整数| 4| +|nacos.core.protocol.raft.rpc_request_timeout_ms|Raft协议请求的超时时间,单位ms|正整数| 5000| +|nacos.core.protocol.raft.max_byte_count_per_rpc|Raft协议单次请求最大字节数|正整数| 128 * 1024| +|nacos.core.protocol.raft.max_entries_size|Raft协议单个日志的最大个数|正整数| 1024| +|nacos.core.protocol.raft.max_body_size|Raft协议发送日志的最大 body 大小|正整数| 512 * 1024| +|nacos.core.protocol.raft.max_append_buffer_size|Raft协议日志存储缓冲区最大大小|正整数| 256 * 1024| +|nacos.core.protocol.raft.max_election_delay_ms|Raft协议选举的最大随机间隔,选举定时器间隔会在指定时间之外随机的最大范围|正整数| 1000| +|nacos.core.protocol.raft.strict_mode|从`2.4.2`版本开始支持,Raft的启动校验是否采用严格模式,开启后,当raft无法选举时,引擎的readiness接口将返回500 | true/false | false | + +### 1.4. Naming模块 + +|参数名 | 含义 | 可选值 | 默认值 | +|------|-------------------------------------------------------------------------------------------------|-----------|---------------| +|nacos.naming.expireInstance| 是否自动摘除临时实例 | true/false | true | +|nacos.naming.data.warmup| 从`2.4.2`版本开始支持,是否在启动时校验数据是否预热,开启可能造成Server的readiness接口返回500,需要等待预热完成,启动时间变长 | true/false | false | +|nacos.naming.clean.empty-service.interval| Naming模块的空服务清理间隔,单位毫秒 | 正整数 | 60 * 1000 | +|nacos.naming.clean.empty-service.expired-time| Naming模块的空服务过期时间,过期的空服务会被清理,单位毫秒 | 正整数 | 60 * 1000 | +|nacos.naming.clean.expired-metadata.interval| Naming模块的元数据清理间隔,单位毫秒 | 正整数 | 5000 | +|nacos.naming.clean.expired-metadata.expired-time| Naming模块的元数据过期时间,过期的元数据会被清理,单位毫秒 | 正整数 | 60 * 1000 | +|nacos.naming.client.expired.time| 临时Client对应数据的过期时间,当Distro协议停止对该Client的数据进行续约且时间超过该值时,该Client数据将被删除,主要应对Nacos Server之间断网的场景,单位毫秒 | 正整数 | 3 * 60 * 1000 | +|nacos.naming.push.pushTaskDelay| 服务数据推送的延迟时间,同一个人服务处于延迟时间内多次变更时,会被合并为一次推送,单位为毫秒 | 正整数 | 500 | +|nacos.naming.push.pushTaskTimeout| 服务数据推送的超时时间,超过该时间未收到客户端的确认,将延迟后重试,单位为毫秒 | 正整数 | 5000 | +|nacos.naming.push.pushTaskRetryDelay| 服务数据推送失败后的重试间隔时间,单位为毫秒 | 正整数 | 1000 | + +### 1.5. Config模块 + +|参数名 |含义 | 可选值 | 默认值 | +|------|------|-----------|-------| +|nacos.config.push.maxRetryTime|配置变更数据推送的延迟时间,同一个人配置处于延迟时间内多次变更时,会被合并为一次推送,单位为毫秒| 正整数 | 50 | +|nacos.config.retention.days|Nacos配置中心配置变更历史保留天数,超过该时间的配置变更历史会被删除| 正整数 | 30 | +|nacos.config.search.max_capacity|Nacos配置中心,根据配置内容查找配置功能的最大队列个数,由于基于内容的检索十分消耗性能,因此对该功能的并发进行限制,最大不可超过32| 0~32 | 4 | +|nacos.config.search.max_thread|根据配置内容查找配置功能的最大线程数,最大并发数,最大不可超过16| 0~16 | 2 | +|nacos.config.search.wait_timeout|根据配置内容查找配置功能的等待超时时间,超过等待时间的查找任务会直接失败丢弃,单位毫秒| 正整数 | 8000 | +|nacos.config.derby.ops.enabled|当使用derby数据库作为存储时,是否开启derby的相关运维接口| true/false | false | +|nacos.persistence.sql.derby.limit.enabled|当使用derby数据库作为存储时,限制derby数据库可执行的SQL范围为DML和部分DDL,从`2.4.1`版本开始支持| true/false | true | + +### 1.6. CMDB模块 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.cmdb.loadDataAtStart| 是否打开CMDB | true/false | false | +|nacos.cmdb.dumpTaskInterval| 全量dump的间隔,单位为秒 | 正整数 | 3600 | +|nacos.cmdb.eventTaskInterval| 变更事件的拉取间隔,单位为秒 | 正整数 | 10 | +|nacos.cmdb.labelTaskInterval| 标签集合的拉取间隔,单位为秒 | 正整数 | 300 | + +### 1.7. Istio模块 + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.extension.naming.istio.enabled| 是否加载istio模块| true/false | false | +|nacos.istio.mcp.server.enabled| 是否开启Istio MCP协议 | true/false | false | +|nacos.istio.mcp.server.port | Istio MCP协议监听端口 | 正整数 | 18848 | + +### 1.8. 插件 + +#### 1.8.1. 鉴权插件 + +关于如何开发鉴权插件,请参考[鉴权插件](../../plugin/auth-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.auth.enabled|Nacos是否开启鉴权|true/false|false| +|nacos.core.auth.system.type|Nacos鉴权插件的类型|nacos/ldap/自定义插件类型|nacos| +|nacos.core.auth.server.identity.key|Nacos Server节点身份信息的key,用户Server节点之间通信的识别,当开启鉴权时为必填项|字符串|null| +|nacos.core.auth.server.identity.value|Nacos Server节点身份信息的value,用户Server节点之间通信的识别,当开启鉴权时为必填项|字符串|null| +|nacos.core.auth.enable.userAgentAuthWhite|Nacos Server使用UserAgent来进行Server节点之间通信的识别,在1.4.1版本后仅作为升级时的兼容,开启后会存在安全问题,后续版本将移除该参数|true/false|false| + +同时对于Nacos默认鉴权插件的使用及更多默认鉴权插件的配置项,请参考[权限校验](../../guide/user/auth/#相关参数) + +#### 1.8.2. 数据源插件 + +其他和数据库相关的开发,请参考[全局参数-数据库](#112-数据库) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|spring.sql.init.platform|Nacos Server 使用的数据库类型 | mysql/空,指定为空时会根据`nacos.standalone`判断使用derby数据库还是mysql数据库;在使用[数据源插件](../../plugin/datasource-plugin.md)时,可以指定为插件对应的数据库值,比如oracle或postgresql | null | +|nacos.plugin.datasource.log.enabled|Nacos Server 是否开启SQL日志打印,开启后会打印每一次执行的SQL,方便进行插件开发时的问题排查,但是较为损耗性能,日常状态建议关闭 | true/false | false | + +#### 1.8.3. 环境变量插件 + +关于如何开发环境变量插件,请参考[环境变量插件](../../plugin/custom-environment-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.custom.environment.enabled|Nacos Server 是否开启环境变量插件 | true/false | false | + +#### 1.8.4. 反脆弱插件 + +反脆弱插件的开发,请参考[反脆弱插件](../../plugin/control-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.plugin.control.manager.type|Nacos反脆弱插件的类型 | nacos/其他自定义插件类型 | null | +|nacos.plugin.control.rule.external.storage|Nacos反脆弱插件,反脆弱规则外部存储类型,需要自行实现 | 字符串 | null | +|nacos.plugin.control.rule.local.basedir|Nacos反脆弱插件,反脆弱规则本地存储目录 | 文件路径 | ${nacos.home}/data/ | + +#### 1.8.5. 配置变更插件 + +反脆弱插件的开发,请参考[配置变更插件](../../plugin/config-change-plugin.md) + +|参数名 |含义 | 可选值 | 默认值| +|------|------|-----------|-----------------| +|nacos.core.config.plugin.${configChangePluginName}.enabled=true|Nacos Server 是否开启配置变更插件 | true/false | false | +|nacos.core.config.plugin.${configChangePluginName}.${propertyKey}=${propertyValue}|配置变更插件的配置项 | 插件自定义 | 插件自定义 | + +## 2. 镜像环境变量 + +属性配置列表 + +| 属性名称 | 描述 | 选项 | +|-----------------------------------------|-------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| MODE | 系统启动方式: 集群/单机,对应`nacos.standalone` | cluster/standalone 默认 **cluster** | +| NACOS_SERVERS | 集群地址,对应`nacos.member.list` | p1:port1空格ip2:port2 空格ip3:port3 | +| PREFER_HOST_MODE | 支持IP还是域名模式,对应`nacos.inetutils.prefer-hostname-over-ip` | hostname/ip 默认**IP** | +| NACOS_SERVER_PORT | Nacos 运行端口,对应`server.port` | 默认**8848** | +| NACOS_SERVER_IP | 多网卡模式下可以指定IP,对应`nacos.server.ip` | | +| SPRING_DATASOURCE_PLATFORM | 单机模式下支持MYSQL数据库,对应`spring.sql.init.platform` | mysql / 空 默认:空 | +| MYSQL_SERVICE_HOST | 数据库 连接地址 | | +| MYSQL_SERVICE_PORT | 数据库端口 | 默认 : **3306** | +| MYSQL_SERVICE_DB_NAME | 数据库库名 | | +| MYSQL_SERVICE_USER | 数据库用户名 | | +| MYSQL_SERVICE_PASSWORD | 数据库用户密码 | | +| MYSQL_SERVICE_DB_PARAM | 数据库连接参数 | 默认:**characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false** | +| MYSQL_DATABASE_NUM | 数据库个数 | 默认:**1** | +| JVM_XMS | -Xms | 默认 :1g | +| JVM_XMX | -Xmx | 默认 :1g | +| JVM_XMN | -Xmn | 512m | +| JVM_MS | - XX:MetaspaceSize | 默认 :128m | +| JVM_MMS | -XX:MaxMetaspaceSize | 默认 :320m | +| NACOS_DEBUG | 是否开启远程DEBUG | y/n 默认 :n | +| TOMCAT_ACCESSLOG_ENABLED | `server.tomcat.accesslog.enabled` | 默认 :false | +| NACOS_AUTH_SYSTEM_TYPE | 权限系统类型选择,目前只支持nacos类型 | 默认 :nacos | +| NACOS_AUTH_ENABLE | 是否开启权限系统,对应`nacos.core.auth.enabled` | 默认 :false | +| NACOS_AUTH_TOKEN_EXPIRE_SECONDS | token 失效时间 | 默认 :18000 | +| NACOS_AUTH_TOKEN | token | `注意:该环境变量的默认值在Nacos 2.2.1版本中已移除,开启鉴权时需要指定` | +| NACOS_AUTH_CACHE_ENABLE | 权限缓存开关 ,开启后权限缓存的更新默认有15秒的延迟 | 默认 : false | +| MEMBER_LIST | 通过环境变量的方式设置集群地址 | 例子:192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 | +| EMBEDDED_STORAGE | 是否开启集群嵌入式存储模式 | `embedded` 默认 : none | +| NACOS_AUTH_CACHE_ENABLE | nacos.core.auth.caching.enabled | default : false | +| NACOS_AUTH_USER_AGENT_AUTH_WHITE_ENABLE | nacos.core.auth.enable.userAgentAuthWhite | default : false | +| NACOS_AUTH_IDENTITY_KEY | nacos.core.auth.server.identity.key | `注意:该环境变量的默认值在Nacos 2.2.1版本中已移除,开启鉴权时需要指定` | +| NACOS_AUTH_IDENTITY_VALUE | nacos.core.auth.server.identity.value | `注意:该环境变量的默认值在Nacos 2.2.1版本中已移除,开启鉴权时需要指定` | +| NACOS_SECURITY_IGNORE_URLS | nacos.security.ignore.urls | default : `/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**` | +| DB_POOL_CONNECTION_TIMEOUT | 数据库连接池超时时间,单位为毫秒 | 默认 : **30000** | +| NACOS_CONSOLE_UI_ENABLED | nacos.console.ui.enabled | default : `true` | +| NACOS_CORE_PARAM_CHECK_ENABLED | nacos.core.param.check.enabled | default : `true` | \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/admin/upgrading.mdx b/src/content/docs/v3.0/zh-cn/manual/admin/upgrading.mdx new file mode 100644 index 00000000000..15fc6504c12 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/admin/upgrading.mdx @@ -0,0 +1,135 @@ +--- +title: 升级手册 +keywords: [Nacos,升级] +description: Nacos版本升级指南,本文档将介绍Nacos升级的一般步骤和方法。 +sidebar: + order: 7 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# 升级手册 + +## 1. 升级版本兼容性 + +当前文档所对应的版本为2.4.X,所有Nacos版本可升级到该版本的兼容性如下: + +| Nacos版本 | 是否支持升级到当前版本 | 备注 | +|----------|--------------------|------| +| 0.X ~ 1.1.X | 不支持 | 0.X ~ 1.1.X 版本需要先升级到1.2以上的,然后参考[Nacos2.0升级文档](https://nacos.io/docs/v2/upgrading/200-upgrading/) 先升级到2.0或2.1版本后再进行升级 | +| 1.2.X ~ 1.4.X | 不支持 | 2.4.X版本不支持从 1.2.X ~ 1.4.X 之间的版本直接升级,请参考[Nacos2.0升级文档](https://nacos.io/docs/v2/upgrading/200-upgrading/) 先升级到2.0或2.1版本后再进行升级 | +| 2.0.X | 支持 | 2.4.X版本支持从 2.0.X 升级到 2.4.X, 但数据库表结构有发生变化,请升级前对比[mysql-schema.sql](https://github.com/alibaba/nacos/blob/master/distribution/conf/mysql-schema.sql)文件,并应用新的表结构后进行升级 | +| 2.1.X ~ 2.3.X | 支持 | 2.4.X版本支持从 2.1.X ~ 2.3.X 升级到 2.4.X | + +## 2. 升级步骤 + +### 2.1. 发行版升级 + +#### 2.1.1 确认表结构 + +先对比部署的旧版本Nacos版本的mysql-schema.sql文件和将升级版本的mysql-schema.sql文件,确认表结构是否有变化。 + +若文件中表结构存在变化,请先进行数据库变更,例如: + +```SQL +ALTER TABLE `config_info` ADD COLUMN `encrypted_data_key` varchar(255) NOT NULL COMMENT '密钥', +``` + +#### 2.1.2. 下载新版本 + + + + 进入Nacos官网[版本下载页面](/download/nacos-server/),选择 [稳定版本](/download/nacos-server/#稳定版本), 然后点击`二进制包下载`列中的`${nacos.version}.zip`进行下载。 + + > 注意:有时大量用户同时进行下载时,可能会遇到下载限流失败的情况,若出现下载限流失败,请稍等后重试,或采用`从 Github 下载方式`。 + + + 进入Nacos Github 的 [最新稳定版本](https://github.com/alibaba/nacos/releases) ,选择需要下载的Nacos版本,在`Assets`中点击下载 `nacos-server-$version.zip` 包。 + + + +#### 2.1.3. 替换二进制jar包 + +解压新下载的发行版压缩包 + +```bash + unzip nacos-server-$version.zip + # 或者 tar -xvf nacos-server-$version.tar.gz +``` + +然后 找到`nacos/targer/nacos-server.jar` 将该jar包替换到旧的发行版中,例如: + +```bash +cp nacos/target/nacos-server.jar ${old.nacos.home}/target/ +``` + +#### 2.1.4. 修改配置文件(可选) + +对比部署的旧版本的配置文件和新版本的配置文件`conf/application.properties`,确认是否有新增或修改的配置项,将这些配置项添加到旧的配置文件中,例如: + +```bash +diff nacos/conf/application.properties ${old.nacos.home}/conf/application.properties +``` + +#### 2.1.5. 修改启动参数(可选) + +对比部署的旧版本的启动脚本和新版本的启动脚本`bin/startup.sh`或`bin/startup.cmd`,确认是否有新增或修改的配置项,将这些配置项添加到旧的启动脚本件中,例如: + +```bash +diff nacos/bin/startup.sh ${old.nacos.home}/bin/startup.sh +``` + +#### 2.1.6. 重启Nacos Server + +均替换完成后,重启Nacos Server: + +``` +# 以集群模式为例 +## Linux +${nacos.home}/bin/shutdown.sh +${nacos.home}/bin/startup.sh + +## Windows +${nacos.home}/bin/shutdown.cmd +${nacos.home}/bin/startup.cmd +``` +### 2.2. Docker/Kubernetes升级 + +#### 2.2.1 确认表结构 + +先对比部署的旧版本Nacos版本的mysql-schema.sql文件和将升级版本的mysql-schema.sql文件,确认表结构是否有变化。 + +若文件中表结构存在变化,请先进行数据库变更,例如: + +```SQL +ALTER TABLE `config_info` ADD COLUMN `encrypted_data_key` varchar(255) NOT NULL COMMENT '密钥', +``` + +#### 2.2.2. 确认环境变量 + +可以对比当前部署版本的环境变量与[系统参数-镜像环境变量](./system-configurations/#2-镜像环境变量)中的环境变量是否有变化,如有,需要修改Docker的环境变量文件或Kubernetes的regcenter中的环境变量。 + +#### 2.2.3. 修改镜像版本 + + + + 首先修改docker compose所对应的文件中的Nacos版本,例如[Nacos Docker](https://github.com/nacos-group/nacos-docker)项目下的standalone-mysql-8.yaml中的 + ```yaml + services: + nacos: + image: + nacos/nacos-server:${target_version} + ``` + 然后执行如下命令进行升级 + ```bash + docker-compose pull + docker-compose up -d --remove-orphans + ``` + + + 通过kubectl set image 命令更新版本,如: + ```bash + kubectl set image deployment/nacos-deployment ${container_name}=nacos/nacos-server:${target_version} + ``` + + diff --git a/src/content/docs/v3.0/zh-cn/manual/test/nacos-e2e.md b/src/content/docs/v3.0/zh-cn/manual/test/nacos-e2e.md new file mode 100644 index 00000000000..5f758b80f54 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/test/nacos-e2e.md @@ -0,0 +1,163 @@ +--- +title: Nacos E2E 测试 +keywords: [Nacos,测试,E2E] +description: Nacos 的端到端测试项目,用于验证 Nacos 在不同场景下的功能正确性。 +sidebar: + order: 2 +--- + +# Nacos E2E 测试 + +[`nacos-group/nacos-e2e`](https://github.com/nacos-group/nacos-e2e) 项目是 Nacos 的端到端测试项目,用于验证 Nacos 在不同场景下的功能正确性。如果你想要向这个项目中新增测试用例,可以按照以下步骤进行: + +## 1. 准备工作 + ++ **Fork 仓库**:首先你需要在 GitHub 上 Fork `nacos-group/nacos-e2e` 仓库到你自己的账号下。 ++ **克隆仓库**:将 Fork 后的仓库克隆到本地。 + +```shell +git clone https://github.com/your-username/nacos-e2e.git +cd nacos-e2e +``` + ++**设置上游仓库**:为了能够拉取官方最新的更新,建议设置上游仓库。 + +```shell +git remote add upstream https://github.com/nacos-group/nacos-e2e.git +``` + +## 2. 理解现有结构 + +查看项目的目录结构,目前主要按客户端测试语言类型和CI部署Nacos的依赖区分,其中java按客户端版本范围和鉴权用例区分 + ++ cicd,build目录包含docker相关文件、构建脚本和Nacos部署的配置文件;helm目录包含Kubernetes 的包管理器,它简化了在 Kubernetes 集群上部署应用程序的过程 ++ cpp,用C++语言编写的用例,测试C++客户端 ++ csharp,用csharp语言编写的用例,测试csharp客户端 ++ golang,用golang语言编写的用例,测试go客户端 ++ java,用java语言编写的用例,测试java客户端,其中4个目录分别验证: + - auth,部署单例模式的Nacos,验证开启鉴权后的场景 + - nacos-1X,1.X客户端在1.3版本以上及2.0版本以下时的测试场景 + - nacos-1X-1.3down,1.X客户端在1.3及以下版本时的测试场景 + - nacos-2X,2.X客户在2.0及以上版本时的测试场景 ++ nodejs,用nodejs语言编写的用例,测试nodejs客户端 ++ python,用python语言编写的用例,测试python客户端 + +## 3. 新增测试用例 + +下面以在`java/nacos-2X`目录下新增一个配置中心相关用例举例: + ++ **选择合适的包**:根据你要测试的客户端语言和客户端版本,选择或创建一个合适的包(例如 `/nacos-e2e/java/nacos-2X/src/test/java/com/alibaba/nacos/config`)。 ++ **编写测试类**:在选定的包下创建一个新的测试类,例如 `YourFeatureTest.java`。 + - 每个目录下会继承一个测试基类,如此配置继承ConfigBase基类,此基类进行配置测试用到的常量声明以及在@BeforeAll @AfterAll 中定义在所有测试方法执行之前或之后运行的方法 + - ConfigBase基类继承BaseOperate,定义一些通用的操作,比如配置中心和服务中心初始化连接时用到的Properties + - BaseOperate继承ResourceInit,定义初始化时用到的变量(从环境变量或者本地文件中获取到的变量)、所有用例启动前服务端的数据写入,初始化一些必要数据(比如命名空间),获取当前服务端客户端的数据(服务端版本、客户端版本等) ++ **配置依赖**:如果需要引入新的依赖,请修改 `pom.xml` 或其他构建文件。 ++ **测试类规范**: + - @BeforeEach 注解的方法中对每个用例数据进行随机初始化和配置 + - @AfterEach 注解的方法中对每个用例写入数据进行删除等操作清理 + - @Test 注解的方法中进行用例逻辑编写,在@DisplayName 中描述当前用例测试的场景 + +示例代码: + +```java +package com.alibaba.nacos.config; + +import com.alibaba.nacos.util.RandomUtils; +import org.junit.jupiter.api.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class YourFeatureTest extends ConfigBase{ + private static final Logger log = LoggerFactory.getLogger(ConfigNormalTest.class); + private String dataId; + private String group; + private String content; + private Map cleanConfigMap = new HashMap<>(); + + @BeforeEach + public void setUp() throws Exception{ + dataId = "config.test." + RandomUtils.getStringWithCharacter(10); + group = DEFAULT_GROUP; + } + + @AfterEach + public void tearDown() throws Exception { + Iterator> iterator = cleanConfigMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + Boolean result = config.removeConfig(entry.getKey(), entry.getValue()); + if (result) { + iterator.remove(); + } + } + } + + @Test + @DisplayName("Publish normal character and sleep 3000ms to get config, expect get correct config content.") + public void testPublishAndGetConfig_normalCharacter() throws Exception{ + content = RandomUtils.getStringWithCharacter(20); + boolean result = config.publishConfig(dataId, group, content); + log.info("publishConfig dataId:{}, group:{}, result:{}", dataId, group, result); + Assertions.assertTrue(result, "publishConfig check fail"); + cleanConfigMap.put(dataId, group); + Thread.sleep(TIME_OUT); + + String value = config.getConfig(dataId, group, TIME_OUT); + log.info("getConfig dataId:{}, group:{}, value:{}", dataId, group, value); + Assertions.assertEquals(content, value, "getConfig value is not expect one"); + } + +} + +``` + +## 4. 运行测试 + +java目录下的用例,修改每个module的daily.conf文件中serverList变量为指定服务端IP:Port,或者在执行命令中传入,亦或本地编译器中执行 + ++ **用例执行命令:** + +```shell +mvn clean test -B -Dtest=YourFeatureTest -DserverList=127.0.0.1 +``` + +其他语言下的用例执行,可以参考每种语言的module下bin目录的run.sh文件,在语言环境具备情况下,安装必要的包和设置环境变量后执行,亦或安装相应的编译器本地执行 + +## 5. 提交更改 + ++ **提交代码**:将你的更改提交到本地仓库。 + +```shell +git add . +git commit -m "Add new e2e test for your feature" +``` + ++ **推送到远程仓库**:将更改推送到你的 GitHub 仓库。 + +```shell +git push origin main +``` + +## 6. 创建 Pull Request + ++ **创建 PR**:回到 GitHub 页面,点击 "New pull request" 按钮,选择你的分支与 `nacos-group/nacos-e2e` 的主分支进行比较,然后创建 Pull Request。 ++ **等待审查**:项目维护者会对你的 PR 进行审查,可能会提出一些修改意见。根据反馈进行相应的调整。 + +## 7. 合并 PR + ++ **合并**:一旦 PR 被批准,它会被合并到主分支中。 + +通过以上步骤,你就可以成功地向 `nacos-group/nacos-e2e` 项目中新增测试用例了。 + +## 8. Nacos主仓库CI能力 + +1. 成功地向 `nacos-group/nacos-e2e` 项目中新增测试用例后,用例最终在`alibaba/nacos` 仓库的actions中触发运行。 +2. CI通过使用阿里云ASK能力为每次PUSH和PR提供一个独立的Nacos环境进行E2E测试,部署使用nacos-group/nacos-e2e仓库中的helm chart(nacos-e2e/cicd),镜像为当次提交的代码。 +3. 由于安全性考虑,将PR和PUSH动作的CI分离为两个流水线。PR触发E2E测试后回写评论至PR中,可以关联跳转到对应的测试报告。 +4. 测试报告可视化,每次E2E测试的报告在CI当次执行页面上可视化展示,无需查看日志,方便排查。 +5. 支持兼容性测试,并发执行多OS和JDK版本的测试。 + diff --git a/src/content/docs/v3.0/zh-cn/manual/test/nacos-unit-test.md b/src/content/docs/v3.0/zh-cn/manual/test/nacos-unit-test.md new file mode 100644 index 00000000000..f2452040c93 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/test/nacos-unit-test.md @@ -0,0 +1,139 @@ +--- +title: Nacos 单元测试 +keywords: [Nacos,测试,单元测试] +description: 描述Nacos 单元测试的规范,以及运行单元测试的步骤,帮助开发者快速了解并贡献测试代码。 +sidebar: + order: 1 +--- + +# Nacos 单元测试 + +Nacos 单元测试主要指的是Nacos 核心仓库[`alibaba/nacos`](https://github.com/alibaba/nacos)中的最基本的单元测试,用于测试各个最小测试单元(一般是Class)的逻辑正确性及改动正确性。 + +该文档将介绍Nacos 单元测试的规范,以及运行单元测试的步骤,帮助开发者快速了解在修改功能代码后如何撰写并贡献对应的单元测试代码。 + +## 1. Nacos 单元测试准备工作 + +- 可参考[Nacos 贡献流程](../../contribution/contributing-flow.md)的前3个步骤,进行仓库克隆、和本地Git的设置。 +- 安装JDK1.8+ 以上版本 +- 安装Maven3.2.5 以上版本。 + +## 2. 新增Nacos 单元测试用例 + +Nacos 的单元测试基于`junit5`,`mockito`两个测试框架,详细使用方法请参考[JUnit5](https://junit.org/junit5/docs/current/user-guide/)和[Mockito](https://site.mockito.org/#features)。 + +新建的单元测试用例类需尽量与所测试类的模块、包名一致;若已经存在单元测试类,请尽量在原有测试类中添加测试用例,不要新建测试类,部分情况为避免用例互相干扰,可以新增单元测试类,但需要在新类名中进行说明和表达。 + +新增的单元测试类需要遵循如下规范: + +### 2.1. Nacos 单元测试规范 + +1. 单元测试类必须以`Test`结尾,前面的类名必须与测试类名一致;如:类名为`NacosNamingService`,则单元测试类名为`NacosNamingServiceTest`。 +2. 单元测试的用例必须以`test`开头,后接需要测试的方法名,以驼峰命名法命名测试用例,如:`testRegisterInstance`。 +3. 单元测试用例中必须存在断言。 +4. 单元测试中不允许依赖外部资源,如:数据库, 若有需要,请使用Mockito进行模拟。 +5. 单元测试的用例若遇到所测试方法存在多个分支和测试用例的情况,请创建多个用例,且通过`For`,`With`,`Without`等描述测试用例场景,如:`DefaultParamCheckerTest`的`testCheckParamInfoForNamespaceShowName`、`testCheckParamInfoForNamespaceId`等。 +6. 单元测试用例中如果存在设置 static 变量,请使用 `@AfterAll` 或 `@AfterEach` 注解在用例结束后进行回滚,避免影响其他测试用例。 +7. 单元测试用例中所需要的依赖尽量使用`@Mock`注解进行模拟,需要用到的前置设置和初始化,尽量在`@BeforeAll` 和 `@BeforeEach`注解中完成,避免大量重复初始化代码。 +8. 单元测试若需要测试异步方法,尽量使用mock的线程池进行,若无法使用mock线程池,可以尝试控制线程池的启动时间和间隔周期,避免单元测试运行时间过长。 +9. 单元测试代码同样需要遵循[Nacos代码规范](https://github.com/alibaba/nacos/blob/develop/style/codeStyle.md)。 + +### 2.2. Nacos 单元测试示例 + +```java + +public class YourClass { + + public boolean demo(String serviceName) { + if (null == serviceName) { + return false; + } + return doDemo(serviceName); + } + + private boolean doDemo(String serviceName) { + if (!ServiceManager.containService(serviceName)) { + SecurityManager.newService(serviceName); + return true; + } else { + throw new Exception(); + } + } +} + +public class YourClassTest { + + private YourClass yourClass; + + private String serviceName; + + @BeforeEach + public void setUp() throws Exception{ + // do initial for each test cases; + yourClass = new YourClass(); + serviceName = "test"; + } + + @AfterEach + public void tearDown() throws Exception { + // do clean data. + ServiceManager.cleanService(serviceName); + } + + @Test + public void testDemoForNullService() { + assertFalse(yourClass.demo(null)); + } + + @Test + public void testDemoForExistService() { + ServiceManager.newService(serviceName); + assertThrows(Exception.class, () -> yourClass.demo(serviceName)); + } + + @Test + public void testDemoForNotExistService() { + assertFalse(ServiceManager.containService(serviceName)); + assertTrue(yourClass.demo(serviceName)); + assertTrue(ServiceManager.containService(serviceName)); + assertNotNull(ServiceManager.getService(serviceName)); + } +} +``` + +## 3. 运行Nacos 单元测试 + +在Nacos 仓库根目录下执行`mvn -Prelease-nacos clean test -DtrimStackTrace=false -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn`命令即可运行单元测试。 + +测试运行完成后,终端中会显示测试结果,同时在各自模块下,会通过`jacoco`插件生成当前模块的测试覆盖率文件,如`api/target/site/jacoco/jacoco.xml`。 + +## 4. 提交Nacos 单元测试 + ++ **提交代码**:将你的更改提交到本地仓库。 + +```shell +git add . +git commit -m "Add new unit test for class/packages/modules" +``` + ++ **推送到远程仓库**:将更改推送到你的 GitHub 仓库。 + +```shell +git push origin $your_branch_name +``` + +## 5. 创建 Pull Request + ++ **创建 PR**:回到 GitHub 页面,点击 "New pull request" 按钮,选择你的分支与 `alibaba/nacos` 的主分支(一般为`develop`)进行比较,然后创建 Pull Request。 ++ **等待审查**:PR中会通过`Github Action`对您提交的单元测试及其他修改进行一系列的检查工作,同时项目维护者会对你的 PR 进行审查,可能会提出一些修改意见。根据运行结果和反馈进行相应的调整。 ++ **合并**:一旦 PR 被批准,它会被合并到主分支中。 + +通过以上步骤,你就可以成功地向 `alibaba/nacos` 项目中新增测试用例了。 + +## 6. 测试报告 + +在PR中以及PR被合并后的`Github Action`中,会对运行之后的单元测试报告,提交到[CodeCov](https://app.codecov.io/gh/alibaba/nacos/)上进行统计和分析,可以查看到测试覆盖率情况。 + +同时在PR中, 会有`Coverage`的简略报告,评论在PR中,原则上在测试覆盖率出现降低时,社区维护人员可能会选择不进行PR的合并,并在评论中提醒您补充单元测试。 + +> 当前部分模块的测试覆盖率还有待提高,欢迎社区同学贡献测试代码。 diff --git a/src/content/docs/v3.0/zh-cn/manual/user/addressing.mdx b/src/content/docs/v3.0/zh-cn/manual/user/addressing.mdx new file mode 100644 index 00000000000..422755e1962 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/addressing.mdx @@ -0,0 +1,240 @@ +--- +title: Nacos 客户端寻址 +keywords: [寻地, 客户端, server-addr, endpoint, 地址服务器, 服务端地址配置] +description: 本文介绍了各语言SDK如何配置寻找Nacos服务器的地址逻辑,以及多种不同的寻址方式的优先级。 +sidebar: + order: 10 +--- + +import {Tabs, TabItem} from '@astrojs/starlight/components'; + +# Nacos 客户端寻址 + +在使用Nacos时, Nacos客户端需要配置Nacos服务器的地址,以确保客户端能连接到Nacos服务器,从而实现服务发现和注册等功能。 + +本文档将介绍Nacos客户端的各种寻址逻辑,以及多种不同的寻址方式的优先级;对于部分支持拓展寻址的编程语言的客户端,本文档还会介绍如何实现自定义的寻址逻辑。 + +## 1. 客户端寻址基本逻辑 + +Nacos的所有客户端在寻址Nacos服务端时,虽然具体的实现有所差异,但基本都遵循如下逻辑: + +![寻址基本逻辑](/img/doc/manual/user/client-addressing.svg) + +1. 首先,Nacos客户端会根据初始化时,接受来自应用的配置,如`Properties`等信息; +2. 在Nacos客户端初始化过程中,会创建一个`ServerListManager(服务列表管理器)`,用于管理服务端的地址列表; +3. 根据传入的`Properties`配置信息,`ServerListManager`会选择一个对应的`ServerListProvider(服务列表提供者)`来实际获取服务端地址列表,默认情况下,Nacos客户端会提供基于`server-addr配置`的`PropertiesServerListProvider`,和`Endpoint 地址服务器`的`EndpointServerListProvider`; +4. 在`ServerListManager`完成`ServerListProvider`的选取、初始化以及从其中获取到服务端地址列表后,Nacos客户端才会创建用于通信的`GrpcClient`,并使用`ServerListManager`管理的服务端地址列表来创建连接,并请求对应的Nacos Server。 + +不同的`ServerListProvider`会导致客户端在寻找服务端地址列表时的行为有所不同,下文对不同`ServerListProvider`的使用条件、行为结果、优先级等特性进行说明: + +### 1.1. 基于配置的寻址逻辑 + +Nacos客户端默认提供了基于配置的`ServerListProvider`,即`PropertiesServerListProvider`,该`ServerListProvider`会根据`server-addr`配置,从配置中获取服务端地址列表,若配置中存在多个服务端地址,Nacos客户端将会使用`,`和`;`字符进行分割、并最终随机选择一个作为服务端地址。 + +若当前选择的服务端地址不可用,则Nacos客户端会按照轮询的方式,依次尝试使用其他服务端地址,当尝试所有的地址之后,会回到第一个服务端地址,继续轮询,直至找到可用的服务端地址或客户端中止。 + +这种寻址方式的最大优点是,Nacos客户端的配置非常简单,只需要配置`server-addr`即可,无需配置额外的服务端地址列表配置;同时也无需部署额外的组件;但是,这种寻址方式相对不灵活,无法动态的修改服务端地址列表,需要修改时只能重新创建一个新的Nacos客户端使用。 + +使用示例: + +> 注意:不同编程语言的配置方式可能存在差异,请根据实际情况修改。 + + + + + ```java + try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + ConfigService configService = NacosFactory.createConfigService(properties); + NamingService configService = NacosFactory.createNamingService(properties); + } catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ``` + + + + ```Go + sc := []constant.ServerConfig{ + { + IpAddr: `{serverAddr1}`, + Port: 8848, + }, + { + IpAddr: "{serverAddr2}", + Port: 8848, + } + } + + //create ClientConfig + cc := *constant.NewClientConfig() + + // create client + namingClient, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + configClient, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + ``` + + + 待补充 + + + +### 1.2. 基于Endpoint地址服务器的寻址逻辑 + +Nacos客户端还提供了基于地址服务器的寻址方式,即`EndpointServerListProvider`,该`ServerListProvider`会根据`endpoint`配置,从`endpoint`所指向的地址服务器中获取服务端地址列表; + +地址服务器可以是任意一个提供对应HTTP接口的服务端,例如一个`nginx`,`tomcat服务端`,`Nacos的address模块等`等等;对应接口需要按照格式返回Nacos服务端的地址列表,格式为每行一个地址,例如: + +``` +127.0.0.1:8848 +1.1.1.1 +``` + +若返回的地址没有端口,则默认端口为8848。 + +同样地,Nacos客户端会随机选择列表中的一个地址作为服务端地址,并使用该地址访问Nacos服务端;若该地址不可用,则Nacos客户端会按照轮询的方式,依次尝试使用其他服务端地址,当尝试所有的地址之后,会回到第一个服务端地址,继续轮询,直至找到可用的服务端地址或客户端中止。 + +该寻址方式的最大优点是可以动态的更新客户端所连接的服务端地址列表,无需重启客户端;但是,这种寻址方式需要额外部署一个地址服务器以提供对应的更新和获取地址列表的接口、同时可能需要配置的内容较多,适合Nacos部署规模较大的场景。 + +使用示例: + +> 注意:不同编程语言的配置方式可能存在差异,请根据实际情况修改。 + + + + + 更多地址服务器相关配置,请参考[Java SDK 配置参数-通用参数](./java-sdk/properties/#21-通用参数)。 + + ```java + try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String endpoint = "{endpoint}"; + Properties properties = new Properties(); + properties.put("endpoint", endpoint); + + ConfigService configService = NacosFactory.createConfigService(properties); + NamingService configService = NacosFactory.createNamingService(properties); + } catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ``` + + + + ```Go + cc := constant.ClientConfig{ + Endpoint: "{endpoint}:8080", + } + + // a more graceful way to create config client + client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + }, + ) + ``` + + + 待补充 + + + +### 1.3. 不同寻址逻辑的触发优先级 + +当Nacos客户端启动时, 同时配置了不同的寻址逻辑,Nacos客户端会按照以下优先级进行服务端地址的获取: + +1. 优先使用`EndpointServerListProvider`,尝试读取`endpoint`相关配置,若存在,则使用`EndpointServerListProvider`获取服务端地址列表,`endpoint`相关配置默认又遵循如下优先级; +- 1.1. 其中环境变量`ALIBABA_ALIWARE_ENDPOINT_URL`的优先级最高,如果存在,则使用`ALIBABA_ALIWARE_ENDPOINT_URL`作为地址服务器地址; +- 1.2. 其次是初始化时传入的Properties中`endpoint`配置优先,如果存在,则使用`endpoint`所配置的地址作为地址服务器地址; +- 1.3. 再优先是JVM参数中配置的`endpoint`配置优先,如果存在,则使用JVM中的`endpoint`所配置的地址作为地址服务器地址; +- 1.4. 最后是环境变量中的`endpoint`,如果存在,使用环境变量中`endpoint`中配置的地址作为地址服务器地址; +2. 若以上关于`EndpointServerListProvider`的配置均不存在,则使用`PropertiesServerListProvider` +- 1.1. 优先使用初始化时传入的Properties中`serverAddr`配置,如果存在,则使用`serverAddr`配置的地址作为Nacos服务端地址列表; +- 1.2. 其次是JVM参数中配置的`serverAddr`配置,如果存在,则使用JVM中的`serverAddr`配置的地址作为Nacos服务端地址列表; +- 1.3. 最后是环境变量中的`serverAddr`,如果存在,使用环境变量中`serverAddr`中配置的地址作为Nacos服务端地址列表; + +> 以上配置中的配置优先级顺序为默认情况下的优先级顺序, 不同的编程语言客户端的优先级可能存在差异,请根据实际情况修改;另外,如果设置了[Java SDK 读取配置的优先级顺序](./java-sdk/properties/#13-如何使用),除了`ALIBABA_ALIWARE_ENDPOINT_URL`以外,其余的配置优先级顺序将会按照设置的优先级顺序进行读取。 + +## 2. 自定义的寻址方式 + +> 目前仅Java SDK支持自定义的寻址方式,其他语言的SDK暂不支持。 + +Nacos的Java SDK通过SPI,可以自定义ServerListProvider,实现自定义的寻址方式: + +### 2.1. 引入依赖 + +```xml + + + com.alibaba.nacos + nacos-client + 2.5.0 + +``` + +### 2.2. 实现自定义ServerListProvider + +实现接口`com.alibaba.nacos.client.address.ServerListProvider` + +ServerListProvider的相应接口如果: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|---| +|init|NacosClientProperties, NacosRestTemplate|void|初始化时会调用此接口注入Nacos的配置,HTTP客户端等,以便于您读取初始化时传入的参数,以及通过HTTP接口访问获取其他信息等| +|getServerList|无|List<String>|`ServerListManager`会调用此接口,获取服务端地址列表| +|getServerName|无|String|获取`ServerListProvider`所对应的服务端名称,用于标记地址列表所对应的服务端名称| +|getNamespace|无|String|获取`ServerListProvider`所归属的命名空间,也即此Nacos客户端所归属的命名空间| +|getContextPath|无|String|获取`ServerListProvider`所对应的服务端上下文路径,当Nacos服务端配置了上下文路径时,需要此上下文路径,以便于Nacos客户端访问服务端| +|getOrder|无|int|获取`ServerListProvider`的优先级,数字越大优先级越高,`EndpointServerListProvider`的优先级为`500`,`PropertiesServerListProvider`的优先级为499| +|match|NacosClientProperties|boolean|判断当前`ServerListProvider`是否匹配当前Nacos客户端的配置,如果匹配,则Nacos客户端会调用`init`对此`ServerListProvider`进行初始化| +|isFixed|无|boolean|判断当前`ServerListProvider`是否所提供的地址列表是否为固定的,比如`EndpointServerListProvider`是可变的,`PropertiesServerListProvider`是固定的| +|getAddressSource|无|String|获取`ServerListProvider`的地址列表来源地址,比如`EndpointServerListProvider`返回是`Endpoint`的地址,`PropertiesServerListProvider`是``(空字符串)| +|shutdown|无|void|关闭`ServerListProvider`,关闭时会调用此接口,以便于您关闭相关资源,比如关闭HTTP客户端等| + +### 2.3. 使用自定义ServerListProvider + +添加对应的SPI配置文件`META-INF/services/com.alibaba.nacos.client.address.ServerListProvider`到您的`resources`目录中。 + +比如内容为: + +``` +org.example.MyServerListProvider +``` + +随后在初始化时传入的Properties中配置能让自定义ServerListProvider匹配到的对应参数,比如代码为: + +```java +public class MyServerListProvider implements ServerListProvider { + + public int getOrder() { + return Integet.MAX_VALUE; + } + + public boolean match(NacosClientProperties nacosClientProperties) { + return Boolean.parseBoolean(nacosClientProperties.getProperty("example.server.list.provider")); + } +} +``` + +配置内容应该为: + +```properties +example.server.list.provider=true +``` diff --git a/src/content/docs/v3.0/zh-cn/manual/user/auth.mdx b/src/content/docs/v3.0/zh-cn/manual/user/auth.mdx new file mode 100644 index 00000000000..670963c893d --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/auth.mdx @@ -0,0 +1,116 @@ +--- +title: 配置鉴权 +keywords: [Authorization, SDK] +description: 本文介绍了各语言SDK如何配置身份信息,访问Nacos的默认鉴权系统 +sidebar: + order: 8 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +> 注意 +> - Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。 +> - Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。 +> - 如果运行在不可信的网络环境或者有强鉴权诉求,请参考官方简单实现做进行[自定义插件开发](../../plugin/auth-plugin.md)。 + +# 配置鉴权 + +Nacos提供了一个简单的默认鉴权实现,当服务端通过[运维手册-权限校验](../admin/auth.mdx)开启鉴权后,客户端、SDK以及openAPI都需要配置身份信息才可以访问Nacos。 本文档介绍如何为各语言客户端及openAPI配置身份信息。 + +## 1. 客户端如何配置鉴权信息 + + + + + 在构建“Properties”类时,需传入用户名和密码。 + ```java + properties.put("username","${username}"); + properties.put("password","${password}"); + ``` + + #### 示例代码 + ```java + try { + // Initialize the configuration service, and the console automatically obtains the following parameters through the sample code. + String serverAddr = "{serverAddr}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + // if need username and password to login + properties.put("username","${username}"); + properties.put("password","${password}"); + + ConfigService configService = NacosFactory.createConfigService(properties); + NamingService configService = NacosFactory.createNamingService(properties); + } catch (NacosException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + ``` + + + + 在构建“ClientConfig”类时,需传入用户名和密码。 + ```Go + cc := *constant.NewClientConfig( + constant.WithUsername("${username}"), + constant.WithPassword("${password}"), + ) + ``` + + #### 示例代码 + ```Go + sc := []constant.ServerConfig{ + *constant.NewServerConfig("${serverAddr}", 8848, constant.WithContextPath("/nacos")), + } + + //create ClientConfig + cc := *constant.NewClientConfig( + constant.WithUsername("${username}"), + constant.WithPassword("${password}"), + ) + + // create client + namingClient, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + + configClient, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + ServerConfigs: sc, + }, + ) + ``` + + + 待补充 + + + +## 2. OpenAPI如何配置鉴权信息 + +首先需要使用用户名和密码登陆nacos。 + +```powershell +curl -X POST '127.0.0.1:8848/nacos/v1/auth/login' -d 'username=nacos&password=nacos' +``` + +若用户名和密码正确,返回信息例如: + +``` +{"accessToken":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyOTE2Nn0.2TogGhhr11_vLEjqKko1HJHUJEmsPuCxkur-CfNojDo","tokenTtl":18000,"globalAdmin":true} +``` + +接下来进行配置信息或服务信息时,应当使用该accessToken鉴权,在url后添加参数`accessToken=$accessToken`,其中$accessToken为登录时返回的token信息,例如 + +```powershell +curl -X GET '127.0.0.1:8848/nacos/v2/cs/config?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&dataId=nacos.example.1&group=nacos_group' +``` + +```powershell +curl -X POST 'http://127.0.0.1:8848/nacos/v2/ns/instance?accessToken=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJuYWNvcyIsImV4cCI6MTYwNTYyMzkyM30.O-s2yWfDSUZ7Svd3Vs7jy9tsfDNHs1SuebJB4KlNY8Q&port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3' +``` diff --git a/src/content/docs/v3.0/zh-cn/manual/user/go-sdk/usage.md b/src/content/docs/v3.0/zh-cn/manual/user/go-sdk/usage.md new file mode 100644 index 00000000000..d8bd7f6130c --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/go-sdk/usage.md @@ -0,0 +1,388 @@ +--- +title: Go SDK 使用手册 +keywords: [Go,SDK,使用手册] +description: 本文档介绍了Nacos的Go SDK(nacos-sdk-go)的使用方式 +sidebar: + order: 5 +--- + +# Go SDK 使用手册 + + +## 使用限制 +Go>=v1.15 + +Nacos>2.x + +## 安装 +使用`go get`安装SDK: +```sh +$ go get -u github.com/nacos-group/nacos-sdk-go/v2 +``` +## 快速使用 +* 初始化客户端配置ClientConfig + +```go +constant.ClientConfig{ + TimeoutMs uint64 // 请求Nacos服务端的超时时间,默认是10000ms + NamespaceId string // Nacos的命名空间Id + Endpoint string // 当使用地址服务器时,需要该配置. https://help.aliyun.com/document_detail/130146.html + RegionId string // Nacos&KMS的regionId,用于配置中心的鉴权 + AccessKey string // Nacos&KMS的AccessKey,用于配置中心的鉴权 + SecretKey string // Nacos&KMS的SecretKey,用于配置中心的鉴权 + OpenKMS bool // 是否开启kms,默认不开启,kms可以参考文档 https://help.aliyun.com/product/28933.html + // 同时DataId必须以"cipher-"作为前缀才会启动加解密逻辑 + CacheDir string // 缓存service信息的目录,默认是当前运行目录 + UpdateThreadNum int // 监听service变化的并发数,默认20 + NotLoadCacheAtStart bool // 在启动的时候不读取缓存在CacheDir的service信息 + UpdateCacheWhenEmpty bool // 当service返回的实例列表为空时,不更新缓存,用于推空保护 + Username string // Nacos服务端的API鉴权Username + Password string // Nacos服务端的API鉴权Password + LogDir string // 日志存储路径 + RotateTime string // 日志轮转周期,比如:30m, 1h, 24h, 默认是24h + MaxAge int64 // 日志最大文件数,默认3 + LogLevel string // 日志默认级别,值必须是:debug,info,warn,error,默认值是info +} +``` + +* ServerConfig + +```go +constant.ServerConfig{ + ContextPath string // Nacos的ContextPath,默认/nacos,在2.0中不需要设置 + IpAddr string // Nacos的服务地址 + Port uint64 // Nacos的服务端口 + Scheme string // Nacos的服务地址前缀,默认http,在2.0中不需要设置 + GrpcPort uint64 // Nacos的 grpc 服务端口, 默认为 服务端口+1000, 不是必填 +} +``` + +Note:我们可以配置多个ServerConfig,客户端会对这些服务端做轮询请求 + +### Create client + +```go +// 创建clientConfig +clientConfig := constant.ClientConfig{ + NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", // 如果需要支持多namespace,我们可以创建多个client,它们有不同的NamespaceId。当namespace是public时,此处填空字符串。 + TimeoutMs: 5000, + NotLoadCacheAtStart: true, + LogDir: "/tmp/nacos/log", + CacheDir: "/tmp/nacos/cache", + LogLevel: "debug", +} + +// 创建clientConfig的另一种方式 +clientConfig := *constant.NewClientConfig( + constant.WithNamespaceId("e525eafa-f7d7-4029-83d9-008937f9d468"), //当namespace是public时,此处填空字符串。 + constant.WithTimeoutMs(5000), + constant.WithNotLoadCacheAtStart(true), + constant.WithLogDir("/tmp/nacos/log"), + constant.WithCacheDir("/tmp/nacos/cache"), + constant.WithLogLevel("debug"), +) + +// 至少一个ServerConfig +serverConfigs := []constant.ServerConfig{ + { + IpAddr: "console1.nacos.io", + ContextPath: "/nacos", + Port: 80, + Scheme: "http", + }, + { + IpAddr: "console2.nacos.io", + ContextPath: "/nacos", + Port: 80, + Scheme: "http", + }, +} + +// 创建serverConfig的另一种方式 +serverConfigs := []constant.ServerConfig{ + *constant.NewServerConfig( + "console1.nacos.io", + 80, + constant.WithScheme("http"), + constant.WithContextPath("/nacos"), + ), + *constant.NewServerConfig( + "console2.nacos.io", + 80, + constant.WithScheme("http"), + constant.WithContextPath("/nacos"), + ), +} + +// 创建服务发现客户端 +_, _ := clients.CreateNamingClient(map[string]interface{}{ + "serverConfigs": serverConfigs, + "clientConfig": clientConfig, +}) + +// 创建动态配置客户端 +_, _ := clients.CreateConfigClient(map[string]interface{}{ + "serverConfigs": serverConfigs, + "clientConfig": clientConfig, +}) + +// 创建服务发现客户端的另一种方式 (推荐) +namingClient, err := clients.NewNamingClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, +) + +// 创建动态配置客户端的另一种方式 (推荐) +configClient, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &clientConfig, + ServerConfigs: serverConfigs, + }, +) +``` + +### Create client for Nacos +https://help.aliyun.com/document_detail/130146.html + +```go +cc := constant.ClientConfig{ + Endpoint: "nacos.aliyun.com:8080", + NamespaceId: "e525eafa-f7d7-4029-83d9-008937f9d468", + RegionId: "cn-shanghai", + AccessKey: "LTAI4G8KxxxxxxxxxxxxxbwZLBr", + SecretKey: "n5jTL9YxxxxxxxxxxxxaxmPLZV9", + OpenKMS: true, + TimeoutMs: 5000, + LogLevel: "debug", +} + +// a more graceful way to create config client +client, err := clients.NewConfigClient( + vo.NacosClientParam{ + ClientConfig: &cc, + }, +) +``` + + +### 服务发现 + +* 注册实例:RegisterInstance + +```go + +success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{ + Ip: "10.0.0.11", + Port: 8848, + ServiceName: "demo.go", + Weight: 10, + Enable: true, + Healthy: true, + Ephemeral: true, + Metadata: map[string]string{"idc":"shanghai"}, + ClusterName: "cluster-a", // 默认值DEFAULT + GroupName: "group-a", // 默认值DEFAULT_GROUP +}) + +``` + +* 注销实例:DeregisterInstance + +```go + +success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{ + Ip: "10.0.0.11", + Port: 8848, + ServiceName: "demo.go", + Ephemeral: true, + Cluster: "cluster-a", // 默认值DEFAULT + GroupName: "group-a", // 默认值DEFAULT_GROUP +}) + +``` + +* 获取服务信息:GetService + +```go + +services, err := namingClient.GetService(vo.GetServiceParam{ + ServiceName: "demo.go", + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + GroupName: "group-a", // 默认值DEFAULT_GROUP +}) + +``` + +* 获取所有的实例列表:SelectAllInstances + +```go +// SelectAllInstance可以返回全部实例列表,包括healthy=false,enable=false,weight<=0 +instances, err := namingClient.SelectAllInstances(vo.SelectAllInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT +}) + +``` + +* 获取实例列表 :SelectInstances + +```go +// SelectInstances 只返回满足这些条件的实例列表:healthy=${HealthyOnly},enable=true 和weight>0 +instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + HealthyOnly: true, +}) + +``` + +* 获取一个健康的实例(加权随机轮询):SelectOneHealthyInstance + +```go +// SelectOneHealthyInstance将会按加权随机轮询的负载均衡策略返回一个健康的实例 +// 实例必须满足的条件:health=true,enable=true and weight>0 +instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT +}) + +``` + +* 监听服务变化:Subscribe + +```go + +// Subscribe key=serviceName+groupName+cluster +// 注意:我们可以在相同的key添加多个SubscribeCallback. +err := namingClient.Subscribe(vo.SubscribeParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + SubscribeCallback: func(services []model.Instance, err error) { + log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services)) + }, +}) + +``` + +* 取消服务监听:Unsubscribe + +```go + +err := namingClient.Unsubscribe(vo.SubscribeParam{ + ServiceName: "demo.go", + GroupName: "group-a", // 默认值DEFAULT_GROUP + Clusters: []string{"cluster-a"}, // 默认值DEFAULT + SubscribeCallback: func(services []model.Instance, err error) { + log.Printf("\n\n callback return services:%s \n\n", utils.ToJsonString(services)) + }, +}) + +``` + +* 获取服务名列表:GetAllServicesInfo +```go + +serviceInfos, err := namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{ + NameSpace: "0e83cc81-9d8c-4bb8-a28a-ff703187543f", + PageNo: 1, + PageSize: 10, + }), + +``` + +### 动态配置 + +* 发布配置:PublishConfig + +```go + +success, err := configClient.PublishConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group", + Content: "hello world!222222"}) + +``` + +* 删除配置:DeleteConfig + +```go + +success, err = configClient.DeleteConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group"}) + +``` + +* 获取配置:GetConfig + +```go + +content, err := configClient.GetConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group"}) + +``` + +* 监听配置变化:ListenConfig + +```go + +err := configClient.ListenConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group", + OnChange: func(namespace, group, dataId, data string) { + fmt.Println("group:" + group + ", dataId:" + dataId + ", data:" + data) + }, +}) + +``` +* 取消配置监听:CancelListenConfig + +```go + +err := configClient.CancelListenConfig(vo.ConfigParam{ + DataId: "dataId", + Group: "group", +}) + +``` + +* 搜索配置: SearchConfig +```go +configPage,err := configClient.SearchConfig(vo.SearchConfigParam{ + Search: "blur", + DataId: "", + Group: "", + PageNo: 1, + PageSize: 10, +}) +``` +## 例子 +我们能从示例中学习如何使用Nacos go客户端 +* [动态配置示例](./example/config) +* [服务发现示例](./example/service) + +## 文档 +Nacos open-api相关信息可以查看文档 [Nacos open-api wepsite](https://nacos.io/en-us/docs/open-api.html). + +Nacos产品了解可以查看 [Nacos website](https://nacos.io/en-us/docs/what-is-nacos.html). + +## 贡献代码 +我们非常欢迎大家为Nacos-sdk-go贡献代码. 贡献前请查看[CONTRIBUTING.md](./CONTRIBUTING.md) + +## 联系我们 +* 加入Nacos-sdk-go钉钉群(23191211). +* [Gitter](https://gitter.im/alibaba/nacos): Nacos即时聊天工具. +* [Twitter](https://twitter.com/nacos2): 在Twitter上关注Nacos的最新动态. +* [Weibo](https://weibo.com/u/6574374908): 在微博上关注Nacos的最新动态. +* [Nacos SegmentFault](https://segmentfault.com/t/nacos): SegmentFault可以获得最新的推送和帮助. +* Email Group: + * users-nacos@googlegroups.com: Nacos用户讨论组. + * dev-nacos@googlegroups.com: Nacos开发者讨论组 (APIs, feature design, etc). + * commits-nacos@googlegroups.com: Nacos commit提醒. \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/failover.md b/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/failover.md new file mode 100644 index 00000000000..5403d30fb18 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/failover.md @@ -0,0 +1,143 @@ +--- +title: Java SDK 容灾 +keywords: [容灾] +description: Java客户端容灾用户指南 +sidebar: + order: 4 +--- + +# Java SDK 容灾 + +我们可以在客户端开启本地容灾,用来应对Nacos服务端出现问题时,保证客户端的数据和接口稳定性。 + +这里有两个使用场景: + +1. 在Nacos服务端发布的时候,我们主动把容灾打开,这样客户端只使用本地容灾数据,Nacos服务的数据抖动或者数据错误都不会影响客户端,我们在Nacos服务端升级完成并且数据验证没问题后再关闭容灾; +2. 在Nacos运行期间,突然出现接口不可用或者数据异常,我们可以快速的开启容灾,让客户端使用容灾数据,减小服务受影响的窗口,等Nacos服务端恢复后再关闭容灾; + +具体方案可以参考:https://github.com/alibaba/nacos/issues/11053 + +## 1. 流程简介 + +image + +如上图所示,客户端的查询请求都会先经过FailoverReactor,如果FailoverReactor有数据,则直接使用,从而忽略掉Nacos Server返回的数据;如果FailoverReactor里面没有数据,则走正常流程,从ServiceInfoHolder里读取缓存; + +## 2. 磁盘容灾 + +FailoverReactor里的数据可以使用不同的数据源,默认的数据源为磁盘。 + +### 2.1. 磁盘容灾文件目录 + +默认的磁盘容灾文件目录为: + +``` +{user.home}/nacos/naming/{namespace}/failover +``` + +这个目录可以定制,如果设置了-D参数: + +``` +-DJM.SNAPSHOT.PATH=/mypath +``` + +则容灾磁盘文件目录变为: + +``` +/mypath/nacos/naming/{namespace}/failover +``` + +### 2.2. 磁盘容灾开关 + +容灾开关存放在磁盘容灾文件目录下的一个文件里,具体文件名为: + +``` +00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 +``` + +文件里存放一个数字0或者1,0代表关闭容灾,1代表打开容灾 + +### 2.3. 磁盘容灾数据 + +容灾的数据分成多个文件,都是存放在磁盘容灾文件目录下,每一个文件存储一个单独的服务的容灾数据,每个文件的文件名格式如下: + +``` +{group.name}%40%40{service.name} +``` + +里面的内容为客户端的ServiceInfo类的JSON序列化字符串,例如: + +``` +{ + "name":"DEFAULT_GROUP@@test.2", + "groupName":"DEFAULT_GROUP", + "clusters":"", + "cacheMillis":10000, + "hosts":[ + { + "instanceId":"1.1.2.1#8888#DEFAULT#DEFAULT_GROUP@@test.2", + "ip":"1.1.2.1", + "port":8888, + "weight":1, + "healthy":true, + "enabled":true, + "ephemeral":true, + "clusterName":"DEFAULT", + "serviceName":"DEFAULT_GROUP@@test.2", + "metadata":{ + "k1":"v1" + }, + "instanceHeartBeatInterval":5000, + "instanceHeartBeatTimeOut":15000, + "ipDeleteTimeout":30000 + } + ], + "lastRefTime":1689835375819, + "checksum":"", + "allIPs":false, + "reachProtectionThreshold":false, + "valid":true +} +``` + +## 3. 扩展容灾数据源 + +磁盘容灾不需要外部依赖,逻辑比较简单,但是管理起来不太方便。因此我们也支持使用SPI来扩展容灾数据源,使用磁盘以外的存储。以下是扩展的步骤。 + +### 3.1. 开发自己的容灾数据源类 + +编写一个类,实现接口com.alibaba.nacos.client.naming.backups.FailoverDataSource: + +``` +public class MyFailoverDataSource implements FailoverDataSource { + + @Override + public FailoverSwitch getSwitch() { + // TODO write your own implementation. + return null; + } + + @Override + public Map getFailoverData() { + // TODO write your own implementation. For naming module, the map + // should contain failover data with service name as key and ServiceInfo as value + return null; + } +} +``` + +### 3.2. 配置容灾数据源类 + +在资源目录下新建文件: + +``` +{resource.root}/META-INF/services/com.alibaba.nacos.client.naming.backups.FailoverDataSource +``` + +{resource.root}的一个例子是src/main/resources。 + +文件内容为: + +``` +your.package.MyFailoverDataSource +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/properties.md b/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/properties.md new file mode 100644 index 00000000000..10819823773 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/properties.md @@ -0,0 +1,223 @@ +--- +title: Java SDK 配置参数 +keywords: [ Java,SDK,配置参数 ] +description: 本文档介绍了Nacos的Java SDK(nacos-client)目前所支持的配置参数列表,以及对Nacos Java SDK读取配置参数的原理做简要介绍。 +sidebar: + order: 3 +--- + +# Java SDK 配置参数 + +## 1. Java SDK 读取参数配置 + +### 1.1. 介绍 + +Nacos Java SDK 通过 `NacosClientProperties`, 一个类似于 `Spring Environment`用来统一管理客户端的各种配置项。 + +### 1.2. 特点 + +- 统一管理 Properties、命令行参数、环境变量和默认值 +- 提供优先级搜索功能, 默认搜索顺序 `properties -> 命令行参数 -> 环境参数 -> 默认值`, 可通过调整优先级来调整搜索顺序, + 默认是 `properties` 优先 +- 配置隔离, 每个 `NacosClientProperties` 对象,除去全局性的配置互不影响. + +### 1.3. 如何使用 + +#### 1.3.1. 优先级 + +默认优先级是 `properties`, 可通过以下2种方式来调整: + +``` +- (命令行参数)-Dnacos.env.first=PROPERTIES|JVM|ENV +- (环境变量)NACOS_ENV_FIRST=PROPERTIES|JVM|ENV +``` + +默认情况相当于`-Dnacos.env.first=PROPERTIES`. + +以上2种方式都指定的情况下,客户端优先使用命令行参数的方式获取优先级参数,若是通过命令行参数的方式没有获取到优先级参数则使用环境变量的方式获取优先级参数.如果以上2种方式都未指定优先级参数默认优先级为`properties` + +默认优先级: +![default_order.png](/img/nacos_client_properties_default_order.png) + +优先级: PROPERTIES +![default_order.png](/img/nacos_client_properties_default_order.png) + +优先级: JVM +![jvm_order.png](/img/nacos_client_properties_jvm_order.png) + +优先级: ENV +![jvm_order.png](/img/nacos_client_properties_env_order.png) + +#### 1.3.2. 搜索 + +`NacosClientProperties` 会按照指定优先级进行搜索配置, 以默认优先级(`PROPERTIES`)为例, 如果要获取一个 key 为 +`key1`的值, 查找顺序如下: + +![search_order.png](/img/nacos_client_properties_search_order.png) + +`NacosClientProperties` 会按照上图顺序搜索,直到查询到为止. + +#### 1.3.3. 配置隔离 + +为了应对多注册中心,多配置中心的场景, `NacosClientProperties` 引入配置隔离的概念. 在 `NacosClientProperties` 中总共有4个取值源, +分别是: 用户自定义的properties、命令行参数、 环境变量和默认值, 其中 `命令行参数、 环境变量和默认值` +这3个是全局共享的无法做到隔离, 那么只剩下用户自定义的properties对象是可以进行隔离的, 每个 `NacosClientProperties` +对象中包含不同的 `Properties` 对象, 通过这种方法做到配置互不影响. + +> 注意: 全局共享的配置: 命令行参数、 环境参数和默认值 一旦初始化完毕,后续使用无法更改,使用 `setProperty` +> 方法,也无法修改. `setProperty` 只能修改`NacosClientProperties` 对象中包含的 `Properties` 对象中的值 + +#### 1.3.4. 配置派生 + +在配置隔离的概念之上又引入了配置派生的概念, 其目的是让配置能够继承.所有 `NacosClientProperties` +对象都是由 `NacosClientProperties.PROTOTYPE` 对象派生而来. 无法通过其他方式创建 `NacosClientProperties` 对象 + +```java +// global properties +NacosClientProperties.PROTOTYPE.setProperty("global-key1","global-value1"); + +// properties1 +NacosClientProperties properties1 = NacosClientProperties.PROTOTYPE.derive(); +properties1. + +setProperty("properties1-key1","properties1-value1"); + +// properties2 +NacosClientProperties properties2 = properties1.derive(); +properties2. + +setProperty("properties2-key1","properties2-value1"); +``` + +以上代码如下图所示: +![derive.png](/img/nacos_client_properties_derive.png) + +那么搜索会怎么搜索呢? 以默认优先级(PROPERTIES)为例: + +```java +// value == global-value1 +String value = properties2.getProperty("global-key1"); + +``` + +![derive_search.png](/img/nacos_client_properties_derive_search.png) + +#### 1.3.5. API + +| 方法名 | 入参内容 | 返回内容 | 描述 | +|---------------|-------------------------------|-----------------------|-------------------------------------------------------------------------------------| +| getProperty | key: String | String | 获取 key 对应的 value 值, 不存在返回 null | +| getProperty | key: String, default: String | String | 获取 key 对应的 value 值, 不存在返回默认值 | +| getBoolean | key: String | Boolean | 获取 key 对应的 Boolean 值, 不存在则返回 null | +| getBoolean | key: String, default: Boolean | Boolean | 获取 key 对应的 Boolean 值, 不存在返回默认值 | +| getInteger | key: String | Integer | 获取 key 对应的 Integer 值, 不存在返回 null | +| getInteger | key: String, default: Integer | Integer | 获取 key 对应的 Integer 值, 不存在返回默认值 | +| getLong | key: String | Long | 获取 key 对应的 Long 值, 不存在返回 null | +| getLong | key: String, default: Long | Long | 获取 key 对应的 Long 值, 不存在返回默认值 | +| setProperty | key: String, value: String | void | 设置 key-value 到 NacosClientProperties 对象中,已存的值会被覆盖 | +| addProperties | properties: Properties | void | 添加 Properties 到 NacosClientProperties 对象中,已存在到值会被覆盖 | +| containsKey | key: String | boolean | 判断是否包含指定 key 的值, 包含返回 true 否则 false | +| asProperties | void | Properties | 将 NacosClientProperties 对象转换为 Properties 对象 | +| derive | void | NacosClientProperties | 创建一个继承父 NacosClientProperties 所有配置的 NacosClientProperties 对象, 内部包含一个空 Properties | +| derive | Properties | NacosClientProperties | 创建一个继承父 NacosClientProperties 所有配置的 NacosClientProperties 对象, 内部包含指定的 Properties 对象 | + +## 2. Java SDK 配置参数列表 + +### 2.1. 通用参数 + +通用参数为初始化注册中心`NamingService`和配置中心`ConfigServie`时均生效的参数: + +| 参数名 | PropertyKeyConst的Key | 含义 | 可选值 | 默认值 | +|--------------------------------|-----------------------------------|--------------------------------------------------------------------------------------------------------------|---------------------------------------------------|--------------------------| +| serverAddr | SERVER_ADDR | Nacos Server的地址列表,即此JAVA SDK访问哪个Nacos Server | 任意域名或IP,多个地址通过英文逗号`,`分割,多个地址必须属于同一个Nacos Server集群 | 无 | +| contextPath | CONTEXT_PATH | Nacos Server OpenAPI 的 context path,对应Nacos Server 的`server.servlet.context-path` 参数 | 任意URL支持的path | nacos | +| endpoint | ENDPOINT | Nacos Server的地址获取接入点,Java SDK或通过该接入点查询Nacos Server的实际域名或IP地址 | 任意域名或IP | 无 | +| endpointPort | ENDPOINT_PORT | Nacos Server的地址获取接入点的端口,配合endpoint使用,即请求${endpoint}:${endpointPort}/nacos/serverlist | 0~65535 | 8080 | +| endpointContextPath | ENDPOINT_CONTEXT_PATH | Nacos Server的地址获取接入点的context path,配合endpoint使用,${endpoint}:${endpointPort}/${endpointContextPath}/serverlist | 任意URL支持的path | nacos | +| endPointClusterName | ENDPOINT_CLUSTER_NAME | Nacos Server在接入点中的集群名,配合endpoint使用,${endpoint}:${endpointPort}/${endpointContextPath}/${endPointClusterName} | 任意URL支持的path | serverlist | +| endpointQueryParams | ENDPOINT_QUERY_PARAMS | Nacos Server的地址获取接入点的请求参数,用于接入点服务扩展自定义逻辑,格式key=value | 任意URL参数,key=value | 无 | +| endpointRefreshIntervalSeconds | ENDPOINT_REFRESH_INTERVAL_SECONDS | Nacos Server定期从地址获取接入点重新获取地址列表的间隔时间,单位为秒 | 任意正整数 | 30 | +| namespace | NAMESPACE | 该 JAVA SDK 所归属的命名空间Id, 设置后该SDK只能访问该命名空间的资源(配置或服务) | 命名空间Id | 空字符串`` | +| username | USERNAME | 开启鉴权功能后,访问Nacos Server所使用的用户名 | 任意字符串 | 无 | +| password | PASSWORD | 开启鉴权功能后,访问Nacos Server所使用的用户名对应的密码 | 任意字符串 | 无 | +| accessKey | ACCESS_KEY | 使用阿里云RAM鉴权时需要使用的accessKey | 任意字符串 | 无 | +| secretKey | SECRET_KEY | 使用阿里云RAM鉴权时需要使用的secretKey | 任意字符串 | 无 | | +| ramRoleName | RAM_ROLE_NAME | 使用阿里云RAM鉴权时需要使用的ramRoleName | 任意字符串 | 无 | +| signatureRegionId | SIGNATURE_REGION_ID | 使用阿里云RAM鉴权时,需要使用的signatureRegionId | 任意字符串 | 无 | +| logAllProperties | LOG_ALL_PROPERTIES | 启动Java SDK时,是否打印全量参数,包含自定义properties、JVM和环境变量,主要用户调试和问题排查。 | boolean | false | +| ~~clusterName~~ | ~~CLUSTER_NAME~~ | 由于和服务实例的ClusterName名称相同,容易造成混淆,该参数已废弃,请使用`endPointClusterName` | 任意URL支持的path | serverlist | +| ~~isAdaptClusterNameUsage~~ | ~~IS_ADAPT_CLUSTER_NAME_USAGE~~ | 是否兼容通过`clusterName`的方式设置`endPointClusterName`,让升级兼容性更好 | boolean | false | +| ~~serverName~~ | ~~SERVER_NAME~~ | 该 JAVA SDK 的名称,目前仅在访问endpoint时使用,由于使用率低且命名不合理,将废弃 | 任意字符串 | 由serverAddr/endpoint自动拼接 | + +### 2.2. 配置中心相关参数 + +仅在初始化配置中心`ConfigServie`时生效: + +| 参数名 | PropertyKeyConst的Key | 含义 | 可选值 | 默认值 | +|----------------------------|--------------------------------|-------------------------------------------------------------------|------------|------------------------------------------------| +| clientWorkerMaxThreadCount | CLIENT_WORKER_MAX_THREAD_COUNT | 自动计算配置中心ConfigService进行配置监听时的最大线程池个数 | >=2 的int值 | CPU个数 | +| clientWorkerThreadCount | CLIENT_WORKER_THREAD_COUNT | 指定配置中心ConfigService进行配置监听时的线程池个数,优先级高于clientWorkerMaxThreadCount | >=2 的int值 | Max(2, Min(clientWorkerMaxThreadCount, CPU个数)) | +| enableRemoteSyncConfig | ENABLE_REMOTE_SYNC_CONFIG | 配置中心ConfigService进行配置监听时立刻对监听的配置进行和服务端的同步和通知,开启可能影响启动监听的速度 | boolean | false | +| configRequestTimeout | CONFIG_REQUEST_TIMEOUT | 指定配置中心ConfigService发起rpc请求超时时间, 默认不启用, 使用RpcClientConfig通用配置的超时时间 | >=0 的long值 | -1 | +| ~~configRetryTime~~ | ~~CONFIG_RETRY_TIME~~ | 旧版本配置中心使用长轮询重试间隔时间,已废弃 | 任意int | 2000 | +| ~~configLongPollTimeout~~ | ~~CONFIG_LONG_POLL_TIMEOUT~~ | 旧版本配置中心使用长轮询超时时间,已废弃 | 任意int | 30000 | +| ~~maxRetry~~ | ~~MAX_RETRY~~ | 旧版本配置中心使用的最大重试次数参数,已废弃 | 任意int | 3 | + +### 2.3. 注册中心相关参数 + +仅在初始化注册中心`NamingServie`时生效: + +| 参数名 | PropertyKeyConst的Key | 含义 | 可选值 | 默认值 | +|----------------------------------|--------------------------------------|-------------------------------------------------------------------|------------|-------------------------------------------------| +| namingLoadCacheAtStart | NAMING_LOAD_CACHE_AT_START | 注册中心NamingService在启动时读取本地磁盘缓存来初始化数据 | boolean | false | +| namingCacheRegistryDir | NAMING_CACHE_REGISTRY_DIR | 注册中心NamingService的本地磁盘缓存目录名拓展名,用于同一节点中区分多个NamingService实例 | 任意字符串 | 空字符串 | +| namingAsyncQuerySubscribeService | NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE | 注册中心NamingService开启异步查询订阅服务的功能,作为数据推送链路异常时的兜底辅助 | boolean | false | +| namingPollingMaxThreadCount | NAMING_POLLING_MAX_THREAD_COUNT | 自动计算注册中心NamingService异步查询订阅服务的最大线程个数 | >=1 的int值 | CPU个数 | +| namingPollingThreadCount | NAMING_POLLING_THREAD_COUNT | 指定注册中心NamingService异步查询订阅服务的线程个数,优先级高于namingPollingMaxThreadCount | >=1 的int值 | Max(2, Min(namingPollingMaxThreadCount, CPU个数)) | +| namingRequestDomainMaxRetryCount | NAMING_REQUEST_DOMAIN_RETRY_COUNT | 当初始化注册中心NamingService`serverAddr`仅有一个地址时,请求Nacos Server失败后的最大重试次数 | 任意int值 | 3 | +| namingPushEmptyProtection | NAMING_PUSH_EMPTY_PROTECTION | 注册中心NamingService开启推空保护功能,当订阅服务时发现服务地址列表为0时,忽略此地址列表 | boolean | false | +| redoDelayTime | REDO_DELAY_TIME | 注册中心NamingService与Nacos Server链接断开后,间隔多长时间检查并进行redo操作,单位毫秒 | 任意long值 | 3000 | +| redoDelayThreadCount | REDO_DELAY_THREAD_COUNT | 注册中心NamingService执行redo操作的线程数 | 任意int值 | 1 | +| namingRequestTimeout | NAMING_REQUEST_TIMEOUT | 注册中心NamingService发起rpc请求超时时间, 默认不启用, 使用RpcClientConfig通用配置的超时时间 | >=0 的long值 | -1 | +| ~~namingClientBeatThreadCount~~ | ~~NAMING_CLIENT_BEAT_THREAD_COUNT~~ | 注册中心NamingService旧版本使用的,用于发送所注册服务实例心跳的线程数,已废弃 | 任意int值 | 无 | + +### 2.4. 连接相关 + +Nacos Java SDK 连接Nacos Server时,可以设置一系列的参数,来提升针对网络抖动时的容错能力,这部分配置暂时**只能通过JVM参数( +-D)**进行设置,社区正在进行改造,支持其能够通过`NacosClientProperties`(即初始化Java SDK时传入的Properties)进行配置,尽请期待。 + +| 参数名 | 含义 | 可选值 | 默认值 | +|-----------------------------------------------------------------|------------------------------------------------------------------------------------|---------|---------------| +| nacos.server.grpc.port.offset | Nacos Server GRPC端口相对主端口的偏移量 | 任意int值 | 1000 | +| nacos.remote.client.grpc.name | 该Nacos Java SDK的GRPC连接的名字 | 任意字符串 | null | +| nacos.remote.client.grpc.connect.keep.alive | 该Nacos Java SDK的GRPC连接的Keep Alive | 任意Long值 | 5000 | +| nacos.remote.client.grpc.retry.times | 该Nacos Java SDK的GRPC连接发起请求时的最大重试次数 | 任意int值 | 3 | +| nacos.remote.client.grpc.timeout | 该Nacos Java SDK的GRPC连接发起请求时的请求超时时间 | 任意Long值 | 3000 | +| nacos.remote.client.grpc.pool.alive | 该Nacos Java SDK的GRPC连接所使用的线程池的线程Keep Alive时间,单位毫秒 | 任意Long值 | 10000 | +| nacos.remote.client.grpc.pool.core.size | 该Nacos Java SDK的GRPC连接所使用的线程池的最小大小 | 任意int值 | CPU个数*2 | +| nacos.remote.client.grpc.pool.max.size | 该Nacos Java SDK的GRPC连接所使用的线程池的最大大小 | 任意int值 | CPU个数*8 | +| nacos.remote.client.grpc.server.check.timeout | 该Nacos Java SDK的GRPC连接刚连接上服务端时,进行连接注册的超时时间 | 任意Long值 | 3000 | +| nacos.remote.client.grpc.queue.size | 该Nacos Java SDK的GRPC连接的请求队列长度 | 任意int值 | 10000 | +| nacos.remote.client.grpc.health.retry | 该Nacos Java SDK的GRPC连接的健康检查重试次数,达到这个次数健康检查失败的连接会被客户端强制关闭,进行重连 | 任意int值 | 3 | +| nacos.remote.client.grpc.health.timeout | 该Nacos Java SDK的GRPC连接的健康检查超时时间 | 任意Long值 | 3000 | +| nacos.remote.client.grpc.maxinbound.message.size | 该Nacos Java SDK的GRPC连接单次请求的Request的最大大小,单位byte | 任意int值 | 10M | +| nacos.remote.client.grpc.channel.keep.alive | 该Nacos Java SDK的GRPC连接对应的TCP Channel的Keep Alive时间,此时间应该大于`connect.keep.alive`,单位毫秒 | 任意int值 | 6 * 60 * 1000 | +| nacos.remote.client.grpc.channel.keep.alive.timeout | 该Nacos Java SDK的GRPC连接对应的TCP Channel的Keep Alive超时时间,单位毫秒 | 任意Long值 | 20 * 1000 | +| nacos.remote.client.grpc.channel.capability.negotiation.timeout | 该Nacos Java SDK的GRPC连接对应的TLS握手超时时间 | 任意Long值 | 5000 | + +### 2.5. 其他参数 + +Nacos Java SDK 中有部分参数对运行时期的影响较小,且需要全局一致,因此此类参数目前需要通过JVM参数(-D)或环境变量进行设置,一般使用时使用默认值即可,仅在遇到一些特殊场景时才需要设置。 + +| 参数名 | 含义 | 可选值 | 默认值 | +|---------------------------|--------------------------------------------------------------------------------------------------------------------|--------------------|-------------------| +| PER_TASK_CONFIG_SIZE | 每个`ConfigService`可监听的最大配置数 | 任意int | 3000 | +| JM.SNAPSHOT.PATH | Nacos Java SDK的本地快照根目录,根目录下会创建`naming`和`config`两个目录用于存放订阅的服务和配置的缓存信息 | 任意目录 | ${user.home} | +| JM.LOG.PATH | Nacos Java SDK的日志输出目录,正常情况下Nacos Java SDK的日志会输出到该目录下,部分特殊的场景和版本,可能会输出到业务日志中(如使用了log4j1.0的版本,或使用Spring Cloud重载了日志配置 | 任意目录 | ${user.home}/logs | +| nacos.server.port | 默认的Nacos Server**配置中心和鉴权login**的端口,当传入的`serverAddr`参数中不带有端口号时,使用此设置的端口连接Nacos Server,建议统一通过`serverAddr`参数设置端口 | 0~65535 | 8848 | +| nacos.naming.exposed.port | 默认的Nacos Server**注册中心**的端口,当传入的`serverAddr`参数中不带有端口号时,使用此设置的端口连接Nacos Server,建议统一通过`serverAddr`参数设置端口 | 0~65535 | 8848 | +| nacos.client.contextPath | 默认的Nacos Server contentPath,`contextPath`和`endpointContextPath` 未传入时使用 | 任意URL支持的path | nacos | +| nacos.env.first | Nacos JAVA SDK 的 `NacosClientProperties` 配置搜索顺序。详情见[1.3.1. 优先级](#131-优先级) | PROPERTIES/JVM/ENV | PROPERTIES | +| project.name | 该SDK所归属的应用名,可在服务订阅者列表和配置订阅者列表中使用,仅作为参考字段使用 | 任意字符串 | unknown | +| ~~NACOS.CONNECT.TIMEOUT~~ | 连接服务时的连接超时时间,旧版本Http使用,已废弃 | 任意int | 1000 | +| NACOS.READ.TIMEOUT | 连接服务时的读取超时时间,旧版本Http使用 | 任意int | 3000 | diff --git a/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/usage.md b/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/usage.md new file mode 100644 index 00000000000..7eef596cd21 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/java-sdk/usage.md @@ -0,0 +1,1068 @@ +--- +title: Java SDK 使用手册 +keywords: [Java,SDK,使用手册] +description: 本文档介绍了Nacos的Java SDK(nacos-client)的使用方式,包括如何配置Nacos Client、如何使用Nacos Client、如何使用Nacos Client的API。 +sidebar: + order: 2 +--- + +# Java SDK 使用手册 + +## 1. 引用概述 + +Maven 坐标 +``` + + com.alibaba.nacos + nacos-client + ${version} + +``` + +> 注意:由于Nacos Java SDK在2.0版本后引入了gRPC,为了避免用户业务引入的gRPC版本不同导致冲突,使用了shaded技术将部分依赖直接封装进nacos-client中,导致nacos-client较大。 +> 如果用户未自行引入gRPC或确认版本无冲突,希望使用纯净版的nacos-client以减小依赖,可以使用classifier来指定使用纯净版。 + +```xml + + + 2.4.2 + + + + + com.alibaba.nacos + nacos-client + ${nacos.version} + + pure + + + + ${project.groupId} + nacos-common + ${nacos.version} + + + ${project.groupId} + nacos-api + ${nacos.version} + + +``` + +## 2. 初始化SDK + +Nacos 初始化SDK仅需要使用 `NacosFactory` 类进行不同模块的创建即可: + +```java + +String serverAddr = "localhost:8848"; + +# 初始化配置中心的Nacos Java SDK +ConfigService configService = NacosFactory.createConfigService(serverAddr); + +# 初始化配置中心的Nacos Java SDK +NamingService namingService = NacosFactory.createNamingService(serverAddr); +``` + +如果初始化SDK时,还需要配置一些参数,可以使用 `Properties` 类进行配置: + +```java + +Properties properties = new Properties(); +# 指定Nacos-Server的地址 +properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); +# 指定Nacos-SDK的命名空间 +properties.setProperty(PropertyKeyConst.NAMESPACE, "${namespaceId}"); + +# 初始化配置中心的Nacos Java SDK +ConfigService configService = NacosFactory.createConfigService(properties); + +# 初始化配置中心的Nacos Java SDK +NamingService namingService = NacosFactory.createNamingService(properties); +``` + +更多初始化时所涉及的参数配置,请参考[Java SDK 配置参数](./properties.md)。 + +> 注意:一个Nacos Java SDK实例只能用于获取同一个命名空间下的配置和服务,如果要获取不同的命名空间下的配置或服务,需要创建不同的Nacos Java SDK实例。 + +## 3. 配置管理 API +### 3.1. 获取配置 +#### 描述 + +用于服务启动的时候从 Nacos 获取配置。 +```java +public String getConfig(String dataId, String group, long timeoutMs) throws NacosException +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写产品名:模块名(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| timeout | long | 读取配置超时时间,单位 ms,推荐值 3000。 | + + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值 | + + +#### 请求示例 + +```java +try { + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfig(dataId, group, 5000); + System.out.println(content); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.2. 监听配置 +#### 描述 + +如果希望 Nacos 推送配置变更,可以使用 Nacos 动态监听配置接口来实现。 + +```java +public void addListener(String dataId, String group, Listener listener) +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- |:-----------------------------------------------------------------------------------------------------------------------------------------------| +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写**产品名:模块名**(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| listener | Listener | 监听器,配置变更进入监听器的回调函数。 | + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值,初始化或者配置变更的时候通过回调函数返回该值。 | + + +#### 请求示例 + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +String content = configService.getConfig(dataId, group, 5000); +System.out.println(content); +configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("recieve1:" + configInfo); + } + @Override + public Executor getExecutor() { + return null; + } +}); + +// 测试让主线程不退出,因为订阅配置是守护线程,主线程退出守护线程就会退出。 正式代码中无需下面代码 +while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } +} +``` + +### 3.3. 删除监听 +#### 描述 + +取消监听配置,取消监听后配置不会再推送。 + +```java +public void removeListener(String dataId, String group, Listener listener) +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组 | +| listener | ConfigChangeListenerAdapter | 监听器,配置变更进入监听器的回调函数。 | + + +#### 使用示例 + +```java +String serverAddr = "{serverAddr}"; +String dataId = "{dataId}"; +String group = "{group}"; +Properties properties = new Properties(); +properties.put("serverAddr", serverAddr); +ConfigService configService = NacosFactory.createConfigService(properties); +configService.removeListener(dataId, group, yourListener); +``` + +### 3.4. 发布配置 +#### 描述 + +用于通过程序自动发布 Nacos 配置,以便通过自动化手段降低运维成本。 + +注意:创建和修改配置时使用的同一个发布接口,当配置不存在时会创建配置,当配置已存在时会更新配置。 + +```java +public boolean publishConfig(String dataId, String group, String content) throws NacosException; + +public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException; + +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 `package.class`(如 `com.taobao.tc.refund.log.level`)的命名规则保证全局唯一性。建议根据配置的业务含义来定义 class 部分。全部字符均为小写。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 256 字节。 | +| group | string | 配置分组,建议填写`产品名:模块名`(如 Nacos`:Test`)来保证唯一性。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 128 字节。 | +| content | string | 配置内容,不超过 100K 字节。 | +| type | string | @Since 1.4.1. 配置类型,见 `com.alibaba.nacos.api.config.ConfigType`,默认为TEXT | + + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否发布成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isPublishOk = configService.publishConfig(dataId, group, "content"); + System.out.println(isPublishOk); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.5. 删除配置 +#### 描述 + +用于通过程序自动删除 Nacos 配置,以便通过自动化手段降低运维成本。 + +>注意: 当配置已存在时会删除该配置,当配置不存在时会直接返回成功消息。 + + +```java +public boolean removeConfig(String dataId, String group) throws NacosException + +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID | +| group | string | 配置分组 | + + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否删除成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + + ConfigService configService = NacosFactory.createConfigService(properties); + boolean isRemoveOk = configService.removeConfig(dataId, group); + System.out.println(isRemoveOk); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.6. 带监听器的获取配置 + +#### 描述 + +如果希望在程序首次启动获取配置时自行注册的Listener用于以后配置更新,建议您直接使用该接口。 + +> 该接口等价于先使用`getConfig`之后再使用`addListener`。 + +```java +String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException; +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- | :--- | +| dataId | string | 配置 ID,采用类似 package.class(如com.taobao.tc.refund.log.level)的命名规则保证全局唯一性,class 部分建议是配置的业务含义。全部字符小写。只允许英文字符和 4 种特殊字符("."、":"、"-"、"\_"),不超过 256 字节。 | +| group | string | 配置分组,建议填写产品名:模块名(Nacos:Test)保证唯一性,只允许英文字符和4种特殊字符("."、":"、"-"、"\_"),不超过128字节。 | +| timeout | long | 读取配置超时时间,单位 ms,推荐值 3000。 | +| listener | Listener | 监听器,配置变更进入监听器的回调函数。 | + +#### 返回值 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 配置值 | + + +#### 请求示例 + +```java +try { + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfigAndSignListener(dataId, group, 5000, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + System.out.println("recieve1:" + configInfo); + } + @Override + public Executor getExecutor() { + return null; + } + }); + System.out.println(content); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +### 3.7. 带Compare-And-Swap(CAS)的发布配置 + +#### 描述 + +直接使用`publishConfig`进行配置发布时,可能存在不同进程间并发的配置覆盖问题,此时可以使用带Compare-And-Swap(CAS)的发布配置API,来保证不会此类情形。 + +注意:创建和修改配置时使用的同一个发布接口,当配置不存在时会创建配置,当配置已存在时会更新配置。 + +```java +boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException; + +boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException; +``` + +#### 请求参数 + +| 参数名 | 参数类型 | 描述 | +| :--- | :--- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| dataId | string | 配置 ID,采用类似 `package.class`(如 `com.taobao.tc.refund.log.level`)的命名规则保证全局唯一性。建议根据配置的业务含义来定义 class 部分。全部字符均为小写。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 256 字节。 | +| group | string | 配置分组,建议填写`产品名:模块名`(如 Nacos`:Test`)来保证唯一性。只允许英文字符和 4 种特殊字符(“.”、“:”、“-”、“\_”),不超过 128 字节。 | +| content | string | 配置内容,不超过 100K 字节。 | +| casMd5 | string | 前配置内容的md5 | +| type | string | 配置类型,见 `com.alibaba.nacos.api.config.ConfigType`,默认为TEXT | + +#### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| boolean | 是否发布成功 | + + +#### 请求示例 + +```java +try { + // 初始化配置服务,控制台通过示例代码自动获取下面参数 + String serverAddr = "{serverAddr}"; + String dataId = "{dataId}"; + String group = "{group}"; + String oldContent = "oldContent"; + String oldContentMd5 = "63fb636909f1ebad67110e49117e6de4"; + Properties properties = new Properties(); + properties.put("serverAddr", serverAddr); + ConfigService configService = NacosFactory.createConfigService(properties); + # 首次发布,casMd5传入null。 + boolean isPublishOk = configService.publishConfigCas(dataId, group, oldContent, null); + System.out.println(isPublishOk); + # old Md5 正确,变成成功 + isPublishOk = configService.publishConfigCas(dataId, group, "newContent", oldContentMd5); + System.out.println(isPublishOk); + # old Md5 错误,变成失败 + isPublishOk = configService.publishConfigCas(dataId, group, "newContent2", oldContentMd5); + System.out.println(isPublishOk); +} catch (NacosException e) { + e.printStackTrace(); +} +``` + +#### 异常说明 + +读取配置超时或网络异常,抛出 NacosException 异常。 + +## 4. 服务发现API +### 4.1. 注册实例 +#### 描述 +注册一个实例到服务。 + +> 由于同一个Nacos Client实例,仅能向一个服务注册一个实例;若同一个Nacos Client实例多次向同一个服务注册实例,后注册的实例将会覆盖先注册的实例。 +> 若有存在代理注册的场景,请使用[批量注册服务实例](#48-批量注册服务实例) + +```java +void registerInstance(String serviceName, String ip, int port) throws NacosException; + +void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException; + +void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; + +void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException; + +void registerInstance(String serviceName, Instance instance) throws NacosException; + +void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:-------|----------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| ip | 字符串 | 服务实例IP | 无,必填 | +| port | int | 服务实例port | 无,必填 | +| clusterName | 字符串 | 集群名 | DEFAULT | +| instance | 参见代码注释 | 实例属性 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); + +# 以下注册请求所造成的结果均一致, 注册分组名为`DEFAULT_GROUP`, 服务名为`nacos.test.service`的实例,实例的ip为`127.0.0.1`, port为`8848`, clusterName为`DEFAULT`. +naming.registerInstance("nacos.test.service", "127.0.0.1", 8848); +naming.registerInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848); +naming.registerInstance("nacos.test.service", "127.0.0.1", 8848, "DEFAULT"); +naming.registerInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848, "DEFAULT"); +Instance instance = new Instance(); +instance.setIp("127.0.0.1"); +instance.setPort(8848); +instance.setClusterName("DEFAULT"); +naming.registerInstance("nacos.test.service", instance); +naming.registerInstance("nacos.test.service", "DEFAULT_GROUP", instance); +``` + +### 4.2. 注销实例 +#### 描述 +删除服务下的一个实例。 + +> 若该服务是通过[批量注册服务实例](#48-批量注册服务实例)进行注册,使用注销实例进行注销时,将注销所有批量注册的实例。 +> 若仅希望注销部分批量注册的实例,请使用[批量注销服务实例](#49-批量注销服务实例) + +```java +void deregisterInstance(String serviceName, String ip, int port) throws NacosException; + +void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException; + +void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; + +void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException; + +void deregisterInstance(String serviceName, Instance instance) throws NacosException; + +void deregisterInstance(String serviceName, String groupName, Instance instance); +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:-------|----------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| ip | 字符串 | 服务实例IP | 无,必填 | +| port | int | 服务实例port | 无,必填 | +| clusterName | 字符串 | 集群名 | DEFAULT | +| instance | 参见代码注释 | 实例属性 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下注销请求所造成的结果均一致, 注销分组名为`DEFAULT_GROUP`, 服务名为`nacos.test.service`的实例,实例的ip为`127.0.0.1`, port为`8848`, clusterName为`DEFAULT`. +naming.deregisterInstance("nacos.test.service", "127.0.0.1", 8848); +naming.deregisterInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848); +naming.deregisterInstance("nacos.test.service", "127.0.0.1", 8848, "DEFAULT"); +naming.deregisterInstance("nacos.test.service", "DEFAULT_GROUP", "127.0.0.1", 8848, "DEFAULT"); +Instance instance = new Instance(); +instance.setIp("127.0.0.1"); +instance.setPort(8848); +instance.setClusterName("DEFAULT"); +naming.deregisterInstance("nacos.test.service", instance); +naming.deregisterInstance("nacos.test.service", "DEFAULT_GROUP", instance); +``` + +### 4.3. 获取全部实例 +#### 描述 +获取服务下的所有实例。 +```java +List getAllInstances(String serviceName) throws NacosException; + +List getAllInstances(String serviceName, String groupName) throws NacosException; + +List getAllInstances(String serviceName, boolean subscribe) throws NacosException; + +List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException; + +List getAllInstances(String serviceName, List clusters) throws NacosException; + +List getAllInstances(String serviceName, String groupName, List clusters) throws NacosException; + +List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException; + +List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------|------------------------------------------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| subscribe | Boolean | 是否订阅服务,为true时将会订阅该服务,同时查询优先通过内存缓存;为false时将直接查询Nacos Server | true | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | + +#### 返回参数 +List<Instance> 实例列表。 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下查询请求所造成的结果均一致. +System.out.println(naming.getAllInstances("nacos.test.service")); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP")); +System.out.println(naming.getAllInstances("nacos.test.service", true)); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP", true)); +System.out.println(naming.getAllInstances("nacos.test.service", new ArrayList<>())); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>())); +System.out.println(naming.getAllInstances("nacos.test.service", new ArrayList<>(), true)); +System.out.println(naming.getAllInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true)); +``` + +### 4.4. 获取健康或不健康实例列表 +#### 描述 +根据条件获取过滤后的实例列表。 +```java +List selectInstances(String serviceName, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException; + +List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException; + +List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, String groupName, List clusters, boolean healthy) throws NacosException; + +List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException; + +List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------|------------------------------------------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| subscribe | Boolean | 是否订阅服务,为true时将会订阅该服务,同时查询优先通过内存缓存;为false时将直接查询Nacos Server | true | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| healthy | boolean | 是否健康,为true时仅会返回健康的实例列表,反之则返回不健康的实例列表。 | true | + +#### 返回参数 +List<Instance> 实例列表。 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下查询请求所造成的结果均一致. +System.out.println(naming.selectInstances("nacos.test.service", true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", true)); +System.out.println(naming.selectInstances("nacos.test.service", true, true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", true, true)); +System.out.println(naming.selectInstances("nacos.test.service", new ArrayList<>(), true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true)); +System.out.println(naming.selectInstances("nacos.test.service", new ArrayList<>(), true, true)); +System.out.println(naming.selectInstances("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true, true)); +``` + +### 4.5. 获取一个健康实例 +#### 描述 +根据负载均衡算法随机获取一个健康实例。 +```java +Instance selectOneHealthyInstance(String serviceName) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException; + +Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------|------------------------------------------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| subscribe | Boolean | 是否订阅服务,为true时将会订阅该服务,同时查询优先通过内存缓存;为false时将直接查询Nacos Server | true | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | + + +#### 返回参数 +Instance 实例。 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 以下查询请求所造成的结果均一致. +System.out.println(naming.selectOneHealthyInstance("nacos.test.service")); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP")); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", true)); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP", true)); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", new ArrayList<>())); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>())); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", new ArrayList<>(), true)); +System.out.println(naming.selectOneHealthyInstance("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), true)); +``` + +### 4.6. 监听服务 +#### 描述 +监听服务下的实例列表变化。 +```java +void subscribe(String serviceName, EventListener listener) throws NacosException; + +void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException; + +void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException; + +void subscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| listener | EventListener | 回调listener | 无,必填 | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}; +naming.subscribe("nacos.test.service", serviceListener); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", serviceListener); +naming.subscribe("nacos.test.service", new ArrayList<>(), serviceListener); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), serviceListener); +``` + +#### 使用自定义线程池进行异步监听 + +Nacos 支持使用自定义线程池进行异步监听回调,只需要将`EventListener`更换为`AbstractEventListener`,并实现`Executor getExecutor()`方法来返回自定义的线程池,Nacos Client将在服务发生变更时使用该线程池进行异步回调。 + +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +ExecutorService executorService = Executors.newFixedThreadPool(1); +EventListener serviceListener = new AbstractEventListener() { + @Override + public void onEvent(Event event) { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } + } + + @Override + public Executor getExecutor() { + return executorService; + } +}; +naming.subscribe("nacos.test.service", serviceListener); +``` + +#### 监听服务变化的差值 + +Nacos 从2.4.0版本你开始,支持监听服务变化的差值,即和之前相比,有哪些实例被新增,移除和修改,只需要将`EventListener`更换为`AbstractNamingChangeListener`,实现`onChange`方法即可。`onChange`中会传入`NamingChangeEvent`,其中`InstancesDiff`记录了此次通知和之前相比的实例变化。 + +同时为了防止差值的错误和异常,`NamingChangeEvent`仍然可以通过`getInstances`方法获取最终的服务实例列表。 + +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +ExecutorService executorService = Executors.newFixedThreadPool(1); +EventListener serviceListener = new AbstractNamingChangeListener() { + @Override + public void onChange(NamingChangeEvent event) { + if (event.isAdded()) { + System.out.println(event.getAddedInstances()); + } + if (event.isRemoved()) { + System.out.println(event.getRemovedInstances()); + } + if (event.isModified()) { + System.out.println(event.getModifiedInstances()); + } + } + + @Override + public Executor getExecutor() { + return executorService; + } +}; +naming.subscribe("nacos.test.service", serviceListener); +``` + +### 4.7. 取消监听服务 +#### 描述 +取消监听服务下的实例列表变化。 +```java +void unsubscribe(String serviceName, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| listener | EventListener | 回调listener | 无,必填 | + +> 注意:取消监听服务时,需要使用进行订阅时的`listener`进行取消监听,否则可能造成取消监听失败。 + +#### 返回参数 +无 + +#### 请求示例 +```java + +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> {}; +naming.subscribe("nacos.test.service", serviceListener); +naming.unsubscribe("nacos.test.service", serviceListener); +naming.unsubscribe("nacos.test.service", "DEFAULT_GROUP", serviceListener); +naming.unsubscribe("nacos.test.service", new ArrayList<>(), serviceListener); +naming.unsubscribe("nacos.test.service", "DEFAULT_GROUP", new ArrayList<>(), serviceListener); +``` + +### 4.8. 批量注册服务实例 + +#### 描述 + +注册一系列实例到指定服务。 + +> 由于同一个Nacos Client实例,仅能向一个服务注册一个实例;若同一个Nacos Client实例多次向同一个服务注册实例,后注册的实例将会覆盖先注册的实例。 +> 考虑到社区存在代理注册的场景:如Nacos-Sync, Proxy-Registry等,需要在一个客户端中注册同一个服务的不同实例,社区新增了批量注册服务实例的功能。 + +```java +void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|--------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| instances | Instance的List | 服务实例列表 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); + +Instance instance1 = new Instance(); +instance1.setIp("127.0.0.1"); +instance1.setPort(8848); +instance1.setClusterName("DEFAULT"); + +Instance instance2 = new Instance(); +instance2.setIp("127.0.0.1"); +instance2.setPort(9848); +instance2.setClusterName("DEFAULT"); + +List instances = new ArrayList<>(2); +instances.add(instance1); +instances.add(instance2); + +naming.batchRegisterInstance("nacos.test.service", "DEFAULT_GROUP", instances); +``` + +### 4.9. 批量注销服务实例 + +#### 描述 + +从指定服务中注销一系列实例。 + +> 针对使用了批量注册服务实例的用户设计,允许用户选择一部分或全部批量注册的实例进行注销。 + +```java +void batchDeregisterInstance(String serviceName, String groupName, List instances) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:--------------|--------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| instances | Instance的List | 服务实例列表 | 无,必填 | + +#### 返回参数 +无 +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); + +Instance instance1 = new Instance(); +instance1.setIp("127.0.0.1"); +instance1.setPort(8848); +instance1.setClusterName("DEFAULT"); + +Instance instance2 = new Instance(); +instance2.setIp("127.0.0.1"); +instance2.setPort(9848); +instance2.setClusterName("DEFAULT"); + +List instances = new ArrayList<>(2); +instances.add(instance1); +instances.add(instance2); + +naming.batchRegisterInstance("nacos.test.service", "DEFAULT_GROUP", instances); +naming.batchDeregisterInstance("nacos.test.service", "DEFAULT_GROUP", instances); +``` + +### 4.10. 带选择器的监听服务 + +#### 描述 + +使用自定义逻辑的选择器,监听服务下的实例列表变化,当服务列表发生变化时,会使用自定义的选择器进行过滤,当过滤后的数据仍然有变化时,才会进行回调通知。 + +```java +void subscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; + +void subscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:---------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| selector | NamingSelector | 自定义的数据选择器 | 无,必填 | +| listener | EventListener | 回调listener | 无,必填 | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}; +# 只会选择订阅ip为`127.0`开头的实例。 +NamingSelector selector = NamingSelectorFactory.newIpSelector("127.0.*"); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", selector, serviceListener); + +``` + +#### 预设提供的数据选择器 + +Nacos Client 提供了预设的多种数据选择器以供默认场景下使用: +1. Cluster选择器,`NamingSelectorFactory.newClusterSelector(Collection clusters)`, 当订阅服务时传入了`clusters`参数,Nacos Client将自动使用该数据选择器。 +2. Ip选择器,`NamingSelectorFactory.newClusterSelector(String ipRegex)`,当实例的ip满足传入的ipRegex时,才会被通知回调。 +3. 元数据选择器,`NamingSelectorFactory.newMetadataSelector(Map metadata)`,当实例的元数据包含**所有**传入选择器的metadata时,才会被通知回调。 +4. 任意元数据选择器,`NamingSelectorFactory.newMetadataSelector(Map metadata, false)`,当实例的元数据包含**任意一对**传入选择器的metadata时,才会被通知回调。 + +#### 开发自定义数据选择器 + +多数情况下, 开发自定义数据选择器只需要创建`DefaultNamingSelector`即可,在构建时传入一个`Predicate filter`作为单个实例是否满足您过滤条件的结果,类似Java中stream的filter方法,如此您仅需要考虑单个实例的过滤条件即可。 + +若是`DefaultNamingSelector`无法满足需求,您需要实现`NamingSelector`接口,根据传入的`NamingContext`进行复杂的逻辑校验,最后输出`NamingResult`给Nacos Client。 + +### 4.11. 取消带选择器的监听服务 + +#### 描述 + +使用自定义逻辑的选择器进行监听服务下的实例列表变化,那么在取消监听时需要使用`取消带选择器的监听服务`的API才能正确取消监听。 + +> 注意:取消监听时需要传入监听时使用的selector和listener,否则可能导致取消监听失败。 + +```java +void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; + +void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; +``` + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:------------|:---------------|-----------------------------|---------------| +| serviceName | 字符串 | 服务名 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | +| clusters | 字符串列表 | 实例的clusterName,空列表时将查询所有实例。 | 空列表 | +| selector | NamingSelector | 自定义的数据选择器 | 无,必填 | +| listener | EventListener | 回调listener | 无,必填 | + +#### 返回参数 +无 + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +EventListener serviceListener = event -> { + if (event instanceof NamingEvent) { + System.out.println(((NamingEvent) event).getServiceName()); + System.out.println(((NamingEvent) event).getInstances()); + } +}; +# 只会选择订阅ip为`127.0`开头的实例。 +NamingSelector selector = NamingSelectorFactory.newIpSelector("127.0.*"); +naming.subscribe("nacos.test.service", "DEFAULT_GROUP", selector, serviceListener); +naming.unsubscribe("nacos.test.service", "DEFAULT_GROUP", selector, serviceListener); + +``` + +### 4.12. 分页获取服务列表 + +#### 描述 + +通过分页的方式获取当前客户端所在命名空间的服务列表 + +```java +ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException; + +ListView getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException; +``` + +> 注意,使用`AbstractSelector`的`getServicesOfServer`方法已废弃,请勿继续使用。 + +#### 请求参数 + +| 名称 | 类型 | 描述 | 默认值 | +|:----------|:----|------------|---------------| +| pageNo | int | 分页序号 | 无,必填 | +| pageSize | int | 分页中每页的服务个数 | 无,必填 | +| groupName | 字符串 | 分组名 | DEFAULT_GROUP | + +#### 返回参数 +服务名列表: ListView + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +# 等价于`naming.getServicesOfServer(1, 10, "DEFAULT_GROUP");` +ListView result = naming.getServicesOfServer(1, 10); +System.out.println(result.getCount()); +System.out.println(result.getData()); +``` + +### 4.13. 获取当前客户端所监听的服务列表 + +#### 描述 + +获取当前客户端所的所有服务列表 + +```java +List getSubscribeServices() throws NacosException; +``` + +#### 请求参数 + +无 + +#### 返回参数 +服务列表: List + +#### 请求示例 +```java +NamingService naming = NamingFactory.createNamingService(System.getProperty("serveAddr")); +System.out.println(naming.getSubscribeServices()); +``` + +## 5. Java SDK的生命周期 + +Nacos的Java SDK 生命周期从创建时开始,到调用`shutdown()`方法时结束,期间对应创建的线程池、连接等均会始终保留,及时连接断开,也会不断重试重新建立连接。 + +因此在使用时需要注意应用中创建的Nacos Java SDK的实例个数,避免造成线程池和连接的泄漏,在更换Nacos Java SDK实例时,切记调用`shutdown()`方法,同时在应用中应尽量复用同一个Nacos Java SDK实例,避免频繁的初始化实例。 diff --git a/src/content/docs/v3.0/zh-cn/manual/user/open-api.md b/src/content/docs/v3.0/zh-cn/manual/user/open-api.md new file mode 100644 index 00000000000..6934f995d72 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/open-api.md @@ -0,0 +1,1919 @@ +--- +title: Open API 手册 +keywords: [Open API,手册] +description: Open API 手册 +sidebar: + order: 7 +--- + +# Open API 手册 + +Nacos 2.X 版本兼容 Nacos1.X 版本的OpenAPI, 请参考文档[Nacos1.X OpenAPI](https://nacos.io/docs/v1/open-api)使用。 + +## 1. 文档规定 + +### 1.1. API 统一返回体格式 + +2.0版本Open API,所有接口请求的响应均为`json`类型的返回体,返回体具有相同的格式 + +```json +{ + "code": 0, + "message": "success", + "data": {} +} +``` + +返回体中各字段的含义如下表所示 + +| 名称 | 类型 | 描述 | +|:---------:|:--------:|--------------------------------------------------------| +| `code ` | `int` | 错误码,`0`代表执行成功,非`0`代表执行失败的某一种情况 | +| `message` | `String` | 错误码提示信息,执行成功为"`success`" | +| `data` | 任意类型 | 返回数据,执行失败时为详细出错信息 | + +> 由于执行成功的情况下code字段与message字段相同,后续在介绍接口的返回结果时,只介绍返回数据的data字段 + +### 1.2. API 错误码汇总 + +API接口返回体中的错误码及对应提示信息汇总见下表 + +| 错误码 | 提示信息 | 含义 | +|---------|------------------------------|----------------------------| +| `0` | `success` | 成功执行 | +| `10000` | `parameter missing` | 参数缺失 | +| `10001` | `access denied` | 访问拒绝 | +| `10002` | `data access error` | 数据访问错误 | +| `20001` | `'tenant' parameter error` | `tenant`参数错误 | +| `20002` | `parameter validate error` | 参数验证错误 | +| `20003` | `MediaType Error` | 请求的`MediaType`错误 | +| `20004` | `resource not found` | 资源未找到 | +| `20005` | `resource conflict` | 资源访问冲突 | +| `20006` | `config listener is null` | 监听配置为空 | +| `20007` | `config listener error` | 监听配置错误 | +| `20008` | `invalid dataId` | 无效的`dataId`(鉴权失败) | +| `20009` | `parameter mismatch` | 请求参数不匹配 | +| `21000` | `service name error` | `serviceName`服务名错误 | +| `21001` | `weight error` | `weight`权重参数错误 | +| `21002` | `instance metadata error` | 实例`metadata`元数据错误 | +| `21003` | `instance not found` | `instance`实例不存在 | +| `21004` | `instance error` | `instance`实例信息错误 | +| `21005` | `service metadata error` | 服务`metadata`元数据错误 | +| `21006` | `selector error` | 访问策略`selector`错误 | +| `21007` | `service already exist` | 服务已存在 | +| `21008` | `service not exist` | 服务不存在 | +| `21009` | `service delete failure` | 存在服务实例,服务删除失败 | +| `21010` | `healthy param miss` | `healthy`参数缺失 | +| `21011` | `health check still running` | 健康检查仍在运行 | +| `22000` | `illegal namespace` | 命名空间`namespace`不合法 | +| `22001` | `namespace not exist` | 命名空间不存在 | +| `22002` | `namespace already exist` | 命名空间已存在 | +| `23000` | `illegal state` | 状态`state`不合法 | +| `23001` | `node info error` | 节点信息错误 | +| `23002` | `node down failure` | 节点离线操作出错 | +| ... | ... | ... | +| 30000 | `server error` | 其他内部错误 | + +## 2. 配置管理 + +### 2.1. 获取配置 + +#### 接口描述 + +获取指定配置 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/config` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `tag` | `String` | 否 | 标签 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|----------| +| `data` | `String` | 配置内容 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "contentTest" + } + ``` + +### 2.2. 发布配置 + +#### 接口描述 + +发布指定配置 + +> 当配置已存在时,则对配置进行更新 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/cs/config` + +#### 请求Body + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置组名 | +| `dataId` | `String` | **是** | 配置名 | +| `content` | `String` | **是** | 配置内容 | +| `tag` | `String` | 否 | 标签 | +| `appName` | `String` | 否 | 应用名 | +| `srcUser` | `String` | 否 | 源用户 | +| `configTags` | `String` | 否 | 配置标签列表,可多个,逗号分隔 | +| `desc` | `String` | 否 | 配置描述 | +| `use` | `String` | 否 | - | +| `effect` | `String` | 否 | - | +| `type` | `String` | 否 | 配置类型 | +| `schema` | `String` | 否 | - | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'dataId=nacos.example' \ + -d 'group=DEFAULT_GROUP' \ + -d 'namespaceId=public' \ + -d 'content=contentTest' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/cs/config' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 2.3. 删除配置 + +#### 接口描述 + +删除指定配置 + +#### 请求方式 + +`DELETE` + +#### 请求URL + +`/nacos/v2/cs/config` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|--------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `tag` | `String` | 否 | 标签 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/cs/config?dataId=nacos.example&group=DEFAULT_GROUP&namespaceId=public' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 2.3. 查询配置历史列表 + +#### 接口描述 + +获取指定配置的历史版本列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history/list` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `pageNo` | `int` | 否 | 当前页,默认为`1` | +| `pageSize` | `int` | 否 | 页条目数,默认为`100`,最大为`500` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------|------------|----------------------------------------------------------| +| `data` | `Object` | 分页查询结果 | +| `data.totalCount` | `int` | 总数 | +| `data.pageNumber` | `int` | 当前页 | +| `data.pagesAvailable` | `int` | 总页数 | +| `data.pageItems` | `Object[]` | 历史配置项列表,参见[历史配置项信息](#ConfigHistoryInfo) | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/list?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "totalCount": 1, + "pageNumber": 1, + "pagesAvailable": 1, + "pageItems": [ + { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + ] + } + } + ``` + +### 2.3. 查询具体版本的历史配置 + +#### 接口描述 + +获取指定版本的历史配置 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `nid` | `long` | **是** | 历史配置id | + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|----------|------------| +| `data` | `Object` | 历史配置项 | +| `data.id` | `String` | 配置`id` | +| `data.lastId` | `int` | | +| `data.dataId` | `String` | 配置名 | +| `data.group` | `String` | 配置分组 | +| `data.tenant` | `String` | 租户信息(命名空间) | +| `data.appName` | `String` | 应用名 | +| `data.md5` | `String` | 配置内容的md5值 | +| `data.content` | `String` | 配置内容 | +| `data.srcIp` | `String` | 源ip | +| `data.srcUser` | `String` | 源用户 | +| `data.opType` | `String` | 操作类型 | +| `data.createdTime` | `String` | 创建时间 | +| `data.lastModifiedTime` | `String` | 上次修改时间 | +| `data.encryptedDataKey` | `String` | | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history?dataId=nacos.example&group=com.alibaba.nacos&namespaceId=&nid=203' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +### 2.6. 查询配置上一版本信息 + +#### 接口描述 + +获取指定配置的上一版本 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/cs/history/previous` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 参数描述 | +|---------------|----------|--------|------------------------| +| `namespaceId` | `String` | 否 | 命名空间,默认为`public`与 `''`相同 | +| `group` | `String` | **是** | 配置分组名 | +| `dataId` | `String` | **是** | 配置名 | +| `id` | `long` | **是** | 配置id | + +

返回数据

+ +| 参数名 | 参数类型 | 描述说明 | +|--------|----------|------------------------------------------------------| +| `data` | `Object` | 历史配置项,参见[历史配置项信息](#ConfigHistoryInfo) | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/cs/history/previous?id=309135486247505920&dataId=nacos.example&group=com.alibaba.nacos&namespaceId=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "id": "203", + "lastId": -1, + "dataId": "nacos.example", + "group": "com.alibaba.nacos", + "tenant": "", + "appName": "", + "md5": "9f67e6977b100e00cab385a75597db58", + "content": "contentTest", + "srcIp": "0:0:0:0:0:0:0:1", + "srcUser": null, + "opType": "I", + "createdTime": "2010-05-04T16:00:00.000+0000", + "lastModifiedTime": "2020-12-05T01:48:03.380+0000" + } + } + ``` + +## 3. 服务发现 + +:::note +Nacos v2的openAPI中,已移除关于`发送实例心跳`的相关API,原因是Nacos2.0对于非持久化实例,已经使用长链接作为存活的基准,而不是通过传统`心跳续约`的方式进行。因此对于使用Nacos2.0的客户端用户,以及针对Nacos2.0进行服务开发的用户,其实已经不再需要使用v2的心跳openAPI。 + +Nacos v2的`注册实例`的OpenAPI,更多的是给予持久化服务实例进行注册;非持久化实例的注册,建议采用Nacos2.0客户端进行,获取更高的性能及更灵敏的变化感知能力。 + +对于仍然在使用Nacos1.X客户端的用户,以及基于Nacos v1 `发送实例心跳`的openAPI的用户,依旧可以继续使用。 +::: + +### 3.1. 注册实例 + +#### 接口描述 + +注册一个实例 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.2. 注销实例 + +#### 接口描述 + +注销指定实例 + +#### 请求方式 + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.3. 更新实例 + +#### 接口描述 + +修改实例信息 + +> 通过该接口更新的元数据拥有更高的优先级,且具有记忆能力;会在对应实例删除后,依旧存在一段时间,如果在此期间实例重新注册,该元数据依旧生效;您可以通过`nacos.naming.clean.expired-metadata.expired-time`及`nacos.naming.clean.expired-metadata.interval`对记忆时间进行修改 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `healthy` | `boolean` | 否 | 是否只查找健康实例,默认为`true` | +| `weight` | `double` | 否 | 实例权重,默认为`1.0` | +| `enabled` | `boolean` | 否 | 是否可用,默认为`true` | +| `metadata` | `JSON格式String` | 否 | 实例元数据 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'ip=127.0.0.1' \ + -d 'port=8090' \ + -d 'weight=0.9' \ + -d 'ephemeral=true' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.4. 查询实例详情 + +#### 接口描述 + +查询某个具体实例的详情信息 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/instance` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------|-----------|--------------| +| `data` | `Object` | 实例详情信息 | +| `data.serviceName` | `String` | 服务名 | +| `data.ip` | `String` | `IP`地址 | +| `data.port` | `int` | 端口号 | +| `data.clusterName` | `String` | 集群名称 | +| `data.weight` | `double` | 实例权重 | +| `data.healthy` | `boolean` | 是否健康 | +| `data.instanceId` | `String` | 实例`id` | +| `data.metadata` | `map` | 实例元数据 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance?namespaceId=public&groupName=&serviceName=test_service&ip=127.0.0.1&port=8080' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "serviceName": "DEFAULT_GROUP@@test_service", + "ip": "127.0.0.1", + "port": 8080, + "clusterName": "DEFAULT", + "weight": 1.0, + "healthy": true, + "instanceId": null, + "metadata": { + "value": "1" + } + } + } + ``` + +### 3.5. 查询指定服务的实例列表 + +#### 接口描述 + +查询指定服务下的实例详情信息列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/instance/list` + +#### 请求头 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------------|----------|----------|----------------------| +| `User-Agent` | `String` | 否 | 用户代理,默认为空 | +| `Client-Version` | `String` | 否 | 客户端版本,默认为空 | + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|----------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名称,默认为`DEFAULT` | +| `ip` | `String` | 否 | `IP`地址,默认为空,表示不限制`IP`地址 | +| `port` | `int` | 否 | 端口号,默认为`0`,表示不限制端口号 | +| `healthyOnly` | `boolean` | 否 | 是否只获取健康实例,默认为`false` | +| `app` | `String` | 否 | 应用名,默认为空 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|----------------------------------------|------------|-----------| +| `data` | | 指定服务的实例列表 | +| `data.name` | `String` | 分组名@@服务名 | +| `data.groupName` | `String` | 分组名 | +| `data.clusters` | `String` | 集群名 | +| `data.cacheMillis` | `int` | 缓存时间 | +| `data.hosts` | `Object[]` | 实例列表 | +| `data.hosts.ip` | `String` | 实例`IP` | +| `data.hosts.port` | `int` | 实例端口号 | +| `data.hosts.weight` | `double` | 实例权重 | +| `data.hosts.healthy` | `boolean` | 实例是否健康 | +| `data.hosts.enabled` | `boolean` | 实例是否可用 | +| `data.hosts.ephemeral` | `boolean` | 是否为临时实例 | +| `data.hosts.clusterName` | `String` | 实例所在的集群名称 | +| `data.hosts.serviceName` | `String` | 服务名 | +| `data.hosts.metadata` | `map` | 实例元数据 | +| `data.hosts.instanceHeartBeatTimeOut` | `int` | 实例心跳超时时间 | +| `data.hosts.ipDeleteTimeout` | `int` | 实例删除超时时间 | +| `data.hosts.instanceHeartBeatInterval` | `int` | 实例心跳间隔 | +| `data.lastRefTime` | `int` | 上次刷新时间 | +| `data.checksum` | `int` | 校验码 | +| `data.allIPs` | `boolean` | | +| `data.reachProtectionThreshold` | `boolean` | 是否到达保护阈值 | +| `data.valid` | `boolean` | 是否有效 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/instance/list?serviceName=test_service&ip=127.0.0.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "name": "DEFAULT_GROUP@@test_service", + "groupName": "DEFAULT_GROUP", + "clusters": "", + "cacheMillis": 10000, + "hosts": [ + { + "ip": "127.0.0.1", + "port": 8080, + "weight": 1.0, + "healthy": true, + "enabled": true, + "ephemeral": true, + "clusterName": "DEFAULT", + "serviceName": "DEFAULT_GROUP@@test_service", + "metadata": { + "value": "1" + }, + "instanceHeartBeatTimeOut": 15000, + "ipDeleteTimeout": 30000, + "instanceHeartBeatInterval": 5000 + } + ], + "lastRefTime": 1662554390814, + "checksum": "", + "allIPs": false, + "reachProtectionThreshold": false, + "valid": true + } + } + ``` + +### 3.6. 批量更新实例元数据 + +#### 接口描述 + +批量更新实例的元数据, + +> 对应元数据的键不存在时,则添加对应元数据 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance/metadata/batch` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `consistencyType` | `String` | 否 | 持久化类型,默认为空 | +| `instances` | `JSON格式String` | 否 | 需要更新的实例列表,默认为空 | +| `metadata` | `JSON格式String` | **是** | 实例元数据 | + +#### 参数说明 + +> - `consistencyType`: 实例的持久化类型,当为‘`persist`’,表示对持久化实例的元数据进行更新;否则表示对临时实例的元数据进行更新 +> - `instances`: 待更新的实例列表,`json`数组,通过`ip+port+ephemeral+cluster`定位到某一实例,为空则表示更新指定服务下所有实例的元数据 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.7. 批量删除实例元数据 + +#### 接口描述 + +批量删除实例的元数据, + +> 对应元数据的键不存在时,则不做操作 + +#### 请求方式 + +`DELETE` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/instance/metadata/batch` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `consistencyType` | `String` | 否 | 持久化类型,默认为空 | +| `instances` | `JSON格式String` | 否 | 需要更新的实例列表,默认为空 | +| `metadata` | `JSON格式String` | **是** | 实例元数据 | + +#### 参数说明 + +> - `consistencyType`: 实例的持久化类型,当为‘`persist`’,表示对持久化实例的元数据进行删除;否则表示对临时实例的元数据进行 +> - `instances`: 待更新的实例列表,`json`数组,通过`ip+port+ephemeral+cluster`定位到某一实例,为空则表示更新指定服务下所有实例的元数据 + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=test_service' \ + -d 'consistencyType=ephemeral' \ + -d 'instances=[{"ip":"3.3.3.3","port": "8080","ephemeral":"true","clusterName":"xxxx-cluster"},{"ip":"2.2.2.2","port":"8080","ephemeral":"true","clusterName":"xxxx-cluster"}]' \ + -d 'metadata={"age":"20","name":"cocolan"}' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/instance/metadata/batch' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.8. 创建服务 + +#### 接口描述 + +创建一个服务 + +> 服务已存在时会创建失败 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `metadata` | `JSON格式String` | 否 | 服务元数据,默认为空 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例,默认为`false` | +| `protectThreshold` | `float` | 否 | 保护阈值,默认为`0` | +| `selector` | `JSON格式String` | 否 | 访问策略,默认为空 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ephemeral=true' \ + -d 'metadata={"k1":"v1"}' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.9. 删除服务 + +#### 接口描述 + +删除指定服务 + +> 服务不存在时会报错,且服务还存在实例时会删除失败 + +#### 请求方式 + +`DELETE` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X DELETE 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.10. 修改服务 + +#### 接口描述 + +更新指定服务 + +> 服务不存在时会报错 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|--------------------|------------------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `metadata` | `JSON格式String` | 否 | 服务元数据,默认为空 | +| `protectThreshold` | `float` | 否 | 保护阈值,默认为`0` | +| `selector` | `JSON格式String` | 否 | 访问策略,默认为空 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'metadata={"k1":"v2"}' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/service' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 3.11. 查询服务详情 + +#### 接口描述 + +查询某个具体服务的详情信息 + +> 服务不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/service` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|---------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-------------------------|-----------|----------------| +| `data` | | 服务信息 | +| `data.namespace` | `String` | 命名空间 | +| `data.groupName` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.clusterMap` | `map` | 集群信息 | +| `data.metadata` | `map` | 服务元数据 | +| `data.protectThreshold` | `float` | 保护阈值 | +| `data.selector` | `Object` | 访问策略 | +| `data.ephemeral` | `Boolean` | 是否为临时实例 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service?serviceName=nacos.test.1' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "public", + "serviceName": "nacos.test.1", + "groupName": "DEFAULT_GROUP", + "clusterMap": {}, + "metadata": {}, + "protectThreshold": 0, + "selector": { + "type": "none", + "contextType": "NONE" + }, + "ephemeral": false + } + } + ``` + +### 3.12. 查询服务列表 + +#### 接口描述 + +查询符合条件的服务列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/service/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|------------------|----------|-----------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `selector` | `JSON格式String` | **是** | 访问策略 | +| `pageNo` | `int` | 否 | 当前页,默认为`1` | +| `pageSize` | `int` | 否 | 页条目数,默认为`20`,最大为`500` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|------------|------------------| +| `data` | | 服务列表信息 | +| `data.count` | `String` | 服务数目 | +| `data.services` | `String[]` | 分页后的服务列表 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/service/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "count": 2, + "services": [ + "nacos.test.1", + "nacos.test.2" + ] + } + } + ``` + +### 3.13. 更新实例健康状态 + +#### 接口描述 + +更新实例的健康状态 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/ns/health/instance` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|-------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `clusterName` | `String` | 否 | 集群名,默认为`DEFAULT` | +| `ip` | `String` | **是** | `IP`地址 | +| `port` | `int` | **是** | 端口号 | +| `healthy` | `boolean` | **是** | 是否健康 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|----------|--------------------| +| `data` | `String` | “`ok`”表示执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'serviceName=nacos.test.1' \ + -d 'ip=127.0.0.1' \ + -d 'port=8080' \ + -d 'healthy=false' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/ns/health/instance' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": "ok" + } + ``` + +### 3.14. 查询客户端列表(新) + +#### 接口描述 + +查询当前所有的客户端列表 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/list` + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------|------------|----------------| +| `data` | `String[]` | 客户端`id`列表 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + "10.128.164.35:9956#true", + "1664358687402_127.0.0.1_2300", + "1664358642902_127.0.0.1_2229", + "192.168.139.1:49825#true", + "10.128.164.35:9954#true", + "192.168.139.1:53556#true" + ] + } + ``` + +> 对于不同版本的nacos client,建立客户端的方式不同。 +> +> 对于`1.x`版本,每个实例会建立两个基于`ip+port`的客户端,分别对应实例注册与服务订阅,`clientId`格式为 `ip:port#ephemeral` +> +> 对于`2.x`版本的`nacos client`, 每个实例会建立一个`RPC`连接,对应一个基于`RPC`连接的客户端,兼具注册与订阅功能,`clientId` +> 格式为`time_ip_port` + + + +### 3.15. 查询客户端信息(新) + +#### 接口描述 + +查询指定客户端的详细信息 + +> 客户端不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|------------------------|-----------|----------------| +| `data` | `Object` | 客户端信息 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ephemeral` | `boolean` | 是否为临时实例 | +| `data.lastUpdatedTime` | `int` | 上次更新时间 | +| `data.clientType` | `String` | 客户端类型 | +| `data.clientIp` | `String` | 客户端`IP` | +| `data.clientPort` | `String` | 客户端端口 | +| `data.connectType` | `String` | 连接类型 | +| `data.appName` | `String` | 应用名 | +| `data.Version` | `String` | 客户端版本 | + +> 只有当`clientType`为`connection`时,会显示`connectType`,`appName`和`appName`字段 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "clientId": "1664527081276_127.0.0.1_4400", + "ephemeral": true, + "lastUpdatedTime": 1664527081642, + "clientType": "connection", + "connectType": "GRPC", + "appName": "-", + "version": "Nacos-Java-Client:v2.1.0", + "clientIp": "10.128.164.35", + "clientPort": "4400" + } + } + ``` + +### 3.16. 查询客户端的注册信息(新) + +#### 接口描述 + +查询指定客户端的注册信息 + +> 客户端不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/publish/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------------------|------------|----------------------| +| `data` | `Object[]` | 客户端注册的服务列表 | +| `data.namespace` | `String` | 命名空间 | +| `data.group` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.registeredInstance` | `Object` | 该服务下注册的实例 | +| `data.registeredInstance.ip` | `String` | `IP`地址 | +| `data.registeredInstance.port` | `int` | 端口号 | +| `data.registeredInstance.cluster` | `String` | 集群名 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/publish/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "registeredInstance": { + "ip": "10.128.164.35", + "port": 9950, + "cluster": "DEFAULT" + } + } + ] + } + ``` + +### 3.17. 查询客户端的订阅信息(新) + +#### 接口描述 + +查询指定客户端的订阅信息 + +> 客户端不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/subscribe/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|------------|----------|----------|------------| +| `clientId` | `String` | **是** | 客户端`id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------------------|------------|----------------------| +| `data` | `Object[]` | 客户端订阅的服务列表 | +| `data.namespace` | `String` | 命名空间 | +| `data.group` | `String` | 分组名 | +| `data.serviceName` | `String` | 服务名 | +| `data.subscriberInfo` | `Object` | 订阅信息 | +| `data.subscriberInfo.app` | `String` | 应用 | +| `data.subscriberInfo.agent` | `String` | 客户端信息 | +| `data.subscriberInfo.addr` | `String` | 地址 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/subscribe/list?clientId=1664527081276_127.0.0.1_4400' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "public", + "group": "DEFAULT_GROUP", + "serviceName": "nacos.test.1", + "subscriberInfo": { + "app": "unknown", + "agent": "Nacos-Java-Client:v2.1.0", + "addr": "10.128.164.35" + } + } + ] + } + ``` + +### 3.18. 查询注册指定服务的客户端信息(新) + +#### 接口描述 + +查询注册指定服务的客户端信息 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/service/publisher/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|-----------|----------|------------------------------------| +| `namespaceId` | `String` | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | `String` | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | `String` | **是** | 服务名 | +| `ephemeral` | `boolean` | 否 | 是否为临时实例 | +| `ip` | `String` | 否 | `IP`地址,默认为空,不限制`IP`地址 | +| `port` | `int` | 否 | 端口号,默认为空,表示不限制端口号 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|----------|------------| +| `data` | | 客户端列表 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ip` | `String` | 客户端`IP` | +| `data.port` | `int` | 客户端端口 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/publisher/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527081276_127.0.0.1_4400", + "ip": "10.128.164.35", + "port": 9950 + }, + { + "clientId": "10.128.164.35:9954#true", + "ip": "10.128.164.35", + "port": 9954 + } + ] + } + ``` + +### 3.19. 查询订阅指定服务的客户端信息(新) + +#### 接口描述 + +查询订阅指定服务的客户端信息 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/ns/client/service/subscriber/list` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|------------------------------------| +| `namespaceId` | String | 否 | 命名空间`Id`,默认为`public` | +| `groupName` | String | 否 | 分组名,默认为`DEFAULT_GROUP` | +| `serviceName` | String | **是** | 服务名 | +| `ephemeral` | boolean | 否 | 是否为临时实例 | +| `ip` | String | 否 | `IP`地址,默认为空,不限制`IP`地址 | +| `port` | int | 否 | 端口号,默认为空,表示不限制端口号 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|-----------------|----------|------------| +| `data` | | 客户端列表 | +| `data.clientId` | `String` | 客户端`id` | +| `data.ip` | `String` | 客户端`IP` | +| `data.port` | `int` | 客户端端口 | + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/ns/client/service/subscriber/list?serviceName=nacos.test.1&ip=&port=' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "clientId": "1664527125645_127.0.0.1_4443", + "ip": "10.128.164.35", + "port": 0 + }, + { + "clientId": "172.24.144.1:54126#true", + "ip": "172.24.144.1", + "port": 54126 + } + ] + } + ``` + +## 4. 命名空间 + +### 4.1. 查询命名空间列表 + +#### 接口描述 + +查询当前所有的命名空间 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/console/namespace/list` + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------------|------------|----------------------| +| `data` | `Object[]` | 命名空间列表 | +| `data.namespace` | `String` | 命名空间`ID` | +| `data.namespaceShowName` | `String` | 命名空间名称 | +| `data.namespaceDesc` | `String` | 命名空间描述 | +| `data.quota` | `int` | 命名空间的容量 | +| `data.configCount` | `int` | 命名空间下的配置数量 | +| `data.type` | `int` | 命名空间类型 | + +> 命名空间分为3种类型,`0 `- 全局命名空间 `1` - 默认私有命名空间 `2 `- 自定义命名空间 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace/list' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": [ + { + "namespace": "", + "namespaceShowName": "public", + "namespaceDesc": null, + "quota": 200, + "configCount": 1, + "type": 0 + } + ] + } + ``` + +### 4.2. 查询具体命名空间 + +#### 接口描述 + +查询具体命名空间的信息 + +> 命名空间不存在时会报错 + +#### 请求方式 + +`GET` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述说明 | +|--------------------------|----------|----------------------| +| `data` | `Object` | 命名空间信息 | +| `data.namespace` | `String` | 命名空间`ID` | +| `data.namespaceShowName` | `String` | 命名空间名称 | +| `data.namespaceDesc` | `String` | 命名空间描述 | +| `data.quota` | `int` | 命名空间的容量 | +| `data.configCount` | `int` | 命名空间下的配置数量 | +| `data.type` | `int` | 命名空间类型 | + +> 命名空间分为3种类型,`0` - 全局命名空间 `1` - 默认私有命名空间 `2` - 自定义命名空间 + +#### 示例 + +* 请求示例 + + ```shell + curl -X GET 'http://127.0.0.1:8848/nacos/v2/console/namespace?namespaceId=test_namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": { + "namespace": "test_namespace", + "namespaceShowName": "test", + "namespaceDesc": null, + "quota": 200, + "configCount": 0, + "type": 2 + } + } + ``` + +### 4.3. 创建命名空间 + +#### 接口描述 + +创建一个命名空间 + +> 命名空间已存在时会报错 + +#### 请求方式 + +`POST` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | +| `namespaceName` | `String` | **是** | 命名空间名称 | +| `namespaceDesc` | `String` | 否 | 命名空间描述 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test' \ + -X POST 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 4.4. 编辑命名空间 + +#### 接口描述 + +编辑命名空间信息 + +#### 请求方式 + +`PUT` + +`Content-Type:application/x-www-form-urlencoded` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求Body + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|-----------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | +| `namespaceName` | `String` | **是** | 命名空间名称 | +| `namespaceDesc` | `String` | 否 | 命名空间描述 | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -d 'namespaceName=test.nacos' \ + -X PUT 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` + +### 4.5. 删除命名空间 + +#### 接口描述 + +删除指定命名空间 + +#### 请求方式 + +`DELETE` + +#### 请求URL + +`/nacos/v2/console/namespace` + +#### 请求参数 + +| 参数名 | 参数类型 | 是否必填 | 描述说明 | +|---------------|----------|----------|--------------| +| `namespaceId` | `String` | **是** | 命名空间`Id` | + +#### 返回数据 + +| 参数名 | 参数类型 | 描述 | +|--------|-----------|--------------| +| `data` | `boolean` | 是否执行成功 | + +#### 示例 + +* 请求示例 + + ```shell + curl -d 'namespaceId=test_namespace' \ + -X DELETE 'http://127.0.0.1:8848/nacos/v2/console/namespace' + ``` + +* 返回示例 + + ```json + { + "code": 0, + "message": "success", + "data": true + } + ``` diff --git a/src/content/docs/v3.0/zh-cn/manual/user/overview/other-language.md b/src/content/docs/v3.0/zh-cn/manual/user/overview/other-language.md new file mode 100644 index 00000000000..13726540dab --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/overview/other-language.md @@ -0,0 +1,21 @@ +--- +title: Nacos SDK 概览 +keywords: [多语言,SDK] +description: 多语言SDK介绍 +sidebar: + order: 1 +--- + +# Nacos SDK 概览 + +Nacos官方目前提供并维护了Java、Golang、Python三个版本的客户端,同时我们将主要依靠社区的贡献来发展多语言客户端。在未来,我们将向Nacos社区用户推荐那些最被广泛使用的以及支持最好的客户端作为Nacos相应语言的官方版本。 + +| 编程语言 | 代码仓库 | 包仓库 | +| ---- | ---- | ---- | +| Java | [https://github.com/alibaba/nacos](https://github.com/alibaba/nacos)| [https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client](https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client) +| Golang | [https://github.com/nacos-group/nacos-sdk-go](https://github.com/nacos-group/nacos-sdk-go) | github.com/nacos-group/nacos-sdk-go/v2| +| Python | [https://github.com/nacos-group/nacos-sdk-python](https://github.com/nacos-group/nacos-sdk-python) |[https://pypi.org/project/nacos-sdk-python/](https://pypi.org/project/nacos-sdk-python/)| +| C++ | [https://github.com/nacos-group/nacos-sdk-cpp](https://github.com/nacos-group/nacos-sdk-cpp)|/| +| NodeJS|[https://github.com/nacos-group/nacos-sdk-nodejs](https://github.com/nacos-group/nacos-sdk-nodejs)|[https://www.npmjs.com/package/nacos](https://www.npmjs.com/package/nacos)| +| C#| [https://github.com/nacos-group/nacos-sdk-csharp](https://github.com/nacos-group/nacos-sdk-csharp)|[https://www.nuget.org/packages/nacos-sdk-csharp](https://www.nuget.org/packages/nacos-sdk-csharp)| +|Rust|[https://github.com/nacos-group/nacos-sdk-rust](https://github.com/nacos-group/nacos-sdk-rust)|[https://crates.io/crates/nacos-sdk/versions](https://crates.io/crates/nacos-sdk/versions) diff --git a/src/content/docs/v3.0/zh-cn/manual/user/parameters-check.md b/src/content/docs/v3.0/zh-cn/manual/user/parameters-check.md new file mode 100644 index 00000000000..07dfb4be22a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/parameters-check.md @@ -0,0 +1,235 @@ +--- +title: 参数校验 +keywords: [参数校验,使用规则] +description: 参数校验 +sidebar: + order: 9 +--- + +# 参数校验 + +2.3.0版本之前的Nacos的参数校验逻辑分散,由各类请求的处理方法单独进行校验,难以更改维护,经常出现参数校验的遗漏,参数校验的规则也没有明确统一;这使得用户使用时经常会因为一些特殊字符导致功能不符合预期或出现漏洞,甚至导致大量推送,导致带宽打满,内存占用过多,导致应用出现故障。 + +在2.3.0之后的版本中,Nacos明确了参数校验规则,在服务端实现了统一的参数校验逻辑并添加了参数校验层,根据校验规则对客户端向服务端发送的请求进行校验。 + +用户可以选择开启参数校验功能,开启后Nacos将会对客户端向服务端发送的请求中的部分参数进行参数校验,确保参数的合法性,避免由于错误使用,导致的不符合预期以及性能问题。 + +## 1. 参数校验开关 + +服务端的参数校验功能**默认开启**,用户可以通过设置`${nacos.home}/conf`目录下的`application.properties`文件中的`nacos.core.param.check.enabled`值选择开启或者关闭服务端参数校验功能。 + +`nacos.core.param.check.enabled=true`时开启Nacos服务端参数校验,`false`关闭服务端参数校验 + +## 2. 参数校验规则 + +开启参数校验后OpenAPI文档 和 SDK文档中的所有接口中的相关参数都会接受格式校验,现对相关参数以及校验规则进行说明: + +|参数描述|最大字符长度|校验规则| +|-----|-----|-----| +|命名空间名称|256|禁止`@#$%^&*`,对应正则表达式:`[^@#$%^&*]+$`| +|命名空间ID|64|只允许字母数字下划线以及"-"字符,对应正则表达式:`^[\w-]+`| +|配置名称|256|只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$`| +|服务名称|512|禁止中文和`@@`且禁止以`@`开头,禁止空白字符,对应正则表达式`^(?!@).((?!@@)[^\u4E00-\u9FA5])*$`| +|分组名称|128|只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$`| +|集群名称|64|只允许数字字母和`-_`,对应正则表达式`^[0-9a-zA-Z-_]+$`| +|IP地址|128|禁止中文字符和空白字符,对应正则表达式为`^[^\u4E00-\u9FA5]*$`| +|端口号|-|取值范围为`0~65535`| +|实例元数据|1024|字段名加字段值的默认总长度小于1024个字符,可使用环境变量`NACOS_NAMING_SERVICE_METADATA_LENGTH=`进行动态调整| + +### 2.1. namespaceShowName + +#### 参数描述 + +命名空间名称 + +#### 校验规则 + +字符长度最大为256,禁止`@#$%^&*`,对应正则表达式:`[^@#$%^&*]+$` + +#### OpenAPI示例 + +- [创建命名空间](./open-api.md#43-创建命名空间) + +#### 校验失败报错信息 + +- 超出长度:`Param 'namespaceShowName' is illegal, the param length should not exceed 256.` +- 非法字符:`Param 'namespaceShowName' is illegal, illegal characters should not appear in the param.` + +### 2.2. namespaceId/tenant/namespace + +#### 参数描述 + +命名空间ID(租户空间) + +#### 校验规则 + +字符长度最大为64,只允许字母数字下划线以及"-"字符,对应正则表达式:`^[\w-]+` + +#### OpenAPI示例 + +- [获取配置](./open-api.md#21-获取配置) +- [注册实例](./open-api.md#31-注册实例) + +#### 校验失败报错信息 + +- 超出长度:`Param 'namespaceId/tenant' is illegal, the param length should not exceed 64.` +- 非法字符:`Param 'namespaceId/tenant' is illegal, illegal characters should not appear in the param.` + +### 2.3. dataId + +#### 参数描述 + +配置名称 + +#### 校验规则 + +字符长度最大为256,只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$` + +#### OpenAPI示例 + +[发布配置](./open-api.md#22-发布配置) + +#### Java SDK示例 + +监听配置:`public void addListener(String dataId, String group, Listener listener) ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'dataId' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'dataId' is illegal, illegal characters should not appear in the param.` + +### 2.4. service/serviceName + +#### 参数描述 + +服务名称 + +#### 校验规则 + +字符长度最大为512,禁止中文和`@@`且禁止以`@`开头,对应正则表达式`^(?!@).((?!@@)[^\u4E00-\u9FA5])*$` + +#### OpenAPI示例 + +[注册实例](./open-api.md#31-注册实例) + +#### Java SDK示例 + +注册实例:`void registerInstance(String serviceName, String ip, int port) throws NacosException; ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'serviceName' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'serviceName' is illegal, illegal characters should not appear in the param.` + +### 2.5. group/groupName + +#### 参数描述 + +分组名称 + +#### 校验规则 + +字符长度最大为128,只允许字母数字以及`_-.:`,对应正则表达式:`^[a-zA-Z0-9-_:\.]*$` + +#### OpenAPI示例 + +[查询实例列表](./open-api.md#35-查询指定服务的实例列表) + +#### Java SDK示例 + +删除配置:`public boolean removeConfig(String dataId, String group) throws NacosException ` + +#### 校验失败报错信息 + +- 超出长度:`Param 'group' is illegal, the param length should not exceed 512.` +- 非法字符:`Param 'group' is illegal, illegal characters should not appear in the param.` + +### 2.6. cluster/clusterName + +#### 参数描述 + +集群名称 + +#### 校验规则 + +字符长度最大为64,只允许数字字母和`-_`,对应正则表达式`^[0-9a-zA-Z-_]+$` + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +获取全部实例:`List getAllInstances(String serviceName, List clusters) throws NacosException;` + +#### 校验失败报错信息 + +- 超出长度:`Param 'cluster' is illegal, the param length should not exceed 64.` +- 非法字符:`Param 'cluster' is illegal, illegal characters should not appear in the param.` + +### 2.7. ip + +#### 参数描述 + +IP地址 + +#### 校验规则 + +字符长度最大为128,禁止中文字符,对应正则表达式为`^[^\u4E00-\u9FA5]*$` + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +注销实例:`void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;` + +#### 校验失败报错信息 + +- 超出长度:`Param 'ip' is illegal, the param length should not exceed 128.` +- 非法字符:`Param 'ip' is illegal, illegal characters should not appear in the param.` + +### 2.8. port + +#### 参数描述 + +端口号 + +#### 校验规则 + +取值范围为0~65535 + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +注销实例:`void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;` + +#### 校验失败报错信息 + +端口取值超出范围:`Param 'port' is illegal, the value should be between 0 and 65535` + +### 2.9. metadata + +#### 参数描述 + +实例元数据 + +#### 校验规则 + +字段名加字段值的总长度小于1024个字符 + +#### OpenAPI示例 + +[更新实例](./open-api.md#33-更新实例) + +#### Java SDK示例 + +注册实例:`void registerInstance(String serviceName, Instance instance) throws NacosException;` + +#### 校验失败报错信息 + +实例总长度超出范围:`Param 'Metadata' is illegal, the param length should not exceed %d.` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/manual/user/python-sdk/usage.md b/src/content/docs/v3.0/zh-cn/manual/user/python-sdk/usage.md new file mode 100644 index 00000000000..e0ab66495d1 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/manual/user/python-sdk/usage.md @@ -0,0 +1,239 @@ +--- +title: Python SDK 使用手册 +keywords: [Python,SDK,使用手册] +description: 本文档介绍了Nacos的Python SDK(nacos-sdk-python)的使用方式 +sidebar: + order: 6 +--- + +# Python SDK 使用手册 + + +### 支持 Python 版本 + +- Python 2.7 +- Python 3.6+ + +### 支持 Nacos 服务端版本 +- Nacos 0.8.0+ +- Nacos 1.x +- Nacos 2.x with http protocol + + +## 安装依赖 +```shell +pip install nacos-sdk-python +``` + +## 开始 +```python +import nacos + +# Both HTTP/HTTPS protocols are supported, if not set protocol prefix default is HTTP, and HTTPS with no ssl check(verify=False) +# "192.168.3.4:8848" or "https://192.168.3.4:443" or "http://192.168.3.4:8848,192.168.3.5:8848" or "https://192.168.3.4:443,https://192.168.3.5:443" +SERVER_ADDRESSES = "server addresses split by comma" +NAMESPACE = "namespace id" + +# no auth mode +client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE) +# auth mode +#client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE, ak="{ak}", sk="{sk}") + +# get config +data_id = "config.nacos" +group = "group" +print(client.get_config(data_id, group)) +``` + +## 客户端配置 +``` +client = NacosClient(server_addresses, namespace=your_ns, ak=your_ak, sk=your_sk) +``` + +* *server_addresses* - **required** - Nacos server address, comma separated if more than 1. +* *namespace* - Namespace. | default: `None` +* *ak* - The accessKey to authenticate. | default: null +* *sk* - The secretKey to authentication. | default: null +* *log_level* - Log level. | default: null +* *log_rotation_backup_count* - The number of log files to keep. | default: `7` + +#### 额外配置 +Extra option can be set by `set_options`, as following: + +``` +client.set_options({key}={value}) +# client.set_options(proxies={"http":"192.168.3.50:809"}) +``` + +Configurable options are: + +* *default_timeout* - Default timeout for get config from server in seconds. +* *pulling_timeout* - Long polling timeout in seconds. +* *pulling_config_size* - Max config items number listened by one polling process. +* *callback_thread_num* - Concurrency for invoking callback. +* *failover_base* - Dir to store failover config files. +* *snapshot_base* - Dir to store snapshot config files. +* *no_snapshot* - To disable default snapshot behavior, this can be overridden by param *no_snapshot* in *get* method. +* *proxies* - Dict proxy mapping, some environments require proxy access, so you can set this parameter, this way http requests go through the proxy. + +## API 列表 + +### 获取配置 +>`NacosClient.get_config(data_id, group, timeout, no_snapshot)` + +* `param` *data_id* Data id. +* `param` *group* Group, use `DEFAULT_GROUP` if no group specified. +* `param` *timeout* Timeout for requesting server in seconds. +* `param` *no_snapshot* Whether to use local snapshot while server is unavailable. +* `return` +W +Get value of one config item following priority: + +* Step 1 - Get from local failover dir(default: `${cwd}/nacos-data/data`). + * Failover dir can be manually copied from snapshot dir(default: `${cwd}/nacos-data/snapshot`) in advance. + * This helps to suppress the effect of known server failure. + +* Step 2 - Get from one server until value is got or all servers tried. + * Content will be save to snapshot dir after got from server. + +* Step 3 - Get from snapshot dir. + +### 增加配置监听 +>`NacosClient.add_config_watchers(data_id, group, cb_list)` + +* `param` *data_id* Data id. +* `param` *group* Group, use `DEFAULT_GROUP` if no group specified. +* `param` *cb_list* List of callback functions to add. +* `return` + +Add watchers to a specified config item. +* Once changes or deletion of the item happened, callback functions will be invoked. +* If the item is already exists in server, callback functions will be invoked for once. +* Multiple callbacks on one item is allowed and all callback functions are invoked concurrently by `threading.Thread`. +* Callback functions are invoked from current process. + +### 移除配置监听 +>`NacosClient.remove_config_watcher(data_id, group, cb, remove_all)` + +* `param` *data_id* Data id. +* `param` *group* Group, use "DEFAULT_GROUP" if no group specified. +* `param` *cb* Callback function to delete. +* `param` *remove_all* Whether to remove all occurrence of the callback or just once. +* `return` + +Remove watcher from specified key. + +### 发布配置 +>`NacosClient.publish_config(data_id, group, content, timeout)` + +* `param` *data_id* Data id. +* `param` *group* Group, use "DEFAULT_GROUP" if no group specified. +* `param` *content* Config value. +* `param` *timeout* Timeout for requesting server in seconds. +* `return` True if success or an exception will be raised. + +Publish one data item to Nacos. +* If the data key is not exist, create one first. +* If the data key is exist, update to the content specified. +* Content can not be set to None, if there is need to delete config item, use function **remove** instead. + +### 删除配置 +>`NacosClient.remove_config(data_id, group, timeout)` +* `param` *data_id* Data id. +* `param` *group* Group, use "DEFAULT_GROUP" if no group specified. +* `param` *timeout* Timeout for requesting server in seconds. +* `return` True if success or an exception will be raised. + +Remove one data item from Nacos. + +### 服务实例注册 +>`NacosClient.add_naming_instance(service_name, ip, port, cluster_name, weight, metadata, enable, healthy,ephemeral,group_name,heartbeat_interval)` +* `param` *service_name* **required** Service name to register to. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster to register to. +* `param` *weight* A float number for load balancing weight. +* `param` *metadata* Extra info in JSON string format or dict format +* `param` *enable* A bool value to determine whether instance is enabled or not. +* `param` *healthy* A bool value to determine whether instance is healthy or not. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `param` *heartbeat_interval* Auto daemon heartbeat interval in seconds. +* `return` True if success or an exception will be raised. + +### 服务实例取消注册 +>`NacosClient.remove_naming_instance(service_name, ip, port, cluster_name)` +* `param` *service_name* **required** Service name to deregister from. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster to deregister from. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `return` True if success or an exception will be raised. + +### 修改服务实例 +>`NacosClient.modify_naming_instance(service_name, ip, port, cluster_name, weight, metadata, enable)` +* `param` *service_name* **required** Service name. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster name. +* `param` *weight* A float number for load balancing weight. +* `param` *metadata* Extra info in JSON string format or dict format. +* `param` *enable* A bool value to determine whether instance is enabled or not. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `return` True if success or an exception will be raised. + +### 查询服务实例列表 +>`NacosClient.list_naming_instance(service_name, clusters, namespace_id, group_name, healthy_only)` +* `param` *service_name* **required** Service name to query. +* `param` *clusters* Cluster names separated by comma. +* `param` *namespace_id* Customized group name, default `blank`. +* `param` *group_name* Customized group name , default `DEFAULT_GROUP`. +* `param` *healthy_only* A bool value for querying healthy instances or not. +* `return` Instance info list if success or an exception will be raised. + +### 查询服务实例详情 +>`NacosClient.get_naming_instance(service_name, ip, port, cluster_name)` +* `param` *service_name* **required** Service name. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster name. +* `return` Instance info if success or an exception will be raised. + +### 主动发送心跳 +>`NacosClient.send_heartbeat(service_name, ip, port, cluster_name, weight, metadata)` +* `param` *service_name* **required** Service name. +* `param` *ip* **required** IP of the instance. +* `param` *port* **required** Port of the instance. +* `param` *cluster_name* Cluster to register to. +* `param` *weight* A float number for load balancing weight. +* `param` *ephemeral* A bool value to determine whether instance is ephemeral or not. +* `param` *metadata* Extra info in JSON string format or dict format. +* `return` A JSON object include server recommended beat interval if success or an exception will be raised. + +### 服务实例监听 +>`NacosClient.subscribe(listener_fn, listener_interval=7, *args, **kwargs)` +* `param` *listener_fn* **required** Customized listener function. +* `param` *listener_interval* Listen interval , default 7 second. +* `param` *service_name* **required** Service name which subscribes. +* `param` *clusters* Cluster names separated by comma. +* `param` *namespace_id* Customized group name, default `blank`. +* `param` *group_name* Customized group name , default `DEFAULT_GROUP`. +* `param` *healthy_only* A bool value for querying healthy instances or not. +* `return` + +### 服务实例取消监听 +>`NacosClient.unsubscribe(service_name, listener_name)` +* `param` *service_name* **required** Service name to subscribed. +* `param` *listener_name* listener_name which is customized. +* `return` + +### 停止所有服务监听 +>`NacosClient.stop_subscribe()` +* `return` + +## 调试模式 +Debugging mode if useful for getting more detailed log on console. + +Debugging mode can be set by: +``` +client = nacos.NacosClient(SERVER_ADDRESSES, namespace=NAMESPACE, username=USERNAME, password=PASSWORD,log_level="DEBUG") +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/overview.md b/src/content/docs/v3.0/zh-cn/overview.md new file mode 100644 index 00000000000..3bf10392def --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/overview.md @@ -0,0 +1,138 @@ +--- +title: Nacos 概览 +keywords: [nacos] +description: 什么是 Nacos? +--- + +# Nacos 概览 + +欢迎来到 Nacos 的世界! + +## 什么是Nacos + +Nacos `/nɑ:kəʊs/` 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 + +Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 + +Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以**“服务”**为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。 + +Nacos 支持几乎所有主流类型的**“服务”**的发现、配置和管理: + +- [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) +- [gRPC](https://grpc.io/docs/guides/concepts.html#service-definition) +- [Dubbo RPC Service](https://dubbo.apache.org) +- [Spring Cloud RESTful Service](https://spring.io/projects/spring-cloud) + +### 产品功能 + +* **服务发现和服务健康监测** + + Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 [原生SDK](./guide/user/sdk.md)、[OpenAPI](./guide/user/open-api.md)、或一个[独立的Agent](./guide/user/other-language.md)注册 Service 后,服务消费者可以使用[DNS TODO](./ecology/use-nacos-with-coredns.md) 或[HTTP&API](./guide/user/open-api.md)查找和发现服务。 + + Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。 + +* **动态配置服务** + + 动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。 + + 动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。 + + 配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。 + + Nacos 提供了一个简洁易用的UI ([控制台样例 Demo](http://console.nacos.io/nacos/index.html)) 帮助您管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。 + +* **动态 DNS 服务** + + 动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。 + + Nacos 提供了一些简单的 [DNS APIs TODO](./ecology/use-nacos-with-coredns.md) 帮助您管理服务的关联域名和可用的 IP:PORT 列表. + +* **服务及其元数据管理** + + Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。 + +### 产品优势 + +- **易于使用** + + Nacos经历几万人使用反馈优化,提供统一的服务发现和配置管理功能,通过直观的 Web 界面和简洁的 API,为开发和运维人员在云原生环境中带来了便捷的服务注册、发现和配置更新操作。 + +- **特性丰富** + + Nacos提供了包括服务发现、配置管理、动态 DNS 服务、服务元数据管理、流量管理、服务监控、服务治理等在内的一系列特性,帮助您在云原生时代,更轻松的构建、交付和管理微服务。 + +- **极致性能** + + Nacos经过阿里双十一超快伸缩场景的锤炼,提供高性能的服务注册和发现能力,以及低延迟的配置更新响应,确保在大规模分布式系统中的高效率和稳定运行。 + +- **超大容量** + + Nacos诞生自阿里的百万实例规模,造就支持海量服务和配置的管理,能够满足大型分布式系统对高并发和高可用性的需求。 + +- **稳定可用** + + Nacos 通过自研的同步协议,配合生态中应用广泛的Raft协议,确保了服务的高可用性和数据的稳定性,保证阿里双十一系统的高可用稳定运行。 + +- **开放生态** + + Nacos拥有活跃的开源社区、广泛的生态整合和持续的创新发展,不仅大量兼容了Spring Cloud、Dubbo等大受欢迎的开源框架、还提供了丰富的插件化能力,帮助用户在云原生时代,提供可定制满足自身特殊需求的独有云原生微服务系统。 + +## 设计理念 + +> 我们相信一切都是服务,每个服务节点被构想为一个星球,每个服务都是一个星系。Nacos 致力于帮助建立这些服务之间的**连接**,助力每个面向星辰的梦想能够透过云层,飞在云上,更好的链接整片星空。 + +Nacos希望帮助用户在云原生时代,在私有云、混合云或者公有云等所有云环境中,更好的构建、交付、管理自己的微服务平台,更快的复用和组合业务服务,更快的交付商业创新的价值,从而为用户赢得市场。正是基于这一愿景,Nacos的设计理念被定位为`易于使用`、`面向标准`、`高可用`和`方便扩展`。 + +![设计理念简图](/img/doc/overview/design-philosophy.svg) + +#### 易于使用 + +易于使用是 Nacos 的一个核心理念,它通过提供用户友好的 Web 界面和简洁的 API 来简化服务的注册、发现和配置管理过程。开发者可以轻松集成 Nacos 到他们的应用中,无需投入大量时间在复杂的设置和学习上。 + +#### 面向标准 + +Nacos 采用面向标准的设计理念,遵循云原生应用开发的最佳实践和标准协议,以确保其服务发现和配置管理功能与广泛的技术栈和平台无缝对接。 + +#### 高可用 + +为了满足企业级应用对高可用的需求,Nacos 实现了集群模式,确保在节点发生故障时,服务的发现和配置管理功能不会受影响。集群模式也意味着 Nacos 可以通过增加节点来水平扩展,提升系统的整体性能和承载能力。 + +![Nacos高可用架构图](/img/doc/overview/availability-structure.svg) + +#### 方便扩展 + +Nacos 还注重易于扩展,它采用了模块化的设计使得各个组件都可以独立地进行扩展或替换。这也为社区贡献者提供了方便,使他们能够针对特定的需求开发新的功能或者改善现有功能,进一步推动 Nacos 的生态发展。 + +![Nacos插件架构图](/img/doc/overview/plugin-structure.svg) + +通过上述设计理念的实现,Nacos 为用户提供了一个强大而灵活的平台,以支持不断变化的业务需求,加速业务创新和数字转型,最终帮助用户在竞争激烈的市场中占据有利地位。 + +## 部署模式 + +Nacos 提供了两种两种部署运行模式:`单机模式`和`集群模式` + +![Nacos部署模式图](/img/doc/overview/deploy-structure.svg) + +### 单机模式 + +单机模式又称`单例模式`, 拥有所有Nacos的功能及特性,具有极易部署、快速启动等优点。但无法与其他节点组成集群,无法在节点或网络故障时提供高可用能力。单机模式同样可以使用内置Derby数据库(默认)和外置数据库进行存储。 + +单机模式主要适合于工程师于本地搭建或于测试环境中搭建Nacos环境,主要用于开发调试及测试使用;也能够兼顾部分对稳定性和可用性要求不高的业务场景。 + +### 集群模式 + +集群模式通过自研一致性协议Distro以及Raft协议,将多个Nacos节点构建成了高可用的Nacos集群。数据将在集群中各个节点进行同步,保证数据的一致性。集群模式具有高可用、高扩展、高并发等优点,确保在故障发生时不影响业务的运行。集群模式**默认**采用外置数据库进行存储,但也可以通过内置数据库进行存储。 + +该模式主要适合于生产环境,也是社区最为推荐的部署模式。 + +## 生态组件 + +![Nacos生态图](/img/doc/overview/ecology-structure.png) + +## 路线规划 + +![NacosRoadMap](/img/doc/overview/roadmap.svg) + +## 参与社区 + +Nacos 主要通过Github 进行社区的协作,欢迎社区中所有的用户和开发者加入到Nacos的开发中来。详情请参考[如何共建](contribution/contributing.md)。 \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/plugin/address-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/address-plugin.md new file mode 100644 index 00000000000..8baec700bbf --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/address-plugin.md @@ -0,0 +1,97 @@ +--- +title: 寻址插件 +keywords: [寻址, 插件] +description: 本文描述如何开发及使用Nacos的寻址插件 +sidebar: + order: 2 + hidden: true +--- + +# 寻址插件 + +Nacos从2.3.0版本开始,支持通过[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)的方式注入集群寻址相关插件,并在`application.properties`配置文件中选择某一种插件实现作为实际寻址服务。本文档会详细介绍如何实现一个寻址插件和如何使其生效。 + +> 注意: +> 目前寻址插件还处于Beta测试的阶段,其API及接口定义可能会随后续版本升级而有所修改,请注意您的插件适用版本。 + +## 寻址插件概述 + +当前Nacos集群寻址有三种寻址方式,单机寻址,配置文件寻址和地址服务器寻址,通过寻址插件,用户可以编写自己的寻址逻辑。 + +## 开发Nacos服务端寻址插件 + +开发Nacos服务端寻址插件,首先需要依赖寻址插件的相关API + +```xml + + com.alibaba.nacos + nacos-address-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本 + +随后实现`com.alibaba.nacos.plugin.address.spi.AddressPlugin`接口, 并将您的实现添加到SPI的services当中。 + +接口中需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|---| +|start|void|void|启动该插件的寻址功能。| +|getServerList|void|List<String>|返回所有Nacos集群结点的地址,地址格式为`IP:Port`。| +|getPluginName|void|String|插件的名称,当名字相同时,后加载的插件会覆盖先加载的插件| +|registerListener|Consumer<List<String>>|AddressPlugin|注册监听器, 当集群地址发生改变时调用监听器的方法| +|shutdown|void|void|关闭插件| + +该接口由`com.alibaba.nacos.plugin.address.spi.AbstractAddressPlugin`抽象类默认实现`getServerList`, `registerListener`和`shutdown`方法, +用户在实际编写插件时继承`AbstractAddressPlugin`实现其余方法即可。`AbstractAddressPlugin`有一个名为serverList的List<String>成员变量,即集群地址集合,用户需要在start方法调用后,维护 +该变量即可。 +当用户需要在配置文件中配置插件相关的参数, 需要在property配置文件中配置以address.plugin开头的key,这时变可以通过`com.alibaba.nacos.plugin.address.common.AddressProperties`单例类获取对应的参数 +```properties +address.plugin.${key} = ${val} +``` +配置之后,用户在编写插件时便可以通过 +```java +AddressProperties.getProperty(${key}) +``` +来获取参数。 + +### 使用服务端插件 + +插件开发完成后,需要打包成jar/zip,放置到nacos服务端的classpath中,如果您不知道如何修改classpath,请直接放置到`${nacos-server.path}/plugins`下 + +放置后,需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +### 所启用的Nacos的寻址插件的名称,与`com.alibaba.nacos.plugin.address.spi.AddressService#getPlugin返回值对应 +nacos.core.member.lookup.type=${addressPluginName} +``` +随后重启nacos集群,在有请求访问到nacos节点后,可以从`${nacos-server.path}/logs/nacos-cluster.log`中看到如下日志: + +```text +[AddressPluginManager] Load AddressPlugin(xxxx) PluginName(xxx) successfully. +``` + +### 使用Nacos自带的寻址插件 + +为了与老版本的寻址兼容,用户在不使用自定义插件时该配置与原来相同,还是`nacos.core.member.lookup.type=[file,address-server]`这一配置项. + +## 客户端插件 + +### 使用自定义插件 +Nacos的客户端用户自定义寻址插件实现方式和服务端相同,当用户需要使用自定义插件时,继承`com.alibaba.nacos.plugin.address.spi.AbstractAddressPlugin`或者实现`com.alibaba.nacos.plugin.address.spi.AddressPlugin`接口,将开发完成的客户端插件打包成jar/zip,放入到您应用的classpath中即可自动生效。在初始化`NacosConfigService`或者`NacosNamingService`时,在传递的`Properties`对象中传入key为`addressPluginName`,val为插件`getPluginName`返回值的参数。 +如: +```java + Properties properties = new Properties(); + properties.put("addressPluginName", ${addressPluginName}); + ConfigService configService = NacosFactory.createConfigService(properties); + String content = configService.getConfig(dataId, group, 5000); +``` + +### 使用Nacos自带的寻址插件 +Nacos的Java客户端插件适配了老版本,如果不使用自定义的插件,客户端的使用和之前没有区别。 + +### 其他语言客户端寻址插件 + +待社区贡献。 diff --git a/src/content/docs/v3.0/zh-cn/plugin/auth-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/auth-plugin.md new file mode 100644 index 00000000000..163f46b87cc --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/auth-plugin.md @@ -0,0 +1,182 @@ +--- +title: 鉴权插件 +keywords: [鉴权, 插件] +description: 本文描述如何开发及使用Nacos的鉴权插件 +sidebar: + order: 1 +--- + +# 鉴权插件 + +Nacos从2.1.0版本开始,支持通过[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)的方式注入鉴权相关插件,并在`application.properties`配置文件中选择某一种插件实现作为实际鉴权服务。本文档会详细介绍如何实现一个鉴权插件和如何使其生效。 + +> 注意: +> 目前鉴权插件还处于Beta测试的阶段,其API及接口定义可能会随后续版本升级而有所修改,请注意您的插件适用版本。 + +## 鉴权插件中的概念 + +鉴权,通俗的表达就是,验证 **谁** 是否能够对 **某个东西** 进行 **某种操作** ,因此Nacos在设计鉴权插件时,将鉴权信息主要抽象为`身份信息`,`资源`和`操作类型`3类主要概念。 + +### 身份信息 IdentityContext + +身份信息(IdentityContext)是请求发起主体在Nacos鉴权插件中的抽象。由于不同的插件实现,身份信息可能不同,较为灵活;比如用户名和密码是一种身份信息,accessToken又是另一种身份信息。因此身份信息(IdentityContext)并没有限制具体的个数和名字,插件实现可以自定义任意个数和身份关键字,Nacos将会从请求中自动获取插件实现定义的身份关键字及其对应的值注入到身份信息(IdentityContext)中,供插件使用。 + +其中必定会包含的内容有: + +|字段名|描述| +|-----|---| +|remote_ip|请求来源ip| + +### 资源 Resource + +资源(Resource)是请求所操作对象在Nacos鉴权插件中的抽象。它主要由Nacos来定义,具体可以是某个配置,某个服务,或者某个分组。 + +资源(Resource)主要由以下内容组成: + +|字段名|描述| +|-----|---| +|namespaceId|请求资源的命名空间ID,部分接口可能没有该值| +|group| 请求资源的分组名,部分接口可能没有该值| +|name | 请求资源的资源名,如服务名或配置的dataId,部分接口可能是定义的特殊值,如`nacos/admin`| +|type | 请求资源的类型,可能取值为`SignType`中的枚举值,主要表示该资源所相关的模块 | +|properties| 请求资源的扩展配置,不属于上述的资源相关信息,会被放如properties中,比如Grpc请求的Request名称或`@Secured`注解上的tags等 | + +### 操作类型 Action + +操作类型(Action)是请求操作在Nacos鉴权插件中的抽象,主要有读操作`R`和写操作`W`,详情查看`ActionTypes`枚举。 + +## 服务端插件 + +开发Nacos服务端鉴权插件,首先需要依赖鉴权插件的相关API + +```xml + + com.alibaba.nacos + nacos-auth-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本 + +随后实现`com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService`接口, 并将您的实现添加到SPI的services当中。 + +接口中需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|---| +|getAuthServiceName|void|String|插件的名称,当名字相同时,后加载的插件会覆盖先加载的插件。| +|identityNames|void|Collection<String>|插件的身份信息关键字,Nacos会从请求中获取以这些关键字为key的参数,并注入到IdentityContext中。| +|enableAuth|ActionTypes,SignType|boolean|在调用`validateIdentity`和`validateAuthority`前调用,插件可自行判断是否对此类型的操作或此类型的模块进行鉴权。| +|validateIdentity|IdentityContext, Resource|boolean|对身份信息进行验证,在`validateAuthority`前调用| +|validateAuthority|IdentityContext, Permission|boolean|对权限进行验证,在`validateIdentity`返回为`true`时调用| +|isLoginEnabled|void|boolean|是否该插件开启开源控制台登录页,返回`true`时,访问开源控制台将需要通过登录页登录| + +### 加载服务端插件 + +插件开发完成后,需要打包成jar/zip,放置到nacos服务端的classpath中,如果您不知道如何修改classpath,请直接放置到`${nacos-server.path}/plugins`下 + +放置后,需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +### 所启用的Nacos的鉴权插件的名称,与`com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService`的`getAuthServiceName`返回值对应 +nacos.core.auth.system.type=${authServiceName} + +### 开启鉴权功能 +nacos.core.auth.enabled=true +``` + +随后重启nacos集群,在有请求访问到nacos节点后,可以从`${nacos-server.path}/logs/nacos.log`中看到如下日志: + +```text +[AuthPluginManager] Load AuthPluginService(xxxx) AuthServiceName(xxx) successfully. +``` + +### 使用Nacos自带的鉴权插件 + +Nacos默认带有一个鉴权的简易实现,主要是为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。开启和使用方式请查看文档[运维手册-鉴权鉴权](../manual/admin/auth.mdx). + +## 客户端插件 + +Nacos的客户端鉴权插件主要工作为将鉴权相关的身份信息,注入到请求中,让每个请求都能够被对应的服务端鉴权插件识别。 + +在Nacos的Java客户端默认自带两个实现: + +- 使用`username`,`password`和`accessToken`的简易鉴权实现; +- 使用`accessKey`和`secretKey`的阿里云鉴权实现; + +### Nacos简易鉴权实现 + +当构造客户端实例时传入的properties中带有`username`,`password`时,客户端会使用简易鉴权实现插件注入身份信息; +如: +```java +Properties properties = new Properties(); +properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); +properties.setProperty(PropertyKeyConst.USERNAME, "nacos"); +properties.setProperty(PropertyKeyConst.PASSWORD, "nacos"); +NamingFactory.createNamingService(properties); +ConfigFactory.createConfigService(properties); +``` + +该插件会异步地通过`username`,`password`进行登录,获取登录成功后的`accessToken`,并将`accessToken`注入到所有客户端请求中,开发者可以根据`accessToken`在实现的服务端插件中进行身份验证及后续的权限验证。 + +### 阿里云鉴权实现 + +当properties中带有`accessKey`和`secretKey`时,则会使用阿里云鉴权实现注入身份信息,如: + ```java + Properties properties = new Properties(); + properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); + properties.setProperty(PropertyKeyConst.ACCESS_KEY, "nacos"); + properties.setProperty(PropertyKeyConst.SECRET_KEY, "nacos"); + NamingFactory.createNamingService(properties); + ConfigFactory.createConfigService(properties); + ``` + +该插件会根据`accessKey`和`secretKey`以及请求的资源内容,自动生成对应的请求签名,并注入到请求中,根据资源类型的不同,请求中的身份信息关键字可能不同: + +|类型|身份关键字|描述| +|-----|-----|-----| +|NamingService|ak|accessKey| +|NamingService|signature|注册中心模块的签名信息| +|NamingService|data|签名数据,主要是时间戳| +|ConfigService|Spas-AccessKey|accessKey| +|ConfigService|Spas-Signature|配置中心模块的签名信息| +|ConfigService|Timestamp|请求的时间戳| +|ConfigService|Spas-SecurityToken|临时token(启用阿里云STS功能时使用)| + +开发者可以根据以上信息,在实现的服务端插件中进行身份验证及后续的权限验证。 + +### 其他自定义插件 + +考虑到开发者的鉴权插件可能有自定义的身份信息关键字,因此Nacos的Java客户端同样可以使用SPI方式注入对应的插件实现。 + +开发Nacos客户端鉴权插件,首先需要依赖鉴权插件的相关API + +```xml + + com.alibaba.nacos + nacos-auth-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本 + +随后实现`com.alibaba.nacos.plugin.auth.spi.client.ClientAuthService`接口, 并将您的实现添加到SPI的services当中。 + +接口中需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|---| +|setServerList|List<String>,Nacos服务端地址列表|void|初始化时会调用此接口注入Nacos的服务列表,方便插件访问nacos服务端,如调用登录接口等| +|setNacosRestTemplate|NacosRestTemplate,Nacos的http客户端|void|初始化时会调用此接口注入Nacos的http客户端,方便插件访问nacos服务端,如调用登录接口等| +|login|Properties,即初始化Nacos客户端时传入的参数|boolean|登录接口,主要执行的是身份信息的转换工作,如`username`,`password`转换为`accessToken`| +|getLoginIdentityContext|Resource|IdentityContext|获取经过登录接口转换后的身份信息,客户端会将该返回对象的内容全部注入到请求中| + +您也可以选择继承`com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService`,该父类默认实现了`setServerList`和`setNacosRestTemplate`。 + +将开发完成的客户端插件打包成jar/zip,放入到您应用的classpath中即可自动生效。 + +### 其他语言客户端鉴权插件 + +待社区贡献。 diff --git a/src/content/docs/v3.0/zh-cn/plugin/config-change-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/config-change-plugin.md new file mode 100644 index 00000000000..ed10f6ae29a --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/config-change-plugin.md @@ -0,0 +1,138 @@ +--- +title: 配置变更 +keywords: [配置变更,配置审计,配置格式校验,webhook] +description: Nacos 支持配置变更插件,可支持配置审计、配置格式校验、及变更后webhook回调 +sidebar: + order: 8 +--- + +# 配置变更插件 + +社区中一直以来都希望Nacos配置中心能在配置发生变更时,通知一些特定系统,用于发送记录、警告等审计功能。在2.3.0版本前,只能通过模拟Nacos客户端订阅配置的方式,对核心配置的变更操作进行订阅,在收到变更通知后,进行发送记录、警告等功能的执行。 + +这种实现方式有几个比较大的问题,第一是监听的配置需要逐个添加,难以对所有配置变更进行获取;第二是只能在配置变更后执行功能逻辑,无法做到前置的操作,如格式校验,白名单校验等。 + +因此Nacos在2.3.0版本后,支持通过[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)注入配置变更插件,允许用户通过自定义插件的方式,对配置变更前,和变更完成后分别执行一些自定义逻辑,如格式校验,白名单校验,webhook等。 + +## 配置变更插件中的概念 + +Nacos的配置变更插件,参考了面向切面编程AOP的设计思想,将配置的变更操作(如新增,更新,删除)作为`切点(PointCut)`,并在这些切点`前(Before)`和`后(After)`织入插件。 + +### 配置变更切点(ConfigChangePointCutTypes) + +Nacos根据配置变更的行为和来源,将配置变更操作在`com.alibaba.nacos.plugin.config.constants.ConfigChangePointCutTypes`中定位为了数个`配置变更切点(ConfigChangePointCutTypes)`,具体内容如下: + +|切点名称|描述|起始版本| +|-----|-----|-----| +|PUBLISH_BY_HTTP|配置通过HTTP接口进行发布,包含了创建配置及修改配置|2.3.0| +|PUBLISH_BY_RPC|配置通过GRPC接口进行发布,包含了创建配置及修改配置|2.3.0| +|REMOVE_BY_HTTP|配置通过HTTP接口进行删除|2.3.0| +|REMOVE_BY_RPC|配置通过GRPC接口进行删除|2.3.0| +|IMPORT_BY_HTTP|配置通过HTTP接口进行导入|2.3.0| +|REMOVE_BATCH_HTTP|配置通过HTTP接口进行批量删除|2.3.0| + +### 配置变更织入类型(ConfigChangeExecuteTypes) + +Nacos的配置变更插件需要在`配置变更切点`之前或之后进行执行,即需要选择`配置变更织入类型(ConfigChangeExecuteTypes)`,定义在`com.alibaba.nacos.plugin.config.constants.ConfigChangeExecuteTypes`中,具体内容如下: + +|织入类型|描述|起始版本| +|-----|-----|-----| +|EXECUTE_BEFORE_TYPE|插件实现在`配置变更切点`之**前**执行|2.3.0| +|EXECUTE_AFTER_TYPE|插件实现在`配置变更切点`之**后**执行|2.3.0| + +## 插件开发 + +开发Nacos服务端配置变更插件,首先需要依赖配置变更插件的的相关API + +```xml + + com.alibaba.nacos + nacos-config-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本,`2.3.0`及以上。 + +随后实现`com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService`接口,该接口需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|-----| +|getServiceType|void|String|插件的名称,用于区分不同类型的插件实现| +|getOrder|void|int|插件的执行顺序,配置变更插件采用链式插件设计,多个插件实现时会按照顺序执行,getOrder越小,顺序越靠前| +|executeType|void|ConfigChangeExecuteTypes|插件实现的`配置变更织入类型`| +|pointcutMethodNames|void|ConfigChangePointCutTypes[]|插件实现织入的`配置变更切点`| +|execute|ConfigChangeRequest,ConfigChangeResponse|void|实际插件执行的逻辑| + +其中`ConfigChangeRequest`和`ConfigChangeResponse`分别为执行逻辑时传入的内容及执行结果, + +`ConfigChangeRequest`的具体内容如下: + +|字段名|字段类型|描述| +|-----|-----|-----| +|requestType|ConfigChangePointCutTypes|本次配置变更的切点类型| +|requestArgs|HashMap|本次配置变更的实际参数,主要包含有`namespace`,`group`,`dataId`,`content`等内容,不同的切点类型参数存在不同| + +`ConfigChangeResponse `的具体内容如下: + +|字段名|字段类型|描述| +|-----|-----|-----| +|responseType|ConfigChangePointCutTypes|本次配置变更的切点类型| +|isSuccess|boolean|执行是否成功,当返回值为`false`时,将会拦截本次配置变更,并直接返回失败的结果| +|retVal|Object|返回内容,预留字段,暂未启用| +|msg|String|执行结果信息,在`isSuccess`为`false`时获取,用于返回给客户端的信息| +|args|Object[]|配置变更操作的执行参数,在`EXECUTE_BEFORE_TYPE`的插件类型时生效,可用于修改实际执行的配置变更时的内容,如将content中的某些内容修改为其他值| + +### 加载插件 + +插件开发完成后,需要打包成jar/zip,放置到nacos服务端的classpath中,如果您不知道如何修改classpath,请直接放置到`${nacos-server.path}/plugins`下 + +放置后,需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +### 所启用的Nacos的配置变更插件的名称,与com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService 的getServiceType 返回值对应 +nacos.core.config.plugin.${configChangePluginName}.enabled=true +``` + +随后重启nacos集群,启动完成后,可在`${nacos-server.path}/logs/nacos.log`日志中看到如下日志。 + +```text +[ConfigChangePluginManager] Load ${className}(${classFullName}) ConfigChangeServiceName(${configChangePluginName}) successfully. +``` + +### 插件自定义参数传递 + +部分插件可能希望通过配置文件设置一些参数,自定义插件可以通过修改`${nacos-server.path}/conf/application.properties`中的以下配置完成: + +```properties +### 所启用的Nacos的配置变更插件的名称,与com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService 的getServiceType 返回值对应 +nacos.core.config.plugin.${configChangePluginName}.${propertyKey}=${propertyValue} +``` + +之后能在`ConfigChangeRequest`中,通过下述方法获取该参数: + +```Java +final Properties properties = (Properties) configChangeRequest.getArg(ConfigChangeConstants.PLUGIN_PROPERTIES); +final String ${propertyKey} = properties.getProperty("${propertyKey}"); +``` + +## 插件DEMO实现 + +在[nacos-group/nacos-plugin](https://github.com/nacos-group/nacos-plugin)中,有一个demo的配置变更插件的实现,该demo插件实现了对配置内容格式的校验、配置导入名称白名单的校验、以及变更后回调webhook。打包成jar/zip,放置到nacos服务端的classpath中,在`${nacos-server.path}/conf/application.properties`中的加入以下配置: + +```properties +# webhook +#nacos.core.config.plugin.webhook.enabled=true +# It is recommended to use EB https://help.aliyun.com/document_detail/413974.html +#nacos.core.config.plugin.webhook.url=http://${webhookIp}:${webhookPort}/${webhookUri}?token=*** +# The content push max capacity ,byte +#nacos.core.config.plugin.webhook.contentMaxCapacity=102400 + +# whitelist +#nacos.core.config.plugin.whitelist.enabled=true +# The import file suffixs +#nacos.core.config.plugin.whitelist.suffixs=xml,text,properties,yaml,html + +# fileformatcheck,which validate the import file of type and content +#nacos.core.config.plugin.fileformatcheck.enabled=true +``` \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/plugin/config-encryption-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/config-encryption-plugin.md new file mode 100644 index 00000000000..f328e4cc473 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/config-encryption-plugin.md @@ -0,0 +1,78 @@ +--- +title: 配置加密 +keywords: [AES,encryption,配置加密] +description: 配置加密 +sidebar: + order: 3 +--- + +> 为保证用户敏感配置数据的安全,Nacos 提供了配置加密的新特性。降低了用户使用的风险,也不需要再对配置进行单独的加密处理。 + +# 前提条件 + +**版本:** + +老版本暂时不兼容,目前只基于2.x版本进行了改造,推荐版本 > 2.0.4。 + +**内嵌数据库启动:** + +数据库表 config_info、config_info_beta、his_config_info中需要新增字段 `encrypted_data_key` ,用来存储每一个配置项加密使用的秘钥。新版本的默认创建表的sql中已经添加该字段。 + +如果之前使用过内嵌数据库的单机模式启动,则需要删除 nacos/data 文件夹,在重新启动会重新创建表。 + +**MySQL启动:** + +数据库表 config_info、config_info_beta、his_config_info中需要新增字段 `encrypted_data_key` ,用来存储每一个配置项加密使用的秘钥。新版本的默认创建表的sql中已经添加该字段。 + +> 对于目前已经搭建好的 Nacos 使用以下 sql 将字段添加到对应的表中: +> +> ``ALTER TABLE table_name ADD COLUMN `encrypted_data_key` text NOT NULL COMMENT '秘钥'`` + +# 插件化实现 + +![](https://tva1.sinaimg.cn/large/008i3skNly1gvsu112vnnj314b0u0764.jpg) + +通过 SPI 的机制抽象出加密和解密的操作,Nacos 默认提供 `AES` 的实现。用户也可以自定义加解密的实现方式。具体的实现在 [nacos-plugin](https://github.com/nacos-group/nacos-plugin) 仓库。 + +在 Nacos 服务端启动的时候就会加载所有依赖的加解密算法,然后通过发布配置的 `dataId` 的前缀来进行匹配是否需要加解密和使用的加解密算法。 + +客户端发布的配置会在客户端通过filter完成加解密,也就是配置在传输过程中都是密文的。而控制台发布的配置会在服务端进行处理。 + +# 如何使用 + +Nacos 加解密插件是可插拔的,有没有都不影响 Nacos 的核心功能的运行。如果想要使用 Naocs 的配置加解密功能需要单独引用加密算法的实现。客户端和服务端都通过添加以下依赖来使用 AES 加解密算法,服务端推荐添加到 config 模块下。 + +``` + + com.alibaba.nacos + nacos-aes-encryption-plugin + ${nacos-aes-encryption-plugin.version} + +``` +${nacos-aes-encryption-plugin.version} 可以获取插件的最新版本。 + +> 目前插件需要自己编译,并未上传至maven中央仓库 + +# 如何编译 + +编译插件之前需要先编译`nacos`并安装至本地仓库. +1. `git clone git@github.com:alibaba/nacos.git` +2. `cd nacos && mvn -B clean package install -Dmaven.test.skip=true` + +> 若出现`revision`变量无法解析,请更新`maven`至最新版本 + +3. `git clone git@github.com:nacos-group/nacos-plugin.git` +4. `mvn install` + +建议上传到公司的maven仓库 + +# 创建加密配置 +- 打开 Nacos 控制台,点击新建配置。 + + ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h0cxaklw10j21g20u0ac8.jpg) +- 配置前缀使用cipher-[加密算法名称]-dataId来标识这个配置需要加密,系统会自动识别并加密。例如使用 AES 算法来解密配置:cipher-aes-application-dev.yml。 + + ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h0cxs40s2tj21b40u0whw.jpg) +- 点击保存,查看数据库 + + ![](https://tva1.sinaimg.cn/large/e6c9d24ely1h0cxwhdc77j21xm0bumz2.jpg) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/plugin/control-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/control-plugin.md new file mode 100644 index 00000000000..50ef82af6b4 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/control-plugin.md @@ -0,0 +1,222 @@ +--- +title: 反脆弱 +keywords: [反脆弱,限流,连接数限制,TPS] +description: Nacos 支持反脆弱插件,避免高压下的集群容量问题。 +sidebar: + order: 7 +--- + +# 反脆弱插件 + +Nacos 从2.3.0版本开始,支持通过[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)的方式注入反脆弱相关插件,并在`application.properties`配置文件中选择某一种插件实现作为实际反脆弱能力。本文档会详细介绍如何实现一个反脆弱插件和如何使其生效。 + +## 反脆弱插件中的概念 + +反脆弱是对访问服务端的**某种资源**的**频率和次数**达到一定程度时进行的限制访问的策略,用于保护服务端在高压情况下能快速拒绝请求,防止过多的资源访问导致服务端资源耗尽引起的大面积不可用;Nacos反脆弱插件,将信息主要抽象为`监控点`和`反脆弱规则`。 + +### 监控点(ControlPoint) + +监控点对应的请求服务端时所占用的资源的映射,目前主要针对的是`连接(Connection)`以及`每秒请求数(TPS)`。 + +- 连接(Connection)监控点主要监控Nacos 服务端中使用Nacos2.X客户端的长连接数量以及使用Nacos1.X客户端的配置长轮询数量,两者独立监控。 +- 每秒请求数(TPS)监控点主要是监控Nacos 服务端中各核心接口被访问的频率,同类型的操作接口会被视为相同的监控点,如注册服务的v1接口和v2接口,具体的每秒请求数(TPS)监控点可查看本文档下文[监控点名称](#1.1)。 + +### 反脆弱规则(ControlRule) + +反脆弱规则是针对每个监控点而执行的不同的限制规则,具体又分为`连接数规则(ConnectionControlRule)`和`每秒请求数规则(TpsControlRule)` + +`连接数规则(ConnectionControlRule)`主要包含如下内容: + +|字段名|类型|描述| +|-----|-----|-----| +|countLimit|int|连接数总数限制,默认为-1,不限制| +|monitorIpList|Set<String>|trace监控的Ip列表,用于详细观察对应ip的连接做了哪些操作,添加后,对应ip的连接请求会被详细打印在remote-digest.log日志中| + +`每秒请求数规则(TpsControlRule)`主要包含如下内容: + +|字段名|类型|描述| +|-----|-----|-----| +|pointName|String|规则所对应的监控点名称| +|pointRule|RuleDetail|规则内容的具体细节| + +其中`RuleDetail`又包含如下内容: + +|字段名|类型|描述| +|-----|-----|-----| +|ruleName|String|规则的名称,区别于监控点名称,同一个监控点可以有多个规则名| +|maxCount|int|TPS总数限制,默认为-1,不限制| +|period|TimeUnit|规则生效的周期,即统计到秒级/分钟级等,默认`TimeUnit.SECONDS`秒级| +|monitorType|String|监控类型,取值为`monitor`或`intercept`,对应为监控模式(只统计和打印tps,即使触发规则也不拦截)和拦截模式| + +## 插件开发 + +开发Nacos服务端反脆弱插件,首先需要依赖反脆弱插件的相关API + +```xml + + com.alibaba.nacos + nacos-control-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本,`2.3.0`及以上。 + +随后继承`com.alibaba.nacos.plugin.control.connection.ConnectionControlManager`抽象类和`com.alibaba.nacos.plugin.control.tps.TpsControlManager`抽象类,实现缺失的方法;然后实现`com.alibaba.nacos.plugin.control.spi.ControlManagerBuilder` 接口,创建上述实现的两个抽象类;最后将您的实现添加到SPI的services当中。 + +`com.alibaba.nacos.plugin.control.connection.ConnectionControlManager`需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|-----| +|applyConnectionLimitRule|ConnectionControlRule|void|应用新的连接数规则| +|check|ConnectionCheckRequest|ConnectionCheckResponse|判断是否命中连接数规则,如果ConnectionCheckResponse中的sucess为false,将会拒绝新连接的建立| + +`com.alibaba.nacos.plugin.control.tps.TpsControlManager`需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|-----| +|registerTpsPoint|String|void|注册TPS监控点,Nacos服务会在启动时向插件注册当前的TPS监控买点,入参为TPS监控点的名字,具体的监控点名称可查看本文档下文[监控点名称](#1.1);插件需要在方法内,维护一个用于记录TPS和规则内容的`TpsBarrier`,详情查看[自定义TPS时间窗口](#1.2)。| +|applyTpsRule|String,TpsControlRule|void|应用新的TPS规则,根据TPS监控点名称关联及更新。| +|check|TpsCheckRequest|TpsCheckResponse|判断是否命中TPS规则,如果TpsCheckResponse中的sucess为false,将会拒绝新的请求。| + +`com.alibaba.nacos.plugin.control.spi.ControlManagerBuilder` 需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|-----| +|getName|void|String|插件的名称,和配置文件中指定的类型进行匹配,使用命中的的插件。| +|buildConnectionControlManager| void |ConnectionControlManager|创建插件对应的`ConnectionControlManager `实现,为null时会使用`no limit`实现。| +|buildTpsControlManager| void |TpsControlManager|创建插件对应的`TpsControlManager`实现,为null时会使用`no limit`实现。| + +### 加载插件 + +插件开发完成后,需要打包成jar/zip,放置到nacos服务端的classpath中,如果您不知道如何修改classpath,请直接放置到`${nacos-server.path}/plugins`下 + +放置后,需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +### 所启用的Nacos的反脆弱插件的名称,与com.alibaba.nacos.plugin.control.spi.ControlManagerBuilder 的getName 返回值对应 +nacos.plugin.control.manager.type=${controlPluginName} +``` + +随后重启nacos集群,启动完成后,可以从`${nacos-server.path}/logs/plugin-control.log`中看到如下日志: + +```text +Found control manager plugin of name=${controlPluginName} +Build connection control manager, class=${your plugin ConnectionControlManager class} +Build tps control manager, class=${your plugin TpsControlManager class} +``` + +## 使用Nacos自带的反脆弱插件 + +Nacos2.3.0版本起,自带一个简易的反脆弱插件实现,可以做到对Nacos服务端的连接数及指定接口TPS进行限制。 + +### 启用Nacos自带的反脆弱插件 + +需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +nacos.plugin.control.manager.type=nacos +``` + +### 设置反脆弱规则 + +可通过创建和修改反脆弱规则文件的方式,修改和设置反脆弱规则,默认反脆弱插件的规则是通过json格式定义的;例如想要设置连接限制为100,可执行如下操作: + +```shell +mkdir -p ${nacos.home}/data/connection/ +echo '{"countLimit": 100}' > ${nacos.home}/data/connection/limitRule +``` +随后重启Nacos节点即可。 + +又例如想要设置配置查询接口的TPS为100,可执行如下操作: + + ```shell + mkdir -p ${nacos.home}/data/tps/ + # ConfigQuery 为配置查询接口的监控点名称(pointName) + echo '{"pointName":"ConfigQuery","pointRule":{"maxCount":100,"monitorType":"intercept"}}' > ${nacos.home}/data/tps/ConfigQuery + ``` +随后重启Nacos节点即可。 + +其他更多反脆弱规则,以及具体的反脆弱监控点名称,请查看下文[监控点名称](#1.1)。 + +### 反脆弱规则存储位置 + +Nacos自带的简易反脆弱插件实现,Nacos服务端会通过本地文件系统,存储和读取反脆弱规则,默认所在目录的为`${nacos.home}/data/connection`及`${nacos.home}/data/tps`中,如果想将规则文件更换目录存储,可以在`${nacos-server.path}/conf/application.properties`中修改以下配置: + +```properties +nacos.plugin.control.rule.local.basedir=${expectedDir} +``` + +这样规则将会被存储在`${expectedDir}/data/connection`及`${expectedDir}/data/tps`中。 + +

+ +## 当前支持的监控点名称 + +|监控点名称|对应内容|描述|起始版本| +|-----|-----|-----|-----| +|connection|节点总连接数|指定节点最大可支持连接数限制|2.3.0| +|ConfigPublish|配置发布接口TPS|指定节点最大可支持配置发布的TPS限制,同时包含了通过http访问和grpc访问的来源|2.3.0| +|ConfigQuery|配置查询接口TPS|指定节点最大可支持配置查询的TPS限制,同时包含了通过http访问和grpc访问的来源|2.3.0| +|ConfigRemove|配置移除接口TPS|指定节点最大可支持配置移除的TPS限制,同时包含了通过http访问和grpc访问的来源|2.3.0| +|ConfigListen|配置监听接口TPS|指定节点最大可支持配置监听的TPS限制,仅包含通过grpc访问的来源|2.3.0| +|RemoteNamingInstanceRegisterDeregister|服务实例注册及注销接口TPS|服务实例注册或注销的TPS限制,仅包含通过grpc访问的来源|2.3.0| +|RemoteNamingInstanceBatchRegister|服务实例批量注册接口TPS|服务实例批量注册的TPS限制,仅包含通过grpc访问的来源|2.3.0| +|RemoteNamingServiceListQuery|服务列表查询接口TPS|服务列表查询的TPS限制,仅包含通过grpc访问的来源|2.3.0| +|RemoteNamingServiceQuery|服务查询接口TPS|服务查询的TPS限制,仅包含通过grpc访问的来源|2.3.0| +|RemoteNamingServiceSubscribeUnSubscribe|服务订阅和取消订阅接口TPS|服务订阅和取消订阅的TPS限制,仅包含通过grpc访问的来源|2.3.0| +|NamingInstanceRegister|服务实例注册接口TPS|服务实例注册的TPS限制,仅包含通过http访问的来源|2.3.0| +|NamingInstanceDeregister|服务实例注销接口TPS|服务实例注销的TPS限制,仅包含通过http访问的来源|2.3.0| +|NamingInstanceUpdate|服务实例元数据更新接口TPS|服务实例更新的TPS限制,仅包含通过http访问的来源|2.3.0| +|NamingInstanceMetadataUpdate|服务实例元数据批量更新接口TPS|服务实例元数据批量更新的TPS限制,仅包含通过http访问的来源|2.3.0| +|NamingServiceSubscribe|服务实例查询及订阅接口TPS|服务订阅及查询的TPS限制,仅包含通过http访问的来源|2.3.0| +|NamingInstanceQuery|单个服务实例查询接口TPS|单个服务实例查询的TPS限制,仅包含通过http访问的来源|2.3.0| +|HttpHealthCheck|服务实例心跳续约接口TPS|服务实例心跳续约的TPS限制,仅包含通过http访问的来源|2.3.0| +|NamingServiceRegister|服务创建接口TPS|服务创建的TPS限制,与`NamingInstanceRegister`不同,此监控点表示的是创建空服务接口所对应的TPS,仅包含通过http访问的来源|2.3.0| +|NamingServiceDeregister|服务删除接口TPS|服务删除的TPS限制,与`NamingInstanceDeregister `不同,此监控点表示的是删除服务接口所对应的TPS,仅包含通过http访问的来源|2.3.0| +|NamingServiceQuery|服务查询接口TPS|服务查询的TPS限制,与`NamingInstanceQuery `不同,此监控点表示的是查询服务信息接口所对应的TPS,仅包含通过http访问的来源|2.3.0| +|NamingServiceListQuery|服务列表查询接口TPS|服务列表查询的TPS限制,与`NamingServiceSubscribe `不同,此监控点表示的是服务列表查询接口所对应的TPS,仅包含通过http访问的来源|2.3.0| +|NamingServiceUpdate|服务元数据更新接口TPS|服务元数据更新的TPS限制,与`NamingInstanceUpdate `不同,此监控点表示的是服务元数据更新接口所对应的TPS,仅包含通过http访问的来源|2.3.0| + +## 反脆弱插件进阶开发 + +Nacos反脆弱插件还支持一些进阶式的拓展,以满足对此方面有更高要求的开发者和用户。 + +### 反脆弱规则外部存储 + +Nacos反脆弱插件的默认情况下,仅支持通过本地文件系统来存储和修改单节点的反脆弱规则,对于一些集群规模较大或集群较多的用户,逐个节点进行调整会消耗大量时间和操作;同时本地文件系统在许多容器化环境中,存在磁盘挂载和持久化的问题。因此Nacos反脆弱插件允许增加一个可选的外部存储进行反脆弱规则的统一存储和下发,外部存储可有插件自行实现对接,例如`数据库`,`配置中心`等。 + +要实现反脆弱规则的外部存储,需要在开发插件时,实现`com.alibaba.nacos.plugin.control.spi.ExternalRuleStorageBuilder`接口,并随插件jar文件一起放置在`${nacos-server.path}/plugins`下。 + +放置后,需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +nacos.plugin.control.rule.external.storage=${controlPluginName} +``` + +随后重启Nacos节点即可。 + +### 动态加载反脆弱规则 + +在自定义插件实现中,可以通过两种方式进行反脆弱规则的动态加载: + +1. 调用`com.alibaba.nacos.plugin.control.ControlManagerCenter#reloadTpsControlRule`方法或`com.alibaba.nacos.plugin.control.ControlManagerCenter#reloadConnectionControlRule`方法。 +2. 通过`NotifyCenter.publishEvent()`发布`ConnectionLimitRuleChangeEvent` 或`TpsControlRuleChangeEvent`事件。 + +### 反脆弱规则的自定义格式解析 + +Nacos 默认使用`Json`格式作为反脆弱规则的文本格式,插件开发者也可以使用其他的格式,如`Yaml`或其他自定义格式进行解析。 + +重写`com.alibaba.nacos.plugin.control.connection.ConnectionControlManager#buildConnectionControlRuleParser`及`com.alibaba.nacos.plugin.control.tps.TpsControlManager#buildTpsControlRuleParser`,实现自定义格式规则解析器`RuleParser`,Nacos将使用自定义的规则解析器`RuleParser`进行规则文本的解析。 + +同时,也可以解析成默认自定义规则的增强规则,配合自定义插件的定制逻辑实现更高程度的反脆弱控制。 + +

+ +### 自定义TPS时间窗口 + +众所周知,在统计TPS时,存在时间窗口算法的区别,不同的时间窗口对TPS的统计结果会有较大的区别。 + +Nacos默认使用简单的同秒统计方式,即按照时钟的秒来进行统计。对于大多数场景来说是足够使用的,但对于一些精确度要求高的用户而言,可能需要使用滑动窗口等更精确的方式进行统计。 + +此时需要插件开发者,继承`com.alibaba.nacos.plugin.control.tps.barrier.TpsBarrier`和`com.alibaba.nacos.plugin.control.tps.barrier.RuleBarrier`,自定义实现TPS的时间窗口和统计方式。并重写`com.alibaba.nacos.plugin.control.tps.TpsControlManager#buildTpsBarrierCreator`,在初始化插件和动态加载反脆弱规则时,生成对应的自定义实现。 \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/plugin/custom-environment-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/custom-environment-plugin.md new file mode 100644 index 00000000000..1ab79dccfd0 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/custom-environment-plugin.md @@ -0,0 +1,58 @@ +--- +title: 自定义环境变量 +keywords: [自定义环境变量,自定义配置,数据库密码加密] +description: Nacos 自定义环境变量插件,可自定义扩展服务端配置等功能,例如数据库密码加密。 +sidebar: + order: 6 +--- + +# 自定义环境变量插件 + +Nacos从2.2.0版本开始,可通过[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)机制注入自定义环境变量实现插件,在插件中自定义nacos的配置,并按照您期望的方式进行处理(如数据库密码加密)。本文档详细介绍一个自定义环境变量插件如何实现以及如何使其生效。 + +> 注意: +> 目前自定义环境变量插件处于Beta测试阶段,其API及接口方法定义可能会在后续版本升级而有较大修改,请注意您的插件适用版本。 + +## 插件开发 + +开发Nacos服务端自定义环境变量插件,首先需要依赖自定义环境变量插件的相关API + +```xml + + com.alibaba.nacos + nacos-custom-environment-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本 + +随后实现`com.alibaba.nacos.plugin.environment.spi.CustomEnvironmentPluginService`接口, 并将您的实现添加到SPI的services当中。 + +接口中需要实现的方法如下: + +| 方法名 | 入参内容 | 返回内容 | 描述 | +|-------------|-----------------------|-----------------------|----------------------------------------------------| +| pluginName | `void` | `String` | 插件的名称。 | +| propertyKey | `void` | `Set` | 需要对服务端自定义的配置项名称。 | +| order | `void` | `Integer` | 插件的优先级,数字越大优先级越高,多个插件同时自定义同一个配置项,优先级高的插件将会覆盖优先级低的。 | +| customValue | `Map` | `Map` | 入参为propertyKey对应的配置项的值,出参为自定义之后的配置项值。 | + +在[nacos-group/nacos-plugin](https://github.com/nacos-group/nacos-plugin)中,有一个demo的自定义环境变量插件实现,该demo插件实现了将数据库密码Base64解密, +于是您可以在`application.properties`配置文件中设置密文数据库密码而不是明文密码。 + +# 如何使用 +插件开发完成后,需要打包成jar/zip,放置到nacos服务端的classpath中,如果您不知道如何修改classpath,请直接放置到`${nacos-server.path}/plugins`下 + +放置后,需要修改`${nacos-server.path}/conf/application.properties`中的以下配置 + +```properties +### 开启自定义环境变量功能 +nacos.custom.environment.enabled=true +``` + +随后重启nacos集后,可以从`${nacos-server.path}/logs/nacos.log`中看到如下日志: + +```text +[CustomEnvironmentPluginManager] Load customEnvironmentPluginService(xxx) customEnvironmentPluginName(xxx) successfully.. +``` diff --git a/src/content/docs/v3.0/zh-cn/plugin/datasource-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/datasource-plugin.md new file mode 100644 index 00000000000..84f65b6bca6 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/datasource-plugin.md @@ -0,0 +1,65 @@ +--- +title: 多数据源 +keywords: [MySQL,Derby,多数据源] +description: 多数据源 +sidebar: + order: 4 +--- + +# 多数据源插件 +Nacos从2.2.0版本开始,可通过SPI机制注入多数据源实现插件,并在引入对应数据源实现后,便可在Nacos启动时通过读取`application.properties`配置文件中`spring.datasource.platform`配置项选择加载对应多数据源插件.本文档详细介绍一个多数据源插件如何实现以及如何使其生效。 + +> 注意: +> 目前多数据源插件处于Beta测试阶段,其API及接口方法定义可能会在后续版本升级而有较大修改,请注意您的插件适用版本。 + +# 插件化实现 +在原来的Config模块中,所有的SQL操作的执行是通过直接使用JdbcTemplate执行固定SQL语句的形式,使得SQL语句与业务逻辑高度耦合,并且只支持Derby与MySQL两种数据源,原有Config模块架构如下。 + +![](/img/config-old-datasource.png) + +现在的多数据源插件通过SPI机制,将SQL操作按照数据表进行抽象出多个Mapper接口,Mapper接口的实现类需要按照不同的数据源编写对应的SQL方言实现; +现在插件默认提供Derby以及MySQL的Mapper实现,可直接使用;而其他的数据源则需要用户使用数据源插件进行加载,其改造后架构图如下。 + +![](/img/config-datasource-plugin.png) + +# 如何使用 +1. 用户查询当前Nacos是否支持所需数据源,Nacos默认提供Derby以及MySQL的实现,若暂未支持可参考下面插件编写者如何开发步骤开发插件自己使用或贡献; +2. 在`application.properties`配置文件中将`spring.datasource.platform`修改为对应的数据源名称,并配置数据源相关参数; +3. 然后编译运行则可支持此数据源; + +# 插件编写者如何开发 +1. 引入`nacos-datasource-plugin`依赖 +2. 实现`com.alibaba.nacos.plugin.datasource.mapper`包下数据表对应Mapper接口中的特殊SQL方法,主要是涉及分页等方言差别,可参考`com.alibaba.nacos.plugin.datasource.impl`下Derby以及MySQL的实现,只需实现对应接口即可。接口与表对应关系如下: + +| 数据库表 | Mapper| +| ----------- | ----------- | +|config_info_aggr| ConfigInfoAggrMapper | +|config_info_beta| ConfigInfoBetaMapper | +|config_info|ConfigInfoMapper| +|config_info_tag|ConfigInfoTagMapper| +|config_tags_relation|ConfigTagsRelationMapper| +|his_config_info|HistoryConfigInfoMapper| + +3. 编写SPI配置文件,其名字为`com.alibaba.nacos.plugin.datasource.mapper.Mapper`,写入实现Mapper接口的类,可参考config模块中Derby与MySQL配置文件。 +4. 插件使用者则可以通过依赖此插件,达到实现对应数据源操作的效果 +5. 编译运行 + +# 如何编译 +编译插件之前需要先编译`nacos`并安装至本地仓库. +1. `git clone git@github.com:alibaba/nacos.git` +2. `cd nacos && mvn -B clean package install -Dmaven.test.skip=true` + +> 若出现`revision`变量无法解析,请更新`maven`至最新版本 + +3. `git clone #{对应数据源插件实现Git地址}` +4. `mvn install` + +建议上传到公司的maven仓库 + +# 未来方案 +未来的版本更新如下: +- [ ] 继续细分SQL,在现有的基础上,减少SQL语句的同时,对动态SQL的实现更加友好; +- [ ] 抽离不同数据源之间的差异列表,并通过配置文件或配置类的方式进行差异列表的替换,方便插件编写者编写插件; + +# 其他数据源的实现 +待补充 diff --git a/src/content/docs/v3.0/zh-cn/plugin/trace-plugin.md b/src/content/docs/v3.0/zh-cn/plugin/trace-plugin.md new file mode 100644 index 00000000000..e290bc8604f --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/plugin/trace-plugin.md @@ -0,0 +1,199 @@ +--- +title: 轨迹追踪 +keywords: [轨迹追踪,推送轨迹,变更轨迹] +description: Nacos 支持轨迹追踪插件,可自定义扩展制作推送轨迹等功能,帮助运维人员快速定位问题。 +sidebar: + order: 5 +--- + +# 轨迹追踪插件 + +Nacos从2.2.0版本开始,可通过[SPI](https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html)机制注入轨迹追踪实现插件,在插件中订阅并处理追踪事件,并按照您期望的方式进行处理(如打日志,写入存储等)。本文档详细介绍一个轨迹追踪插件如何实现以及如何使其生效。 + +> 注意: +> 目前轨迹追踪插件处于Beta测试阶段,其API及接口方法定义可能会在后续版本升级而有较大修改,请注意您的插件适用版本。 +> +> Nacos 的轨迹追踪不同于一般意义的链路追踪, 主要目的是追踪和记录一些Nacos的相关操作,如服务注册、注销、推送、状态变更等,并非追踪微服务间的相互访问链路,如需要监控追踪服务间的相互访问,请使用对应的链路追踪项目。 + +## 轨迹追踪插件中的概念 + +### 追踪事件 TraceEvent + +Nacos 在关键操作的链路中进行了埋点,定义了一系列的追踪事件`TraceEvent`, 将多个针对相同资源(如服务,配置等)的追踪事件串起来之后,便得到了该资源的轨迹。 + +在追踪事件`TraceEvent`中,会包含如下内容: + +|字段名|描述| +|-----|---| +|type|事件的类型,由具体事件定义| +|eventTime|事件发生的时间| +|namespaceId|事件对应资源的命名空间ID| +|group| 事件对应资源的分组名| +|name | 事件对应资源的资源名,如服务名或配置的dataId| + +目前Nacos中已经定义的子追踪事件类型有: + +|事件名|描述|详情| +|-----|---|---| +|RegisterInstanceTraceEvent|服务实例注册事件,主要发生于注册服务提供者时|[事件详情](#1.1)| +|DeregisterInstanceTraceEvent|服务实例注销事件,主要发生于注销服务提供者时|[事件详情](#1.2)| +|RegisterServiceTraceEvent|服务注册事件,不同于`服务实例注册事件`,主要发生于创建空服务时|[事件详情](#1.3)| +|DeregisterServiceTraceEvent|服务注销事件,不同于`服务实例注销事件`,主要发生于删除空服务时|[事件详情](#1.4)| +|SubscribeServiceTraceEvent|服务订阅事件,主要发生于订阅服务时|[事件详情](#1.5)| +|UnsubscribeServiceTraceEvent|取消服务订阅事件,主要发生于取消订阅服务时|[事件详情](#1.6)| +|PushServiceTraceEvent|服务推送事件,主要发生于发生服务推送时|[事件详情](#1.7)| +|HealthStateChangeTraceEvent|服务实例健康状态变更事件,主要发生于实例因心跳/健康检查而导致实例健康状态变化时|[事件详情](#1.8)| + +## 插件开发 + +开发Nacos服务端轨迹追踪插件,首先需要依赖轨迹追踪插件的相关API + +```xml + + com.alibaba.nacos + nacos-trace-plugin + ${project.version} + +``` + +`${project.version}` 为您开发插件所对应的Nacos版本 + +随后实现`com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber`接口, 并将您的实现添加到SPI的services当中。 + +接口中需要实现的方法如下: + +|方法名|入参内容|返回内容|描述| +|-----|-----|-----|---| +|getName|void|String|插件的名称,当名字相同时,后加载的插件会覆盖先加载的插件。| +|subscribeTypes|void|List>|该插件期望订阅的事件类型,返回空列表是不订阅。| +|onEvent|TraceEvent|void|处理事件的具体逻辑,输入的具体事件类型由`subscribeTypes`接口定义| +|executor|void|Executor|当返回不为`null`时,将使用该Executor进行`onEvent`调用,否则将使用事件分发线程进行调用| + +> 注意: +> 建议插件实现时使用独立Executor,如插件实现中有存在阻塞的IO操作,当存在IO异常时将阻塞其他事件的onEvent调用,导致积压问题。 + +在[nacos-group/nacos-plugin](https://github.com/nacos-group/nacos-plugin)中,有一个demo的轨迹追踪插件实现,该demo插件订阅了注册及注销实例的事件,并打印到日志中。 + +## 轨迹追踪插件的降级 + +由于轨迹追踪插件增强监控类别的插件,不会对Nacos的数据造成影响,因此当轨迹追踪插件出现问题时,因尽量不影响Nacos主要链路。 + +因此建议插件实现时使用独立Executor,如插件实现中有存在阻塞的IO操作,当存在IO异常时将阻塞其他事件的onEvent调用,导致积压问题。 + +如果不幸发生积压,轨迹追踪插件的事件队列达到上限时,会自动丢弃后来的事件,以保证系统整体稳定性。 + +发生丢弃时能从`nacos.log`中看到`Trace Event Publish failed, event : {}, publish queue size : {}`字样。 + +## 附录:子追踪事件详情 + +

服务实例注册事件 RegisterInstanceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `REGISTER_INSTANCE_TRACE_EVENT` + +额外内容: + +|字段名|描述| +|-----|---| +|clientIp|注册服务实例请求的来源IP,可能为null| +|rpc|来源是否为gRPC,`true`时为gRPC注册,`false`时为HTTP注册| +|instanceIp|所注册实例的地址IP/HOST| +|instancePort|所注册实例的端口PORT| + +

服务实例注销事件 DeregisterInstanceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `DEREGISTER_INSTANCE_TRACE_EVENT` + +额外内容: + +|字段名|描述| +|-----|---| +|clientIp|注销服务实例请求的来源IP,可能为null| +|reason|注销服务实例的原因,详情见[服务实例注销原因](#1.2.1)| +|rpc|来源是否为gRPC,`true`时为gRPC注册,`false`时为HTTP注册| +|instanceIp|所注销实例的地址IP/HOST| +|instancePort|所注销实例的端口PORT| + +

服务实例注销原因 DeregisterInstanceReason

+ +|原因|描述| +|-----|---| +|REQUEST|注销来自于客户端请求,即由用户发起的注销| +|NATIVE_DISCONNECTED|注销来自于客户端链接断开| +|SYNCED_DISCONNECTED|注销来自于客户端链接断开,但该客户端链接是与集群其他的节点,断开后同步到本节点的| +|HEARTBEAT_EXPIRE|注销来自于客户端心跳请求超时,适用于1.X版本的客户端| + +

服务注册事件 RegisterServiceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `REGISTER_SERVICE_TRACE_EVENT` + +额外内容:无 + +

服务注销事件 DeregisterServiceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `DEREGISTER_SERVICE_TRACE_EVENT` + +额外内容:无 + +

服务订阅事件 SubscribeServiceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `SUBSCRIBE_SERVICE_TRACE_EVENT` + +额外内容: + +|字段名|描述| +|-----|---| +|clientIp|订阅者IP| + +

取消服务订阅事件 UnsubscribeServiceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `UNSUBSCRIBE_SERVICE_TRACE_EVENT` + +额外内容: + +|字段名|描述| +|-----|---| +|clientIp|订阅者IP| + +

服务推送事件 PushServiceTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `PUSH_SERVICE_TRACE_EVENT` + +额外内容: + +|字段名|描述| +|-----|---| +|clientIp|订阅者IP| +|instanceSize|本次推送的提供者数量| +|pushCostTimeForAll|本次推送总耗时,定义为开始发起推送到推送结束时的耗时,包含了在聚合队列中的等待时间以及执行推送的时间| +|pushCostTimeForNetWork|本次推送的网络耗时,定义为执行推送到推送结束的耗时,仅包含了网络耗时| +|serviceLevelAgreementTime|本次推送的实际生效耗时,定义为服务变更到推送结束时的耗时,粗略值| + +

服务实例健康状态变更事件 HealthStateChangeTraceEvent

+ +> 2.2.0版本开始支持。 + +type: `HEALTH_STATE_CHANGE_TRACE_EVENT` + +额外内容: + +|字段名|描述| +|-----|---| +|instanceIp|实例的地址IP/HOST| +|instancePort|实例的端口PORT| +|isHealthy|变更结果是否为健康| +|healthCheckType|健康检查的类型| +|healthStateChangeReason|健康状态发生的原因| diff --git a/src/content/docs/v3.0/zh-cn/quickstart/quick-start-docker.mdx b/src/content/docs/v3.0/zh-cn/quickstart/quick-start-docker.mdx new file mode 100644 index 00000000000..fc30a2fb9e4 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/quickstart/quick-start-docker.mdx @@ -0,0 +1,83 @@ +--- +title: Nacos Docker 快速开始 +keywords: [Nacos,Docker] +description: Nacos Docker 快速开始 +sidebar: + order: 2 +--- + +# Nacos Docker 快速开始 + +这个快速开始手册是帮忙您快速在通过Nacos的Docker镜像,在Docker容器中部署并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**单机模式**及**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**集群模式**并**开启鉴权**,以避免存在**稳定性和安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 1. 环境准备 + +使用此快速开始方法进行Nacos安装及部署,需要安装[Docker](https://www.docker.com/)和[Docker Compose](https://docs.docker.com/compose/)。 + +## 2. 下载 nacos-docker 项目 + +```powershell +git clone https://github.com/nacos-group/nacos-docker.git +cd nacos-docker +``` + +## 3. 执行 docker-compose 命令启动Nacos + +> 首次执行命令时,会自动下载所需的相关Docker镜像,需要等待的时长取决于网络速度。您也可以提前下载好相关镜像,以缩短执行部署命令的等待时间。 + +```powershell +docker-compose -f example/standalone-derby.yaml up +``` + +## 4. 验证Nacos服务是否启动成功 + +通过`docker logs -f $container_id`命令,查看Nacos服务启动日志,如果看到如下日志,说明服务启动成功。 + +``` +Nacos started successfully in xxxx mode. use xxxx storage +``` + +可以通过下列服务,快速检验Nacos的功能。 + +### 4.1. 服务注册 + + ```powershell + curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080' + ``` + +### 4.2. 服务发现 + + ```powershell + curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName' + ``` + +### 4.3. 发布配置 + + ```powershell + curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld" + ``` + +### 4.4. 获取配置 + + ```powershell + curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test" + ``` + +### 4.5. Nacos控制台页面 + + link:http://127.0.0.1:8848/nacos/ + +## Nacos + Grafana + Prometheus +参考:[Nacos监控指南](../guide/admin/monitor-guide.md) + +**Note**: grafana创建一个新数据源时,数据源地址必须是 **http://prometheus:9090** + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Docker](https://github.com/nacos-group/nacos-docker) diff --git a/src/content/docs/v3.0/zh-cn/quickstart/quick-start-kubernetes.mdx b/src/content/docs/v3.0/zh-cn/quickstart/quick-start-kubernetes.mdx new file mode 100644 index 00000000000..a8b0f689af5 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/quickstart/quick-start-kubernetes.mdx @@ -0,0 +1,81 @@ +--- +title: Nacos Kubernetes 快速开始 +keywords: [nacos,kubernetes] +description: 本项目包含一个可构建的Nacos Docker Image,旨在利用 StatefulSets 在 Kubernetes上部署 Nacos。 +sidebar: + order: 3 +--- + +# Nacos Kubernetes 快速开始 + +这个快速开始手册是帮忙您快速在通过Nacos的Docker镜像,在Kubernetes中部署并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**开启鉴权**,以避免存在**安全性**的风险。 +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始为方便快速在kubernetes环境中部署Nacos,连接数据库时使用了默认的数据库配置,建议仅在测试中使用,在实际生产环境部署时,请**务必**修改部署的数据库配置信息,以避免存在**安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 1. 环境准备 + +使用此快速开始方法进行Nacos安装及部署,需要安装[Kubernetes](https://kubernetes.io/)。 + +## 2. 下载 nacos-k8s 项目 + +```shell +git clone https://github.com/nacos-group/nacos-k8s.git +cd nacos-k8s +``` + +## 3. 快速启动 + +> 使用此方式快速启动,请注意这是没有使用持久化卷的,可能存在数据丢失风险: + +```shell +cd nacos-k8s +chmod +x quick-startup.sh +./quick-startup.sh +``` + +## 4. 验证Nacos服务是否启动成功 + +通过`kubectl logs -f $pod_name`命令,查看Nacos服务启动日志,如果看到如下日志,说明服务启动成功。 + +``` +Nacos started successfully in xxxx mode. use xxxx storage +``` + +可以通过下列服务,快速检验Nacos的功能。 + +### 4.1. 服务注册 + + ```powershell + curl -X POST 'http://${cluster-ip}:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080' + ``` + +### 4.2. 服务发现 + + ```powershell + curl -X GET 'http://${cluster-ip}:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName' + ``` + +### 4.3. 发布配置 + + ```powershell + curl -X POST "http://${cluster-ip}:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=helloWorld" + ``` + +### 4.4. 获取配置 + + ```powershell + curl -X GET "http://${cluster-ip}:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test" + ``` + +### 4.5. Nacos控制台页面 + +link:`http://${cluster-ip}:8848/nacos/` + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) +* [Nacos Docker](https://github.com/nacos-group/nacos-k8s) \ No newline at end of file diff --git a/src/content/docs/v3.0/zh-cn/quickstart/quick-start-native.mdx b/src/content/docs/v3.0/zh-cn/quickstart/quick-start-native.mdx new file mode 100644 index 00000000000..f214528a689 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/quickstart/quick-start-native.mdx @@ -0,0 +1,69 @@ +--- +title: Nacos Native 快速开始 +keywords: [Nacos,快速开始] +description: 这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 +sidebar: + order: 1 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Nacos 快速开始 + +这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**单机模式**及**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**集群模式**并**开启鉴权**,以避免存在**稳定性和安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 环境需求 +Nacos Native 是 Nacos 源码基于 GraalVM 打包而成,其运行不再依赖系统中单独的 JDK 程序。Nacos Native 目前提供了 Linux(GNU)、MacOS 三类操作系统所对应的可执行程序,当前 Nacos Native 对应的 Java Nacos 版本为 2.4.0,可执行程序可以在对应的 release 页面下进行下载。推荐使用完备(包含完备的 libstdc++ 库)的 64bit 的操作系统来运行 Nacos Native。 + +## 下载安装包 +在对应页面下载得到程序包后进行解压,解压后检视文件是否完备: + +1. 在 Linux 操作系统中应当包含 nacos-server 二进制程序、libinstrument.so 链接文件与相关其他配置。 +2. 在 MacOS 操作系统中应当包含 nacos-server 二进制程序与相关其他配置。 + +对于其他操作系统未发布的 Nacos Native 版本,您可以选择在该操作系统上手动进行源码编译得到 Nacos Native 二进制程序包。 + +## 启动服务器 +Nacos Native 的运行建议至少在 1C2G 60G 的机器配置下运行。对于 Linux/Unix/Mac 操作系统,启动命令为: + +```shell +# standalone 代表着单机模式运行,非集群模式 +sh startup.sh -m standalone +``` + +若要启动 Nacos Native 集群,则需要在相应的 `conf` 文件夹位置下包含对应的 `cluster.conf` 文件,该步骤与 Java Nacos 保持一致,在启动 Nacos Native 集群后会自动扫描并获取集群 IP。 + +## 关闭服务器 +对于 Linux/Unix/Mac 操作系统,关闭命令为: + +```shell +sh shutdown.sh +``` + +## 日志检视 +以 Linux/Unix 操作系统为例,Nacos Native 的运行日志会被记录在 `/root/nacos/logs` 目录中,其日志结构与 Java Nacos 保持一致。 + +## 构建原生Nacos +Nacos Native 提供了不同操作系统对应的构建配置,通过 Maven 可以快速构建对应操作系统下的原生 Nacos 程序。参考构建指令进行构建: + +```shell +# 在 win x64 环境下构建 Native Nacos +mvn clean install -DskipTests=true -Pnative -Pnative-win64 +# 在 macos x86_64 环境下构建 Native Nacos +mvn clean install -DskipTests=true -Pnative -Pnative-osx-x86_64 +# 在 linux x64 环境下构建 Native Nacos +mvn clean install -DskipTests=true -Pnative -Pnative-linux64 +``` + +其中 `-Pnative` 配置为基本 native 构建配置,`-Pnative-` 为操作系统类型,改配置主要引导 rocksdb 库能够正确地被 Nacos Native 打包。值得注意的是,虽然 Nacos Native 提供了 musl 类型的打包配置,但 GraalVM 本身仍然需要在支持 GNU 的操作系统下进行编译,因此与 musl 相关的编译不推荐使用。 + +编译成功后,相关文件会发布在 `/console/target` 目录下,根据“下载安装包”中对应的文件完备性检视引导取用编译文件即可。 + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) diff --git a/src/content/docs/v3.0/zh-cn/quickstart/quick-start.mdx b/src/content/docs/v3.0/zh-cn/quickstart/quick-start.mdx new file mode 100644 index 00000000000..a30749577cf --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/quickstart/quick-start.mdx @@ -0,0 +1,117 @@ +--- +title: Nacos 快速开始 +keywords: [Nacos,快速开始] +description: 这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 +sidebar: + order: 1 +--- + +import { Tabs, TabItem } from '@astrojs/starlight/components'; + +# Nacos 快速开始 + +这个快速开始手册是帮忙您快速在您的电脑上,下载、安装并使用 Nacos。 + +:::note +- 快速开始旨在帮助您快速上手安装、部署及入门使用Nacos,本快速开始所生产出的Nacos服务为**单机模式**及**未开启鉴权**,建议仅在测试中使用,若在实际生产环境中部署,请部署**集群模式**并**开启鉴权**,以避免存在**稳定性和安全性**的风险。 +- Nacos定义为一个IDC内部应用组件,并非面向公网环境的产品,建议在内部隔离网络环境中部署,**强烈不建议**部署在公共网络环境。 +::: + +## 0. 版本选择 + +您可以通过Nacos的[版本下载页面](/download/nacos-server/)、[release notes](https://github.com/alibaba/nacos/releases) 及 [发布声明](/news/release/)中找到每个版本支持的功能的介绍,当前推荐的稳定版本为`2.4.3`. + +## 1. 预备环境准备 + +Nacos 依赖 [Java](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/) 环境来运行,请确保是在以下版本环境中安装使用: + +1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。 +2. 64 bit JDK 1.8+;[下载](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) & [配置](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/)。 + +## 2. 下载安装包 + +你可以通过Nacos官网网站及Github两种方式来获取 **Nacos 发行包** 。 + + + + 进入Nacos官网[版本下载页面](/download/nacos-server/),选择 [稳定版本](/download/nacos-server/#稳定版本), 然后点击`二进制包下载`列中的`${nacos.version}.zip`进行下载。 + + > 注意:有时大量用户同时进行下载时,可能会遇到下载限流失败的情况,若出现下载限流失败,请稍等后重试,或采用`从 Github 下载方式`。 + + + 进入Nacos Github 的 [最新稳定版本](https://github.com/alibaba/nacos/releases) ,选择需要下载的Nacos版本,在`Assets`中点击下载 `nacos-server-$version.zip` 包。 + + + +## 3. 解压缩Nacos 发行包 + +```bash + unzip nacos-server-$version.zip + # 或者 tar -xvf nacos-server-$version.tar.gz + cd nacos/bin +``` + +## 4.启动服务器 + +* 注:Nacos的运行建议至少在2C4G 60G的机器配置下运行。 + + + + 启动命令(standalone代表着单机模式运行,非集群模式): + + `sh startup.sh -m standalone` + + 如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行: + + + 启动命令(standalone代表着单机模式运行,非集群模式): + + `startup.cmd -m standalone` + + + +## 5.验证Nacos服务是否启动成功 + +进入`${nacos.home}/logs/` 目录下, 使用`tail -f start.out` 查看日志,如果看到如下日志,说明服务启动成功。 + +``` +Nacos started successfully in stand alone mode. use embedded storage +``` + +可以通过下列服务,快速检验Nacos的功能。 + +### 5.1. 服务注册 + +`curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=20.18.7.10&port=8080'` + +### 5.2. 服务发现 + +`curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.naming.serviceName'` + +### 5.3. 发布配置 + +`curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test&content=HelloWorld"` + +### 5.4. 获取配置 + +`curl -X GET "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group=test"` + +### 5.5. Nacos控制台页面 + +打开任意浏览器,输入地址:`http://127.0.0.1:8848/nacos`,即可进入Nacos控制台页面。 + +## 6.关闭服务器 + +### 6.1. Linux/Unix/Mac + +`sh shutdown.sh` + +### 6.2. Windows + +`shutdown.cmd` + +或者双击shutdown.cmd运行文件。 + +## 相关项目 + +* [Nacos](https://github.com/alibaba/nacos) diff --git a/src/content/docs/v3.0/zh-cn/upgrading/200-compatibility.md b/src/content/docs/v3.0/zh-cn/upgrading/200-compatibility.md new file mode 100644 index 00000000000..d5f8c60d9b9 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/upgrading/200-compatibility.md @@ -0,0 +1,206 @@ +--- +title: Nacos 2.0.0 兼容性文档 +keywords: [Nacos,2.0.0] +description: Nacos 2.0.0 兼容性文档 +sidebar: + order: 1 +--- + +> 该文档即将废弃,推荐查看[运维手册-升级手册](../manual/admin/upgrading.mdx)。 + +# Nacos2.0代码存放位置 + +Nacos代码当前保存在develop分支中,启动方式与Nacos 1.x相同(当前位于v1.x-develop),欢迎贡献。 + +# Nacos 2.0.0 兼容性文档 + +经过社区的讨论和开发, Nacos 基于长连接的2.0.0版本的核心功能已开发完成,目前2.0.0正式版本已发布,欢迎大家使用。 + +2.0.0支持Nacos1.X服务端的平滑升降级的能力,详情请查看[Nacos2.0升级文档](https://nacos.io/docs/v2/upgrading/version2-upgrading/) 。 + +## Nacos 2.0.0版本压测 + +详情见:[Nacos2.0服务发现模块压测报告](../guide/admin/nacos2-naming-benchmark.md) 以及 [Nacos2.0配置模块压测报告](../guide/admin/nacos2-config-benchmark.md) 。 + +大规模压测报告将在近期放出。 + +## 新版本部署 + +Nacos2.0版本相比1.X新增了gRPC的通信方式,因此需要增加2个端口。新增端口是在配置的主端口(server.port)基础上,进行一定偏移量自动生成。 + +|端口|与主端口的偏移量|描述| +|--|--|--| +|9848|1000|客户端gRPC请求服务端端口,用于客户端向服务端发起连接和请求| +|9849|1001|服务端gRPC请求服务端端口,用于服务间同步等| +|7848|-1000|Jraft请求服务端端口,用于处理服务端间的Raft相关请求| + +**使用VIP/nginx请求时,需要配置成TCP转发,不能配置http2转发,否则连接会被nginx断开。** +**9849和7848端口为服务端之间的通信端口,请勿暴露到外部网络环境和客户端测。** + +![nacos2_port_exposure.png](/img/nacos2_port_exposure.png) + +客户端拥有相同的计算逻辑,用户如同1.X的使用方式,配置主端口(默认8848),通过相同的偏移量,计算对应gRPC端口(默认9848)。 + +因此如果客户端和服务端之前存在端口转发,或防火墙时,需要对端口转发配置和防火墙配置做相应的调整。 + +其余部署参考[Nacos部署手册](../guide/admin/deployment.md) ,将版本相关替换成2.1.1。 + +## 兼容性 + +Nacos2.0的服务端完全兼容1.X客户端。Nacos2.0客户端由于使用了gRPC,无法兼容Nacos1.X服务端,请勿使用2.0以上版本客户端连接Nacos1.X服务端。 + +## 功能完成度及旧版本客户端适配情况: + +### 配置中心 + +#### JAVA SDK + +- 完全兼容1.X客户端所有API接口方法; +- 完全实现2.0客户端所有API接口方法。 + +#### 其他语言 SDK + +- 完全兼容 + +#### openAPI + +- 完全兼容所有配置中心相关openAPI。 + +### 服务发现 + +#### JAVA SDK + +- 完全兼容1.X客户端所有API接口方法; +- 完全兼容2.0客户端所有API接口方法; + +#### 其他语言 SDK + +- 完全兼容所有服务发现相关openAPI。 + +#### openAPI + +- 注册实例(支持) +- 注销实例(支持) +- 修改实例(支持) +- 查询实例列表(支持) +- 查询实例详情(支持) +- 发送实例心跳(支持) +- 创建服务(支持) +- 删除服务(支持) +- 修改服务(支持) +- 查询服务(支持) +- 查询服务列表(支持) +- 查询系统开关(支持) +- 修改系统开关(支持) +- 查看系统当前数据指标(支持) +- 查看当前集群Server列表(支持) +- 查看当前集群leader(将废弃) +- 更新实例的健康状态(支持) +- 批量更新实例元数据(支持) +- 批量删除实例元数据(支持) + +### 控制台 + +- 完全兼容配置中心相关页面及功能 +- 完全兼容权限控制相关页面及功能 +- 完全兼容命名空间相关页面及功能 +- 完全兼容集群管理相关页面及功能 +- 完全兼容服务发现相关页面及功能 + +## 生态兼容情况 + +### Spring Cloud Alibaba + +可通过指定nacos-client方式,提前使用Nacos2.0长连接功能 + +``` + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + 2.1.5.RELEASE + + + com.alibaba.nacos + nacos-client + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + 2.1.5.RELEASE + + + com.alibaba.nacos + nacos-client + + + + + com.alibaba.nacos + nacos-client + 2.1.1 + +``` + +### Dubbo + +Nacos2.0版本客户端重新适配了Dubbo2.7.X。并且Dubbo社区正在对新版本进行修改,不再强依赖反射,详情请看 [Dubbo#7291](https://github.com/apache/dubbo/issues/7291) + +### Nacos Spring Boot + +Nacos spring boot 已发布新版本适配2.0客户端。请升级至最新版本。 + +## 使用方式 + +### SDK客户端、控制台 + +Nacos 2.0版本使用方式和Nacos1.X版本使用完全一致。客户端接口请参考[SDK文档](../guide/user/sdk.md)。 + +### 服务端 + +Nacos 2.0服务端的使用也和旧版本没有太大区别,这里对新版本中新增的数个配置参数进行说明 + +|参数|默认值|描述| +|--|--|--| +|nacos.naming.clean.empty-service.interval|60000(单位毫秒)|Nacos自动清理空服务的工作间隔,将替代旧版本中的`nacos.naming.empty-service.clean.period-time-ms`参数| +|nacos.naming.clean.empty-service.expired-time|60000(单位毫秒)|Nacos判断可清理的空服务的过期时间,当服务没有发布的实例,且超过该过期时间未发生更新后,将被判定为过期空服务而移除| +|nacos.naming.clean.expired-metadata.interval|5000(单位毫秒)|Nacos自动清理过期元数据的工作间隔| +|nacos.naming.clean.expired-metadata.expired-time|60000(单位毫秒)|Nacos自动清理过期服务的过期时间,当服务或实例本身被移除超过该设定时间后,元数据信息将会被移除| + +## FAQ + +### 能否支持Nacos旧版本客户端? + +配置中心兼容支持Nacos1.0起的所有版本客户端,服务发现兼容Nacos1.2起所有版本客户端。 +因此建议使用Nacos1.2.0之后版本客户端。 +但nacos1.X的客户端不具有长连接能力,因此仍然建议使用Nacos2.0客户端。 + +### 启动后,调用openAPI 报错 code:503,msg:server is DOWN now, please try again later! + +Nacos在1.4版本后使用Jraft替换了自研的Raft实现,Jraft的选主比原先自研的Raft更加严格,会记录之前启动时的ip或host。因此重启时如果ip变动了,有可能造成选主失败,从而导致nacos无法正确提供服务。 +解决方式为删除nacos目录下的data,再启动。 + +或者使用`-Dnacos.server.ip=${domain}`,然后将nacos/conf的cluster.conf配置domain列表,避免重启时ip变动导致的raft选主问题。 + +### 找不到符号`com.alibaba.nacos.consistency.entity` + +这个包目录是由`protobuf`在编译时自动生成,您可以通过`mvn compile`来自动生成他们。如果您使用的是IDEA,也可以使用IDEA的protobuf插件。 + +### 启动时报错`Connection is unregistered.`或`Client not connected,current status:STARTING`. + +原因是客户端gRPC无法和服务端创建连接,请先使用`telnet ${nacos.server.address}:${nacos.server.grpc.port}`进行测试,查看网络是否畅通,服务端端口是否已经正确监听。 + +若服务端没有问题,查看配置是否有误,服务端和客户端的所配置的端口应一致。 + +若配置也没有问题,查看是否有防火墙或VIP端口转发问题,Nacos2.0的gRPC端口均通过主端口的偏移量计算产生,因此端口转发也需要满足该偏移量。 + +### Nacos2.0增加了9848,9849端口来进行GRPC通信,我需要在application.properties中额外配置吗? + +不需要,这两个端口在Nacos2.0内部是通过8848+1000以及8848+1001这种偏移量方式计算出来的,不需要用户额外在配置文件中配置。但如果使用的是docker或存在端口转发方式启动,需要把这两个端口进行配置。 + +### 启动nacos2.0时希望用nginx 代理,9848这个端口怎样处理,要通过nginx暴露出来么?以及docker是否需要映射? + +如果存在防火墙或者nginx端口转发问题,需要进行相应的端口暴露配置。如在nginx中,在已经暴露8848(x)的基础上,需要额外暴露9848(x+1000)。 + +### 待补充... diff --git a/src/content/docs/v3.0/zh-cn/upgrading/200-upgrading.md b/src/content/docs/v3.0/zh-cn/upgrading/200-upgrading.md new file mode 100644 index 00000000000..dc0715ea09e --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/upgrading/200-upgrading.md @@ -0,0 +1,550 @@ +--- +title: Nacos 2.0 升级文档 +keywords: [Nacos,2.0,升级] +description: Nacos 2.0 升级文档 +sidebar: + order: 2 +--- + +> 该文档即将废弃,推荐查看[运维手册-升级手册](../manual/admin/upgrading.mdx)。 + +# Nacos 2.0.0 部署及升级文档 + +本文档包含两个部分:Nacos2.0.0的部署,以及如何从Nacos1.x平滑升级至Nacos2.0.0。 + +部署部分,适用于直接部署Nacos2.0.0以上版本的用户。 + +升级部分,适用于从Nacos1.X版本平滑升级到Nacos2.0.0版本(以及2.0.0-BETA版本)的用户。Nacos2.0.0-ALPHA版本无法进行平滑升级,请勿参照本文档进行升级。 + +由于Nacos1.X和Nacos2.0的数据结构发生了变化,为了能够完成平滑升降级,需要将数据进行双写,分别生成Nacos1和Nacos2的数据结构进行存储。因此会对性能有一定影响。当集群升级并稳定运行后,可以关闭双写,关闭双写后将会失去平滑降级的功能。 + +> 在Nacos2.1.0版本后,默认关闭了双写能力,因此无法支持从Nacos1.X版本平滑升级到2.1.0的能力,若需要使用平滑升级能力,从Nacos1.X直接升级到Nacos2.1.0版本,需要在application.properties文件中设置配置nacos.core.support.upgrade.from.1x=true. + +# 部署步骤 + +本部分,适用于直接部署Nacos2.0.0以上版本的用户。 + +## 1.预备环境准备 + +Nacos 依赖 [Java](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/) 环境来运行。如果您是从代码开始构建并运行Nacos,还需要为此配置 [Maven](https://maven.apache.org/index.html)环境,请确保是在以下版本环境中安装使用: + +1. 64 bit OS,支持 Linux/Unix/Mac/Windows,推荐选用 Linux/Unix/Mac。 +2. 64 bit JDK 1.8+;[下载](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) & [配置](https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/)。 +3. Maven 3.2.x+;[下载](https://maven.apache.org/download.cgi) & [配置](https://maven.apache.org/settings.html)。 + +## 2.下载源码或者安装包 + +你可以通过源码和发行包两种方式来获取 Nacos。 + +### 从 Github 上下载源码方式 + +```bash +git clone https://github.com/alibaba/nacos.git +cd nacos/ +mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U +ls -al distribution/target/ + +// change the $version to your actual path +cd distribution/target/nacos-server-$version/nacos/bin + +``` + +### 下载编译后压缩包方式 + +您可以从 [最新稳定版本](https://github.com/alibaba/nacos/releases) 下载 `nacos-server-$version.zip` 包。 + + +```bash + unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz + cd nacos/bin +``` + + +## 3.启动服务器 + +### Linux/Unix/Mac + +单机启动命令(standalone代表着单机模式运行): + +`sh startup.sh -m standalone` + +如果您使用的是ubuntu系统,或者运行脚本报错提示[[符号找不到,可尝试如下运行: + +`bash startup.sh -m standalone` + +单机启动,使用内置数据库(注:使用内置Derby数据库需要保证~/nacos/data/derby-data文件夹下无残留数据): + +`bash startup.sh -p embedded` + +集群启动(使用内嵌数据库): + +`bash startup.sh -p embedded` + +集群启动(使用外置数据库): + +`bash startup.sh` + +## 4.启动后自检 + +集群中所有机器部署为2.0.X版本并启动时,应当进行启动之后的检查。 + +当集群中所有节点logs/naming-server.log日志中观察到upgrade check result true及Upgrade to 2.0.X,便判定为集群准备完毕时,此时才可以使用Nacos2.0。 + +## 5.关闭双写 + +为了节省性能开销,当集群部署完成后,可以先观察一段时间运行情况,当确认无误后,可以关闭双写,从而释放性能,具体的关闭方式是通过API进行: + +`curl -X PUT 'localhost:8848/nacos/v1/ns/operator/switches?entry=doubleWriteEnabled&value=false'` + +关闭后可以从`logs/naming-server.log`日志中观察到`Disable Double write, stop and clean v1.x cache and features`字样。说明关闭双写。 + +**注意**,关闭双写后无法在进行平滑降级,请先确认关闭前集群正确运行。 + +## 6.关闭服务器 + +### Linux/Unix/Mac + +`sh shutdown.sh` + +### Windows + +`cmd shutdown.cmd` + +或者双击shutdown.cmd运行文件。 + +# 升级步骤 + +以linux系统为例。window系统请自行替换`sh`脚本为`cmd`脚本。 + +## 1. 停止旧节点 + +选择集群中一台Nacos1.X节点,使用Nacos目录下`nacos/bin/shutdown.sh`进行停止。 + +## 2. 替换文件 + +下载并解压缩`nacos-server-2.0.2.tar.gz`,将其下的`bin`,`conf`,`target`目录覆盖原Nacos1.X的安装目录下。 + +## 3. 修改配置 + +自行修改`nacos/bin/startup.sh`中的JVM参数,`conf/cluster.conf`中的集群列表以及`conf/application.prpperties`中数据库或其他相关参数。 + +## 4. 启动Nacos2.0 + +使用nacos目录下`nacos/bin/startup.sh`启动nacos2.0,其他更多启动指令请查看[Nacos部署环境](../guide/admin/deployment.md) 。 + +## 5. 观察是否启动成功 + +首先查看nacos目录下 `logs/start.out`或`logs/nacos.log` 观察到nacos启动成功的日志,如 `Nacos started successfully in cluster mode. use xxx storage` 说明程序已启动成功。 + +之后在观察 `logs/naming-server.log` 中,可以看到有`upgrade check result false` 以及 `Check whether close double write`等日志信息。 + +属于正常现象。 + +## 6. 升级其他节点 + +待该节点的服务及实例信息已经同步完毕后(可从控制台进行确认)。重复1~5步骤,将其他的nacos节点也进行升级。 + +## 7. 确认升级完成 + +当集群中最后一个节点也升级到2.0.X版本时,集群会开始进行升级检测。每个节点会对该节点的服务信息和实例信息进行校验,并检测是否还有未完成的双写任务。 + +当该节点的服务信息和实例信息已经核对成功,并且没有双写任务存在时,该节点会判定自己已经做好升级准备,并修改自己的状态且通知其他Nacos节点。每台节点是否完成升级准备可以从控制台的集群管理中元数据信息中看到`"readyToUpgrade": false/true`。 + +当集群中所有节点均判定为准备完毕时。Nacos集群中的节点会进行升级切换,自动升级到Nacos2.0的处理逻辑。 + +可以从`logs/naming-server.log`日志中观察到`upgrade check result true`及`Upgrade to 2.0.X`。 + +

+ +## 8.1 关闭双写 + +当集群升级完成后,可以先观察一段时间运行情况,当确认无误后,可以关闭双写,从而释放性能,具体的关闭方式是通过API进行: + +`curl -X PUT 'localhost:8848/nacos/v1/ns/operator/switches?entry=doubleWriteEnabled&value=false'` + +关闭后可以从`logs/naming-server.log`日志中观察到`Disable Double write, stop and clean v1.x cache and features`字样。说明关闭双写。 + +**注意**,关闭双写后无法在进行平滑降级,请先确认关闭前集群正确运行。 + +## 8.2 降级 + +集群升级完毕后,依旧会进行双写,当升级后发现Nacos2.0存在问题时,可以快速进行降级,降级流程为重复步骤1~6,只是将版本改为对应的1.X版本。 + +当第一台降级完成后,集群即可观察到`logs/naming-server.log` 中的`upgrade check result false` ,且控制台集群管理中,所有新版本`"readyToUpgrade": false`。 + +

升级相关的openAPI

+ +在2.0.2版本中,nacos-server提供了一些方便查看升级状态及不同版本中的数据区别,方便用户排查升级中的问题。 + +## 查看统计 + +### 描述 + +查看当前升级状态 + +### 请求类型 +GET + +### 请求URL +/nacos/v1/ns/upgrade/ops/metrics + +### 请求参数 +无 + +### 返回参数 + +| 参数类型 | 描述 | +| :--- | :--- | +| string | 升级状态 | + +### 示例 + +```plain +upgraded = true +isAll20XVersion = true +isDoubleWriteEnabled = false +doubleWriteDelayTaskCount = 0 +serviceCountV1 = 0 +instanceCountV1 = 0 +serviceCountV2 = 0 +instanceCountV2 = 0 +subscribeCountV2 = 0 +responsibleServiceCountV1 = 0 +responsibleInstanceCountV1 = 0 +ephemeralServiceCountV2 = 0 +persistentServiceCountV2 = 0 +ephemeralInstanceCountV2 = 0 +persistentInstanceCountV2 = 0 +service.V1.not.in.V2 = +service.V2.not.in.V1 = +``` + +## 查询服务 + +### 描述 +查询对应Nacos版本中一个服务内容 + +### 请求类型 +GET + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/service +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X GET '127.0.0.1:8848/nacos/v1/ns/upgrade/ops/service?serviceName=nacos.test.2' +``` +### 示例返回 +``` +{ + metadata: { }, + groupName: "DEFAULT_GROUP", + namespaceId: "public", + name: "nacos.test.2", + selector: { + type: "none" + }, + protectThreshold: 0, + clusters: [ + { + healthChecker: { + type: "TCP" + }, + metadata: { }, + name: "c1" + } + ] +} +``` + +## 查询服务列表 + +### 描述 +查询对应Nacos版本的服务列表 + +### 请求类型 +GET + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/service/list +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| pageNo | int | 是 | 当前页码 | +| pageSize | int | 是 | 分页大小 | +| groupName | 字符串 | 否 | 分组名 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X GET '127.0.0.1:8848/nacos/v1/ns/upgrade/ops/service/list?pageNo=1&pageSize=2' +``` + +### 示例返回 +``` +{ + "count":148, + "doms": [ + "nacos.test.1", + "nacos.test.2" + ] +} +``` + +## 查询实例列表 + +### 描述 +查询对应Nacos版本中某个服务下的实例列表 + +### 请求类型 +GET + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/instance/list +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| clusters | 字符串,多个集群用逗号分隔 | 否 | 集群名称 | +| healthyOnly | boolean | 否,默认为false | 是否只返回健康实例 | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X GET '127.0.0.1:8848/nacos/v1/ns/upgrade/ops/instance/list?serviceName=nacos.test.1' +``` +### 示例返回 +```json +{ + "dom": "nacos.test.1", + "cacheMillis": 1000, + "useSpecifiedURL": false, + "hosts": [{ + "valid": true, + "marked": false, + "instanceId": "10.10.10.10-8888-DEFAULT-nacos.test.1", + "port": 8888, + "ip": "10.10.10.10", + "weight": 1.0, + "metadata": {} + }], + "checksum": "3bbcf6dd1175203a8afdade0e77a27cd1528787794594", + "lastRefTime": 1528787794594, + "env": "", + "clusters": "" +} +``` + +## 查询实例详情 + +### 描述 +查询一个对应Nacos版本中某个服务下个某个实例详情。 + +### 请求类型 +GET + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/instance +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| ip | 字符串 | 是 | 实例IP | +| port | 字符串 | 是 | 实例端口 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| cluster | 字符串 | 否 | 集群名称 | +| healthyOnly | boolean | 否,默认为false | 是否只返回健康实例 | +| ephemeral | boolean | 否 | 是否临时实例 | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X GET '127.0.0.1:8848/nacos/v1/ns/upgrade/ops/instance?serviceName=nacos.test.2&ip=10.10.10.10&port=8888&cluster=DEFAULT' +``` +### 示例返回 +```json +{ + "metadata": {}, + "instanceId": "10.10.10.10-8888-DEFAULT-nacos.test.2", + "port": 8888, + "service": "nacos.test.2", + "healthy": false, + "ip": "10.10.10.10", + "clusterName": "DEFAULT", + "weight": 1.0 +} +``` + +## 添加服务 + +### 描述 +补充添加一个服务到对应Nacos版本下 + +### 请求类型 +POST + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/service +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| protectThreshold | 浮点数 | 否 | 保护阈值,取值0到1,默认0 | +| metadata | 字符串 | 否 | 元数据 | +| selector | JSON格式字符串 | 否 | 访问策略 | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X POST '127.0.0.1:8848/nacos/v1/ns/service?serviceName=nacos.test.2&metadata=k1%3dv1' +``` +### 示例返回 +``` +ok +``` + +## 删除服务 + +### 描述 +从对应Nacos版本中删除一个服务,如果删除v2服务,只有当服务下实例数为0时允许删除。 + +### 请求类型 +DELETE + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/service +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X DELETE '127.0.0.1:8848/nacos/v1/ns/service?serviceName=nacos.test.2' +``` +### 示例返回 +``` +ok +``` + +## 注册实例 + +### 描述 +注册一个实例到对应Nacos版本的服务下。 + +### 请求类型 +POST + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/instance +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| ip | 字符串 | 是 | 服务实例IP | +| port | int | 是 | 服务实例port | +| namespaceId | 字符串 | 否 | 命名空间ID | +| weight | double | 否 | 权重 | +| enabled | boolean | 否 | 是否上线 | +| healthy | boolean | 否 | 是否健康 | +| metadata | 字符串 | 否 | 扩展信息 | +| clusterName | 字符串 | 否 | 集群名 | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| ephemeral | boolean | 否 | 是否临时实例 | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X POST 'http://127.0.0.1:8848/nacos/v1/ns/instance?port=8848&healthy=true&ip=11.11.11.11&weight=1.0&serviceName=nacos.test.3&encoding=GBK&namespaceId=n1' +``` +### 示例返回 +ok + +## 注销实例 + +### 描述 +删除对应Nacos版本服务下的一个实例。 + +### 请求类型 +DELETE + +### 请求路径 +```plain +/nacos/v1/ns/upgrade/ops/instance +``` + +### 请求参数 + +| 名称 | 类型 | 是否必选 | 描述 | +| :--- | :--- | :--- | --- | +| serviceName | 字符串 | 是 | 服务名 | +| groupName | 字符串 | 否 | 分组名 | +| ip | 字符串 | 是 | 服务实例IP | +| port | int | 是 | 服务实例port | +| clusterName | 字符串 | 否 | 集群名称 | +| namespaceId | 字符串 | 否 | 命名空间ID | +| ephemeral | boolean | 否 | 是否临时实例 | +| ver | 字符串 | 否 | 版本 `v1` 或者 `v2`, 默认`v2` | + +### 示例请求 +```plain +curl -X DELETE '127.0.0.1:8848/nacos/v1/ns/instance?serviceName=nacos.test.1&ip=1.1.1.1&port=8888&clusterName=TEST1' +``` +### 示例返回 +ok + +# 升级过程中可能遇到的问题 + +## 1. 最后一台节点升级完成时,注册的服务出现波动(变成不健康或暂时被摘除) + +升级过程中,为了节约性能,双写的内容仅是内容发生变更时的状态,心跳等内容不会被双写,因此切换版本时,可能有部分实例的心跳过久而健康检查又刚好开始执行,从而被标记非健康或摘除。 + +后续心跳处理将会把数据补充回来,最终会一致。 + +## 2. 升级完成后,升级的最后一台服务端报错`Server is DOWN` + +这可能是因为Raft选主失败导致的,解决方法是重启最后一台升级的服务端。或先将最后一台服务端降级,之后再重新进行一次升级即可。 diff --git a/src/content/docs/v3.0/zh-cn/what-is-nacos.md b/src/content/docs/v3.0/zh-cn/what-is-nacos.md new file mode 100644 index 00000000000..b91a3da09e6 --- /dev/null +++ b/src/content/docs/v3.0/zh-cn/what-is-nacos.md @@ -0,0 +1,97 @@ +--- +title: Nacos 配置中心简介, Nacos 是什么 +keywords: [nacos] +description: Nacos 配置中心简介,Nacos 是什么 +--- + +# 什么是 Nacos + +> 该文档即将废弃,推荐查看[Nacos 概览](./overview.md). + +## 概览 + +欢迎来到 Nacos 的世界! + +Nacos `/nɑ:kəʊs/` 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 + +Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。 + +Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。 + +## 什么是 Nacos? +服务(Service)是 Nacos 世界的一等公民。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理: + +[Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) + +[gRPC](https://grpc.io/docs/guides/concepts.html#service-definition) & [Dubbo RPC Service](https://dubbo.apache.org) + +[Spring Cloud RESTful Service](https://spring.io/projects/spring-cloud) + +Nacos 的关键特性包括: + +* **服务发现和服务健康监测** + + Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 [原生SDK](./guide/user/sdk.md)、[OpenAPI](./guide/user/open-api.md)、或一个[独立的Agent TODO](./guide/user/other-language.md)注册 Service 后,服务消费者可以使用[DNS TODO](./ecology/use-nacos-with-coredns.md) 或[HTTP&API](./guide/user/open-api.md)查找和发现服务。 + + Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。 + +* **动态配置服务** + + 动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。 + + 动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。 + + 配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。 + + Nacos 提供了一个简洁易用的UI ([控制台样例 Demo](http://console.nacos.io/nacos/index.html)) 帮助您管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。 + +* **动态 DNS 服务** + + 动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。 + + Nacos 提供了一些简单的 [DNS APIs TODO](./ecology/use-nacos-with-coredns.md) 帮助您管理服务的关联域名和可用的 IP:PORT 列表. + +* **服务及其元数据管理** + + Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。 + +## Nacos 地图 +一图看懂 Nacos,下面架构部分会详细介绍。 +![nacos_map](/img/nacosMap.jpg) +- 特性大图:要从功能特性,非功能特性,全面介绍我们要解的问题域的特性诉求 +- 架构大图:通过清晰架构,让您快速进入 Nacos 世界 +- 业务大图:利用当前特性可以支持的业务场景,及其最佳实践 +- 生态大图:系统梳理 Nacos 和主流技术生态的关系 +- 优势大图:展示 Nacos 核心竞争力 +- 战略大图:要从战略到战术层面讲 Nacos 的宏观优势 + +## Nacos 生态图 + +![nacos_landscape.png](https://cdn.nlark.com/lark/0/2018/png/11189/1533045871534-e64b8031-008c-4dfc-b6e8-12a597a003fb.png) + +如 Nacos 全景图所示,Nacos 无缝支持一些主流的开源生态,例如 + +* [Spring Cloud](./ecology/use-nacos-with-spring-cloud.md) +* [Apache Dubbo and Dubbo Mesh](./ecology/use-nacos-with-dubbo.md) +* [Kubernetes and CNCF](./quickstart/quick-start-kubernetes.mdx) + +使用 Nacos 简化服务发现、配置管理、服务治理及管理的解决方案,让微服务的发现、管理、共享、组合更加容易。 + +关于如何在这些生态中使用 Nacos,请参考以下文档: + +[Nacos与Spring Cloud一起使用](./ecology/use-nacos-with-spring-cloud.md) + +[Nacos与Kubernetes一起使用](./quickstart/quick-start-kubernetes.mdx) + +[Nacos与Dubbo一起使用](./ecology/use-nacos-with-dubbo.md) + + +## 下一步 + +继续阅读 [快速开始](./quickstart/quick-start.mdx) 以快速上手 Nacos。 + + + + + +