From 5355c21f1600de3dabab364d041f5be3570a939d Mon Sep 17 00:00:00 2001 From: "ken.lj" Date: Fri, 20 May 2022 14:22:38 +0800 Subject: [PATCH] 3.0 documentation (#1086) --- .github/workflows/build_and_deploy.yml | 5 - assets/scss/_styles_project.scss | 4 + assets/scss/main.scss | 16 + config.toml | 30 + .../en/docs/v2.7/user/configuration/xml.md | 2 +- content/en/docs/v2.7/user/examples/context.md | 2 +- content/en/docs/v2.7/user/recommend.md | 2 +- .../v2.7/user/references/registry/redis.md | 2 +- .../user/references/registry/zookeeper.md | 2 +- .../docs/v3.0/languages/erlang/quick-start.md | 2 +- content/en/latest/admin/_index.md | 8 + content/en/latest/community/_index.md | 186 +++ .../latest/contribution-guidelines/_index.md | 94 ++ .../committer/_index.md | 11 + .../committer/apache-dubbo-page_dev.md | 34 + .../committer/label-an-issue-guide_dev.md | 32 + .../committer/new-committer-guide_dev.md | 87 ++ .../committer/release-guide_dev.md | 483 +++++++ .../committer/website-guide_dev.md | 11 + .../contributor/_index.md | 11 + .../contributor/become-a-committer_dev.md | 20 + .../contributor/cla-signing-guide_dev.md | 30 + .../contributor/dubbo-extension-guide_dev.md | 53 + .../mailing-list-subscription-guide_dev.md | 87 ++ .../contributor/new-contributor-guide_dev.md | 133 ++ .../reporting-security-issues_dev.md | 26 + .../software-donation-guide_dev.md | 112 ++ .../contributor/test-coverage-guide_dev.md | 21 + content/en/latest/download/_index.md | 13 + content/en/latest/facade-docs/_index.md | 15 + .../en/latest/facade-docs/contact /_index.md | 11 + content/en/latest/facade-docs/how/_index.md | 8 + .../en/latest/facade-docs/how/quick-start.md | 38 + content/en/latest/facade-docs/what/_index.md | 7 + .../latest/facade-docs/what/core-concepts.md | 7 + .../en/latest/facade-docs/what/ecosystem.md | 7 + .../en/latest/facade-docs/what/overview.md | 7 + content/en/latest/facade-docs/why/_index.md | 11 + .../en/latest/facade-docs/why/advantage.md | 11 + content/en/latest/golang-sdk/_index.md | 8 + content/en/latest/java-sdk/_index.md | 8 + content/en/latest/java-sdk/v2.x/_index.md | 8 + .../advanced-features-and-usage/_index.md | 8 + .../v2.x/concepts-and-architecture/_index.md | 8 + .../v2.x/concepts-and-architecture/users.md | 6 + .../java-sdk/v2.x/contributing/_index.md | 8 + .../java-sdk/v2.x/introduction/_index.md | 8 + .../java-sdk/v2.x/introduction/users.md | 6 + .../java-sdk/v2.x/quick-start/_index.md | 8 + .../java-sdk/v2.x/quick-start/quick-start.md | 8 + .../java-sdk/v2.x/reference-manual/_index.md | 8 + .../v2.x/upgrades-and-compatibility/_index.md | 8 + content/en/latest/java-sdk/v3.x/_index.md | 8 + .../advanced-features-and-usage/_index.md | 8 + .../v3.x/concepts-and-architecture/_index.md | 8 + .../v3.x/concepts-and-architecture/users.md | 6 + .../java-sdk/v3.x/contributing/_index.md | 8 + .../java-sdk/v3.x/introduction/_index.md | 8 + .../java-sdk/v3.x/introduction/users.md | 6 + .../java-sdk/v3.x/quick-start/_index.md | 8 + .../java-sdk/v3.x/reference-manual/_index.md | 8 + .../v3.x/upgrades-and-compatibility/_index.md | 8 + content/en/latest/notices/_index.md | 11 + content/en/latest/notices/security.md | 73 + content/en/latest/pixiu/_index.md | 8 + content/zh/_index.html | 66 +- content/zh/docs3-building/docs/_index.md | 38 + .../zh/docs3-building/docs/contact /_index.md | 11 + .../zh/docs3-building/docs/mannual/Golang.md | 13 + .../zh/docs3-building/docs/mannual/Java.md | 13 + .../zh/docs3-building/docs/mannual/_index.md | 40 + .../docs3-building/docs/quickstart/_index.md | 38 + .../zh/docs3-building/docs/tasks/_index.md | 11 + .../docs/tasks/deploy-on-k8s.md | 11 + .../docs/tasks/dubbo-mesh-proxyless.md | 9 + .../docs/tasks/dubbo-mesh-sidecar.md | 9 + content/zh/docs3-building/docs/tasks/idl.md | 204 +++ .../docs/tasks/traffic-management.md | 15 + content/zh/docs3-building/docs/what/_index.md | 17 + .../docs3-building/docs/what/architecture.md | 77 + .../zh/docs3-building/docs/what/ecosystem.md | 57 + .../zh/docs3-building/docs/what/overview.md | 113 ++ .../zh/docs3-building/docs/what/usecases.md | 9 + .../zh/docs3-building/docs/whatsnew/_index.md | 31 + .../docs/whatsnew/background.md | 66 + .../zh/docs3-building/docs/whatsnew/mesh.md | 61 + .../docs/whatsnew/servicediscovery3.md | 11 + .../zh/docs3-building/docs/whatsnew/triple.md | 9 + .../zh/docs3-building/golang-sdk/_index.md | 8 + content/zh/docs3-building/java-sdk/_index.md | 23 + .../advanced-features-and-usage/_index.md | 8 + .../observability/_index.md | 8 + .../others/_index.md | 8 + .../others/graceful-shutdown.md | 41 + .../others/lifecycle.md | 6 + .../others/logger-strategy.md | 29 + .../others/service-container.md | 67 + .../others/set-host.md | 75 + .../performance/_index.md | 8 + .../performance/reference-config-cache.md | 43 + .../performance/simplify-registry-data.md | 262 ++++ .../performance/threading-model/_index.md | 8 + .../performance/threading-model/consumer.md | 45 + .../performance/threading-model/provider.md | 6 + .../advanced-features-and-usage/rpc/_index.md | 8 + .../rpc/accesslog.md | 21 + .../rpc/async-call.md | 222 +++ .../rpc/attachment.md | 38 + .../rpc/callback-parameter.md | 120 ++ .../rpc/context.md | 46 + .../rpc/echo-service.md | 29 + .../rpc/events-notify.md | 108 ++ .../rpc/generic-service.md | 151 ++ .../rpc/group-merger.md | 67 + .../rpc/local-mock.md | 106 ++ .../rpc/local-stub.md | 50 + .../rpc/parameter-validation.md | 197 +++ .../rpc/result-cache.md | 38 + .../security/_index.md | 8 + .../security/tls.md | 51 + .../security/token-authorization.md | 37 + .../service/_index.md | 8 + .../service/multi-versions.md | 49 + .../service/service-downgrade.md | 27 + .../service/service-group.md | 33 + .../concepts-and-architecture/_index.md | 8 + .../overall-architecture.md | 11 + .../service-discovery.md | 71 + .../service-invocation.md | 13 + .../java-sdk/contributing/_index.md | 8 + .../java-sdk/contributing/guide.md | 6 + .../java-sdk/contributing/overview.md | 6 + .../java-sdk/quick-start/_index.md | 8 + .../java-sdk/reference-manual/_index.md | 8 + .../reference-manual/config-center/_index.md | 7 + .../config-center/apollo/_index.md | 7 + .../config-center/apollo/guide.md | 6 + .../config-center/apollo/overview.md | 6 + .../config-center/nacos/_index.md | 7 + .../config-center/nacos/guide.md | 6 + .../config-center/nacos/overview.md | 6 + .../config-center/overview/_index.md | 79 ++ .../config-center/zookeeper/_index.md | 7 + .../config-center/zookeeper/guide.md | 7 + .../config-center/zookeeper/overview.md | 6 + .../reference-manual/config/_index.md | 7 + .../config/annotation/_index.md | 6 + .../config/annotation/description.md | 6 + .../config/annotation/guide.md | 97 ++ .../reference-manual/config/api/_index.md | 6 + .../config/api/description.md | 6 + .../reference-manual/config/api/guide.md | 315 +++++ .../reference-manual/config/env/_index.md | 6 + .../config/env/description.md | 6 + .../reference-manual/config/env/guide.md | 35 + .../config/overview/_index.md | 196 +++ .../config/properties/_index.md | 6 + .../config/properties/description.md | 6 + .../config/properties/guide.md | 301 ++++ .../reference-manual/config/xml/_index.md | 6 + .../config/xml/description.md | 349 +++++ .../reference-manual/config/xml/guide.md | 98 ++ .../reference-manual/config/yaml/_index.md | 6 + .../config/yaml/description.md | 6 + .../reference-manual/config/yaml/guide.md | 6 + .../reference-manual/graalvm/_index.md | 214 +++ .../metadata-center/_index.md | 7 + .../metadata-center/nacos/_index.md | 7 + .../metadata-center/nacos/guide.md | 6 + .../metadata-center/nacos/overview.md | 6 + .../metadata-center/overview/_index.md | 619 ++++++++ .../metadata-center/redis/_index.md | 7 + .../metadata-center/redis/guide.md | 7 + .../metadata-center/redis/overview.md | 6 + .../metadata-center/zookeeper/_index.md | 7 + .../metadata-center/zookeeper/guide.md | 6 + .../metadata-center/zookeeper/overview.md | 6 + .../reference-manual/performance/_index.md | 8 + .../performance/benchmarking.md | 106 ++ .../reference-manual/protocol/_index.md | 8 + .../reference-manual/protocol/dubbo/_index.md | 8 + .../reference-manual/protocol/dubbo/guide.md | 6 + .../protocol/dubbo/overview.md | 121 ++ .../reference-manual/protocol/grpc/_index.md | 8 + .../reference-manual/protocol/grpc/guide.md | 6 + .../protocol/grpc/overview.md | 23 + .../protocol/hessian/_index.md | 8 + .../protocol/hessian/guide.md | 74 + .../protocol/hessian/overview.md | 6 + .../reference-manual/protocol/http/_index.md | 8 + .../reference-manual/protocol/http/guide.md | 65 + .../protocol/http/overview.md | 6 + .../protocol/memcached/_index.md | 8 + .../protocol/memcached/guide.md | 6 + .../protocol/memcached/overview.md | 6 + .../protocol/overview/_index.md | 200 +++ .../reference-manual/protocol/redis/_index.md | 8 + .../reference-manual/protocol/redis/guide.md | 53 + .../protocol/redis/overview.md | 6 + .../reference-manual/protocol/rest/_index.md | 8 + .../reference-manual/protocol/rest/guide.md | 6 + .../protocol/rest/overview.md | 1243 +++++++++++++++++ .../reference-manual/protocol/rmi/_index.md | 8 + .../reference-manual/protocol/rmi/guide.md | 86 ++ .../reference-manual/protocol/rmi/overview.md | 6 + .../protocol/thrift/_index.md | 8 + .../reference-manual/protocol/thrift/guide.md | 45 + .../protocol/thrift/overview.md | 6 + .../protocol/triple/_index.md | 8 + .../reference-manual/protocol/triple/guide.md | 283 ++++ .../protocol/triple/overview.md | 6 + .../protocol/webservice/_index.md | 8 + .../protocol/webservice/guide.md | 119 ++ .../protocol/webservice/overview.md | 6 + .../java-sdk/reference-manual/qos/_index.md | 8 + .../java-sdk/reference-manual/qos/command.md | 243 ++++ .../java-sdk/reference-manual/qos/overview.md | 6 + .../reference-manual/registry/_index.md | 7 + .../registry/multicast/_index.md | 7 + .../registry/multicast/guide.md | 46 + .../registry/multicast/overview.md | 6 + .../registry/multiple-registry/_index.md | 7 + .../registry/multiple-registry/guide.md | 7 + .../registry/multiple-registry/overview.md | 6 + .../reference-manual/registry/nacos/_index.md | 7 + .../reference-manual/registry/nacos/guide.md | 135 ++ .../registry/nacos/overview.md | 6 + .../registry/overview/_index.md | 6 + .../reference-manual/registry/redis/_index.md | 7 + .../reference-manual/registry/redis/guide.md | 86 ++ .../registry/redis/overview.md | 6 + .../registry/zookeeper/_index.md | 7 + .../registry/zookeeper/guide.md | 171 +++ .../registry/zookeeper/overview.md | 6 + .../java-sdk/reference-manual/spi/_index.md | 8 + .../spi/description/_index.md | 6 + .../reference-manual/spi/description/cache.md | 88 ++ .../spi/description/cluster.md | 83 ++ .../spi/description/compiler.md | 60 + .../spi/description/config-center.md | 103 ++ .../spi/description/container.md | 67 + .../spi/description/dispatcher.md | 67 + .../spi/description/dubbo-spi.md | 693 +++++++++ .../spi/description/exchanger.md | 96 ++ .../spi/description/exporter-listener.md | 71 + .../spi/description/extension-factory.md | 62 + .../spi/description/filter.md | 94 ++ .../spi/description/invoker-listener.md | 69 + .../spi/description/load-balance.md | 70 + .../spi/description/logger-adapter.md | 89 ++ .../spi/description/merger.md | 73 + .../spi/description/monitor.md | 80 ++ .../spi/description/networker.md | 64 + .../reference-manual/spi/description/page.md | 61 + .../spi/description/protocol.md | 158 +++ .../spi/description/proxy-factory.md | 70 + .../spi/description/registry.md | 210 +++ .../spi/description/remoting.md | 127 ++ .../spi/description/router.md | 69 + .../spi/description/serialize.md | 77 + .../spi/description/status-checker.md | 69 + .../spi/description/telnet-handler.md | 82 ++ .../spi/description/threadpool.md | 66 + .../spi/description/validation.md | 80 ++ .../reference-manual/spi/overview/_index.md | 101 ++ .../2.x-to-3.x-compatibility-guide.md | 46 + .../upgrades-and-compatibility/_index.md | 8 + .../migration-service-discovery.md | 160 +++ .../migration-triple.md | 344 +++++ content/zh/users/_index.md | 82 +- content/zh/users/alibaba.md | 7 + content/zh/users/icbc.md | 7 + content/zh/users/xiaomi.md | 7 + layouts/partials/docs/prog-lang-home.html | 35 + layouts/partials/docs/toc-inline.html | 14 + layouts/shortcodes/docs/content_box.md | 27 + .../shortcodes/docs/prog-lang-home-content.md | 25 + layouts/shortcodes/page/header.html | 13 + layouts/shortcodes/page/page-meta-links.html | 3 + layouts/shortcodes/page/toc.html | 30 + static/imgs/v3/concepts/architecture-1.png | Bin 0 -> 15318 bytes static/imgs/v3/concepts/capacity.png | Bin 0 -> 103042 bytes static/imgs/v3/concepts/dubbo-hsf.png | Bin 0 -> 22917 bytes static/imgs/v3/concepts/dubbo3-goals.png | Bin 0 -> 51955 bytes static/imgs/v3/concepts/rpc.png | Bin 0 -> 22695 bytes static/imgs/v3/mesh/dubbo-proxyless.png | Bin 0 -> 118155 bytes static/imgs/v3/mesh/dubbo-sidecar.png | Bin 0 -> 127440 bytes static/imgs/v3/mesh/istio.jpg | Bin 0 -> 44471 bytes static/imgs/v3/mesh/mix-meshj.png | Bin 0 -> 126151 bytes 289 files changed, 14774 insertions(+), 69 deletions(-) create mode 100755 content/en/latest/admin/_index.md create mode 100644 content/en/latest/community/_index.md create mode 100755 content/en/latest/contribution-guidelines/_index.md create mode 100755 content/en/latest/contribution-guidelines/committer/_index.md create mode 100644 content/en/latest/contribution-guidelines/committer/apache-dubbo-page_dev.md create mode 100644 content/en/latest/contribution-guidelines/committer/label-an-issue-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/committer/new-committer-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/committer/release-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/committer/website-guide_dev.md create mode 100755 content/en/latest/contribution-guidelines/contributor/_index.md create mode 100644 content/en/latest/contribution-guidelines/contributor/become-a-committer_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/cla-signing-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/dubbo-extension-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/mailing-list-subscription-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/new-contributor-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/reporting-security-issues_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/software-donation-guide_dev.md create mode 100644 content/en/latest/contribution-guidelines/contributor/test-coverage-guide_dev.md create mode 100755 content/en/latest/download/_index.md create mode 100755 content/en/latest/facade-docs/_index.md create mode 100755 content/en/latest/facade-docs/contact /_index.md create mode 100755 content/en/latest/facade-docs/how/_index.md create mode 100644 content/en/latest/facade-docs/how/quick-start.md create mode 100644 content/en/latest/facade-docs/what/_index.md create mode 100644 content/en/latest/facade-docs/what/core-concepts.md create mode 100644 content/en/latest/facade-docs/what/ecosystem.md create mode 100644 content/en/latest/facade-docs/what/overview.md create mode 100755 content/en/latest/facade-docs/why/_index.md create mode 100755 content/en/latest/facade-docs/why/advantage.md create mode 100755 content/en/latest/golang-sdk/_index.md create mode 100755 content/en/latest/java-sdk/_index.md create mode 100755 content/en/latest/java-sdk/v2.x/_index.md create mode 100755 content/en/latest/java-sdk/v2.x/advanced-features-and-usage/_index.md create mode 100755 content/en/latest/java-sdk/v2.x/concepts-and-architecture/_index.md create mode 100644 content/en/latest/java-sdk/v2.x/concepts-and-architecture/users.md create mode 100755 content/en/latest/java-sdk/v2.x/contributing/_index.md create mode 100755 content/en/latest/java-sdk/v2.x/introduction/_index.md create mode 100644 content/en/latest/java-sdk/v2.x/introduction/users.md create mode 100755 content/en/latest/java-sdk/v2.x/quick-start/_index.md create mode 100644 content/en/latest/java-sdk/v2.x/quick-start/quick-start.md create mode 100755 content/en/latest/java-sdk/v2.x/reference-manual/_index.md create mode 100755 content/en/latest/java-sdk/v2.x/upgrades-and-compatibility/_index.md create mode 100755 content/en/latest/java-sdk/v3.x/_index.md create mode 100755 content/en/latest/java-sdk/v3.x/advanced-features-and-usage/_index.md create mode 100755 content/en/latest/java-sdk/v3.x/concepts-and-architecture/_index.md create mode 100644 content/en/latest/java-sdk/v3.x/concepts-and-architecture/users.md create mode 100755 content/en/latest/java-sdk/v3.x/contributing/_index.md create mode 100755 content/en/latest/java-sdk/v3.x/introduction/_index.md create mode 100644 content/en/latest/java-sdk/v3.x/introduction/users.md create mode 100755 content/en/latest/java-sdk/v3.x/quick-start/_index.md create mode 100755 content/en/latest/java-sdk/v3.x/reference-manual/_index.md create mode 100755 content/en/latest/java-sdk/v3.x/upgrades-and-compatibility/_index.md create mode 100755 content/en/latest/notices/_index.md create mode 100755 content/en/latest/notices/security.md create mode 100755 content/en/latest/pixiu/_index.md create mode 100755 content/zh/docs3-building/docs/_index.md create mode 100755 content/zh/docs3-building/docs/contact /_index.md create mode 100755 content/zh/docs3-building/docs/mannual/Golang.md create mode 100644 content/zh/docs3-building/docs/mannual/Java.md create mode 100755 content/zh/docs3-building/docs/mannual/_index.md create mode 100755 content/zh/docs3-building/docs/quickstart/_index.md create mode 100755 content/zh/docs3-building/docs/tasks/_index.md create mode 100644 content/zh/docs3-building/docs/tasks/deploy-on-k8s.md create mode 100644 content/zh/docs3-building/docs/tasks/dubbo-mesh-proxyless.md create mode 100644 content/zh/docs3-building/docs/tasks/dubbo-mesh-sidecar.md create mode 100644 content/zh/docs3-building/docs/tasks/idl.md create mode 100644 content/zh/docs3-building/docs/tasks/traffic-management.md create mode 100644 content/zh/docs3-building/docs/what/_index.md create mode 100644 content/zh/docs3-building/docs/what/architecture.md create mode 100644 content/zh/docs3-building/docs/what/ecosystem.md create mode 100644 content/zh/docs3-building/docs/what/overview.md create mode 100644 content/zh/docs3-building/docs/what/usecases.md create mode 100755 content/zh/docs3-building/docs/whatsnew/_index.md create mode 100644 content/zh/docs3-building/docs/whatsnew/background.md create mode 100644 content/zh/docs3-building/docs/whatsnew/mesh.md create mode 100644 content/zh/docs3-building/docs/whatsnew/servicediscovery3.md create mode 100644 content/zh/docs3-building/docs/whatsnew/triple.md create mode 100755 content/zh/docs3-building/golang-sdk/_index.md create mode 100755 content/zh/docs3-building/java-sdk/_index.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/_index.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/observability/_index.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/_index.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/graceful-shutdown.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/lifecycle.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/logger-strategy.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/service-container.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/set-host.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/_index.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/reference-config-cache.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/simplify-registry-data.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/_index.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/consumer.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/provider.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/_index.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/accesslog.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/async-call.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/attachment.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/callback-parameter.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/context.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/echo-service.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/events-notify.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/generic-service.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/group-merger.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-mock.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-stub.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/parameter-validation.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/result-cache.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/_index.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/tls.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/token-authorization.md create mode 100755 content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/_index.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/multi-versions.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-downgrade.md create mode 100644 content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-group.md create mode 100755 content/zh/docs3-building/java-sdk/concepts-and-architecture/_index.md create mode 100644 content/zh/docs3-building/java-sdk/concepts-and-architecture/overall-architecture.md create mode 100644 content/zh/docs3-building/java-sdk/concepts-and-architecture/service-discovery.md create mode 100644 content/zh/docs3-building/java-sdk/concepts-and-architecture/service-invocation.md create mode 100755 content/zh/docs3-building/java-sdk/contributing/_index.md create mode 100644 content/zh/docs3-building/java-sdk/contributing/guide.md create mode 100644 content/zh/docs3-building/java-sdk/contributing/overview.md create mode 100755 content/zh/docs3-building/java-sdk/quick-start/_index.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/overview/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/annotation/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/annotation/description.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/annotation/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/api/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/api/description.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/api/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/env/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/env/description.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/env/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/overview/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/properties/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/properties/description.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/properties/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/xml/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/xml/description.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/xml/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/yaml/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/yaml/description.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/config/yaml/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/graalvm/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/overview/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/performance/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/performance/benchmarking.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/_index.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/http/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/http/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/http/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/overview/_index.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/qos/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/qos/command.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/qos/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/overview/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/redis/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/redis/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/redis/overview.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/guide.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/overview.md create mode 100755 content/zh/docs3-building/java-sdk/reference-manual/spi/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/_index.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/cache.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/cluster.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/compiler.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/config-center.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/container.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/dispatcher.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/dubbo-spi.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/exchanger.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/exporter-listener.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/extension-factory.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/filter.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/invoker-listener.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/load-balance.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/logger-adapter.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/merger.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/monitor.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/networker.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/page.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/protocol.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/proxy-factory.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/registry.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/remoting.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/router.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/serialize.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/status-checker.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/telnet-handler.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/threadpool.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/description/validation.md create mode 100644 content/zh/docs3-building/java-sdk/reference-manual/spi/overview/_index.md create mode 100644 content/zh/docs3-building/java-sdk/upgrades-and-compatibility/2.x-to-3.x-compatibility-guide.md create mode 100755 content/zh/docs3-building/java-sdk/upgrades-and-compatibility/_index.md create mode 100644 content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-service-discovery.md create mode 100644 content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-triple.md create mode 100644 content/zh/users/alibaba.md create mode 100644 content/zh/users/icbc.md create mode 100644 content/zh/users/xiaomi.md create mode 100644 layouts/partials/docs/prog-lang-home.html create mode 100644 layouts/partials/docs/toc-inline.html create mode 100644 layouts/shortcodes/docs/content_box.md create mode 100644 layouts/shortcodes/docs/prog-lang-home-content.md create mode 100644 layouts/shortcodes/page/header.html create mode 100644 layouts/shortcodes/page/page-meta-links.html create mode 100644 layouts/shortcodes/page/toc.html create mode 100644 static/imgs/v3/concepts/architecture-1.png create mode 100644 static/imgs/v3/concepts/capacity.png create mode 100644 static/imgs/v3/concepts/dubbo-hsf.png create mode 100644 static/imgs/v3/concepts/dubbo3-goals.png create mode 100644 static/imgs/v3/concepts/rpc.png create mode 100644 static/imgs/v3/mesh/dubbo-proxyless.png create mode 100644 static/imgs/v3/mesh/dubbo-sidecar.png create mode 100644 static/imgs/v3/mesh/istio.jpg create mode 100644 static/imgs/v3/mesh/mix-meshj.png diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml index 62eb7334d047..b601693aa87a 100644 --- a/.github/workflows/build_and_deploy.yml +++ b/.github/workflows/build_and_deploy.yml @@ -4,11 +4,6 @@ on: push: branches: - master - pull_request: - types: [ opened, synchronize, reopened, closed ] - branches: - - master - jobs: build_and_deploy_job: diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss index a6fb42e0f422..d4d505426bdd 100644 --- a/assets/scss/_styles_project.scss +++ b/assets/scss/_styles_project.scss @@ -42,3 +42,7 @@ assets/scss/_styles_project.scss } } } + +.card-bottom { + margin-bottom: 20px; +} diff --git a/assets/scss/main.scss b/assets/scss/main.scss index 804a78cc887b..7dc275e0bf37 100644 --- a/assets/scss/main.scss +++ b/assets/scss/main.scss @@ -57,4 +57,20 @@ footer { } } +.o-lang-home__list { + & h4 { + margin-bottom: 0.75rem; + } + + & ul { + padding-left: 0.25rem; + padding-bottom: 0.5rem; + list-style: none; + } + + & li { + padding-bottom: 0.25rem; + } +} + @import "styles_project"; \ No newline at end of file diff --git a/config.toml b/config.toml index 0551c9a542aa..e7a91c31357a 100644 --- a/config.toml +++ b/config.toml @@ -105,6 +105,35 @@ url_latest_version = "https://dubbo.apache.org/zh/docs/" version = "v2.x" url = "https://dubbo.apache.org/zh/docsv2.7/" +[[params.versions]] + version = "v3.0(building)" + url = "https://dubbo.apache.org/zh/docs3-building/" + + +# Add Ecosystems +# en_ecosystem_docs_menu = "Advanced Docs" +# cn_ecosystem_docs_menu = "高级文档" +# +# [[params.ecosystems.docs]] +# ecosystem = "Dubbo Java SDK" +# en.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/en/java-sdk/" +# cn.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/cn/java-sdk/" +# +# [[params.ecosystems.docs]] +# ecosystem = "Dubbo Golang SDK" +# en.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/en/golang-sdk/" +# cn.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/cn/golang-sdk/" +# +# [[params.ecosystems.docs]] +# ecosystem = "Dubbo Pixiu" +# en.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/en/pixiu/" +# cn.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/cn/pixiu/" +# +# [[params.ecosystems.docs]] +# ecosystem = "Dubbo Admin" +# en.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/en/admin/" +# cn.docs.url = "https://chickenlj.github.io/incubator-dubbo-website/cn/admin/" + # Repository configuration (URLs for in-page links to opening issues and suggesting changes) github_repo = "https://github.com/apache/dubbo-website" # An optional link to a related project repo. For example, the sibling repository where your product code lives. @@ -128,6 +157,7 @@ sidebar_search_disable = true navbar_logo = true # Set to true to disable the About link in the site footer footer_about_disable = true +navbar_translucent_over_cover_disable = true # Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events. # This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set. diff --git a/content/en/docs/v2.7/user/configuration/xml.md b/content/en/docs/v2.7/user/configuration/xml.md index 25657fa47382..1d6485a5053e 100644 --- a/content/en/docs/v2.7/user/configuration/xml.md +++ b/content/en/docs/v2.7/user/configuration/xml.md @@ -7,7 +7,7 @@ description: "Configure Dubbo with XML" --- -> About the XML configuration items, see:[XML References](../references/xml). If you prefer use API directly instead of using Spring, see [API Configuration](../api). Want an example of how to use configuration, see [Quick Start](../../quick-start). +> About the XML configuration items, see:[XML References](content/old/en/docs/v2.7/user/references/xml). If you prefer use API directly instead of using Spring, see [API Configuration](../api). Want an example of how to use configuration, see [Quick Start](../../quick-start). ## provider.xml demo diff --git a/content/en/docs/v2.7/user/examples/context.md b/content/en/docs/v2.7/user/examples/context.md index 74d0aab94c59..d9a73b89e6d8 100644 --- a/content/en/docs/v2.7/user/examples/context.md +++ b/content/en/docs/v2.7/user/examples/context.md @@ -6,7 +6,7 @@ weight: 19 description: "Dubbo context" --- -All environment information of during the current call will put into the context,and all configuration information will convert the parameters of `URL` instance,Ref to the column of **URL parameters** at the [schema configuration reference book](../references/xml) +All environment information of during the current call will put into the context,and all configuration information will convert the parameters of `URL` instance,Ref to the column of **URL parameters** at the [schema configuration reference book](content/old/en/docs/v2.7/user/references/xml) `RpcContext` is a temporary status recorder of `ThreadLocal`,when accept `RPC` request or send `RPC` request,The `RpcContext` will be changed.Such as: `A` call `B` and `B` call `C`. On `B` machine,before `B` call `C`,the `RpcContext` will record the information of `A` call `B`.After `B` call `C`,the `RpcContext` record the information of `B` call `C`. diff --git a/content/en/docs/v2.7/user/recommend.md b/content/en/docs/v2.7/user/recommend.md index 1d8ae8d2af79..2e0724a65fa0 100644 --- a/content/en/docs/v2.7/user/recommend.md +++ b/content/en/docs/v2.7/user/recommend.md @@ -168,7 +168,7 @@ The Dubbo default value is used if completely not set up , please see the instru ``` -[^1]: Overlay rules for configuration: 1) The method level configuration has a higher priority than the interface level, that is to say,small scope have a high priority 2) Consumer side configuration has a higher priority than provider side, better than global configuration, the last one is the Dubbo hard coded configuration value([Dubbo configuration introduction](./configuration/properties.md)) +[^1]: Overlay rules for configuration: 1) The method level configuration has a higher priority than the interface level, that is to say,small scope have a high priority 2) Consumer side configuration has a higher priority than provider side, better than global configuration, the last one is the Dubbo hard coded configuration value([Dubbo configuration introduction](content/old/en/docs/v2.7/user/configuration/properties.md)) [^2]: With the first call, the call will be called 3 times [^3]: How to select a service to call when there are multiple Provider services [^4]: It means that consumer service can call the best provider service, and reduce to call the the slow provider service. diff --git a/content/en/docs/v2.7/user/references/registry/redis.md b/content/en/docs/v2.7/user/references/registry/redis.md index 7a0fab03e2a4..b09ca5f26608 100644 --- a/content/en/docs/v2.7/user/references/registry/redis.md +++ b/content/en/docs/v2.7/user/references/registry/redis.md @@ -73,7 +73,7 @@ A home-brewed service registry server is used in Alibaba instead of redis server ## Installation -Pls. refer to [redis install manual](http://dubbo.apache.org/en-us/docs/admin/install/redis.html) for how to install a redis based registry server. To set it up, specify `dubbo.registry.address` to `redis://127.0.0.1:6379` in `conf/dubbo.properties` for both provider and consumer (you can refer to [quick start](../../quick-start.md)) after install a redis server. +Pls. refer to [redis install manual](http://dubbo.apache.org/en-us/docs/admin/install/redis.html) for how to install a redis based registry server. To set it up, specify `dubbo.registry.address` to `redis://127.0.0.1:6379` in `conf/dubbo.properties` for both provider and consumer (you can refer to [quick start](content/old/en/docs/v2.7/user/quick-start.md)) after install a redis server. [^1]: [Redis](http://redis.io) is a high performance KV cache server diff --git a/content/en/docs/v2.7/user/references/registry/zookeeper.md b/content/en/docs/v2.7/user/references/registry/zookeeper.md index 1f7777e13589..b1ed1ff37ce2 100644 --- a/content/en/docs/v2.7/user/references/registry/zookeeper.md +++ b/content/en/docs/v2.7/user/references/registry/zookeeper.md @@ -142,7 +142,7 @@ Configure single zookeeper to serve as multiple registry servers: ## Zookeeper Installation -Pls. refer to [zookeeper install manual](../../../admin/install/zookeeper.md) for how to install zookeeper based registry server. To set it up, specify `dubbo.registry.address` to `zookeeper://127.0.0.1:2181` in `conf/dubbo.properties` for both provider and consumer (you can refer to [quick start](../../quick-start.md)) after install a zookeeper server. +Pls. refer to [zookeeper install manual](content/old/en/docs/v2.7/admin/install/zookeeper.md) for how to install zookeeper based registry server. To set it up, specify `dubbo.registry.address` to `zookeeper://127.0.0.1:2181` in `conf/dubbo.properties` for both provider and consumer (you can refer to [quick start](content/old/en/docs/v2.7/user/quick-start.md)) after install a zookeeper server. ## Declaration of Reliability diff --git a/content/en/docs/v3.0/languages/erlang/quick-start.md b/content/en/docs/v3.0/languages/erlang/quick-start.md index ce5001af09de..7ac5008c392a 100644 --- a/content/en/docs/v3.0/languages/erlang/quick-start.md +++ b/content/en/docs/v3.0/languages/erlang/quick-start.md @@ -38,7 +38,7 @@ into the project's `apps` directory. ``` ## Consumer configuration -Please reference [Reference Config](./reference.md) +Please reference [Reference Config](content/old/en/docs/v3.0/languages/erlang/reference.md) ## Init dubbolib in your project It is need you diff --git a/content/en/latest/admin/_index.md b/content/en/latest/admin/_index.md new file mode 100755 index 000000000000..58b4141f2fd4 --- /dev/null +++ b/content/en/latest/admin/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "admin" +linkTitle: "admin" +weight: 3 +--- + diff --git a/content/en/latest/community/_index.md b/content/en/latest/community/_index.md new file mode 100644 index 000000000000..95c7cbc6bf16 --- /dev/null +++ b/content/en/latest/community/_index.md @@ -0,0 +1,186 @@ +--- +title: Community +menu: + main: + weight: 40 +--- + + +{{% blocks/lead %}} +You may consider contributing more to the following side projects of Apache Dubbo: +{{% /blocks/lead %}} + +{{< blocks/section color="white" height="auto">}} +
+
+
+
+
dubbo-go
+
languages
+

golang implementation

+ GitHub > +
+
+
+
+
+
+
dubbo-js
+
language
+

js implementation

+ GitHub > +
+
+
+
+
+
+
dubbo-php
+
languages
+

php implementation

+ GitHub > +
+
+
+
+
+
+
dubbo-python
+
languages
+

python implementation

+ GitHub > +
+
+
+
+ +
+
+
+
+
dubbo-erlang
+
languages
+

erlang implementation

+ GitHub > +
+
+
+
+
+
+
dubbo-website
+
documentation
+

Dubbo Official Website

+ GitHub > +
+
+
+
+
+
+
dubbo-samples
+
samples
+

Dubbo java samples

+ GitHub > +
+
+
+
+
+
+
dubbo-go-samples
+
samples
+

Dubbo golang samples

+ GitHub > +
+
+
+
+ +
+
+
+
+
dubbo-admin
+
eco-system
+

Dubbo admin console

+ GitHub > +
+
+
+
+
+
+
dubbo-spring-boot
+
eco-system
+

Spring Boot Starter

+ GitHub > +
+
+
+
+
+
+
dubbo-go-hessian2
+
eco-system
+

Hessian2 golang library

+ GitHub > +
+
+
+
+
+
+
dubbo-proxy
+
eco-system
+

Dubbo proxy server

+ GitHub > +
+
+
+
+ +
+
+
+
+
dubbo-hessian-lite
+
eco-system
+

Hessian java library

+ GitHub > +
+
+
+
+
+
+
dubbo-getty
+
eco-system
+

Dubbo-go networking library

+ GitHub > +
+
+
+
+
+
+
dubbo-spi-extensions
+
SPI extensions
+

Dubbo extension collections

+ GitHub > +
+
+
+
+
+
+
dubbo-awesome
+
eco-system
+

Dubbo resource collections

+ GitHub > +
+
+
+
+{{< /blocks/section >}} + diff --git a/content/en/latest/contribution-guidelines/_index.md b/content/en/latest/contribution-guidelines/_index.md new file mode 100755 index 000000000000..b4267abff489 --- /dev/null +++ b/content/en/latest/contribution-guidelines/_index.md @@ -0,0 +1,94 @@ + +--- +type: docs +title: "Dubbo Contribution Guidelines" +linkTitle: "Contribution" +description: "Dubbo Contribution Guidelines" +menu: + main: + weight: 50 +--- + + +## 为 Dubbo 做贡献 + +Dubbo 是在非限制性的 Apache 2.0 许可下发布的,遵循标准的 Github 开发流程,使用Github追踪处理问题,并将 pull request 合并到 master 中。如果您想为 Dubbo 做贡献(即便是一些微小的),请不要犹豫,遵循下面的指导方针。 + +### 联系我们 + +#### 邮件列表 + + +邮件列表是讨论几乎所有与 Dubbo 有关事情的推荐方式。有关如何订阅的详细文档,请参阅[指南](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide)。 + +- [dev@dubbo.apache.org](mailto:dev-subscribe@dubbo.apache.org): 开发邮件列表,如果您在使用或开发Dubbo时遇到任何问题,您可以在此提出问题。 +- [commits@dubbo.apache.org](mailto:commits-subscribe@dubbo.apache.org): 所有提交将被发送到这个邮件列表。如果您对Dubbo的发展感兴趣,您可以订阅它。 +- [notification@dubbo.apache.org](mailto:notification-subscribe@dubbo.apache.org): 所有Github [issue](https://github.com/apache/dubbo/issues)和[pull request](https://github.com/apache/dubbo/pulls)的更新都会被发送到这个邮件列表。 + +### 报告问题 + +在报告任何问题时请遵循[模版](https://github.com/apache/dubbo/issues/new?template=dubbo-issue-report-template.md)。 + +### 代码约定 +我们的代码风格几乎和标准 Java 约定一致(流行IDE的默认设置满足这一点),主要有以下附加限制: + +* 如果当前行中有超过 120 个字符,则起一个新的行。 + +* 确保所有新的 .java 文件都有一个简单的 JavaDoc 类注释,其中至少有一个标识创建日期的标签,最好至少有一个关于该类的解释说明。 + +* 将ASF许可注释添加到所有新的 .java 文件(从项目中的现有文件复制) + +* 请确保没有将 @author 标记添加到您所贡献的文件中,因为 Apache 不使用 @author 标记,其他方式(如cvs)将公平地记录所有您的贡献。 + +* 为代码添加一些 JavaDoc,如果您更改命名空间,则需要一些 XSD DOC 元素。 + +* 对于新的特征或重要的修复程序,应该添加单元测试。 + +* 如果没有其他人使用您的分支,请将它与 master(或主项目中的其他目标分支)同步。 + +* 当编写提交消息时,请遵循这些约定,如果您正在修复一个现有问题,请在提交消息的末尾添加 Fixes XXX(其中XXX是问题编号)。 + +### 贡献流程 + +这是一个贡献者工作流程的大致说明: + +* 克隆当前项目 +* 从希望贡献的分支上创新新的分支,通常是 master 分支。 +* 提交您的更改。 +* 确保提交消息的格式正确。 +* 将新分支推送到您克隆的代码库中。 +* 执行检查表 [pull request模版](https://github.com/apache/dubbo/blob/master/PULL_REQUEST_TEMPLATE.md)。 +* 在提交 pull request 请求前, 请将您克隆的代码和远程代码库同步,这样您的 pull request 会简单清晰。具体操作如下: +``` +git remote add upstream git@github.com:apache/dubbo.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 请求到 apache/dubbo 并等待回复。 + +谢谢您的贡献! + +### 代码风格 + + +我们提供了 IntelliJ idea 的模版文件[dubbo_codestyle_for_idea.xml](https://github.com/apache/dubbo/tree/master/codestyle/dubbo_codestyle_for_idea.xml),您可以将它导入到IDE。 + +如果使用 Eclipse,可以通过参考该文件手动配置。 + +**注意事项** + +使用 dubbo_codestyle_for_idea.xml 为你的 IDEA 设置代码格式是贡献代码前至关重要的一个步骤,否则你将会无法通过 Travis CI 的代码风格校验,下面几个步骤给你演示了如何配置代码格式: + +1. 进入菜单页 `Editor > Code Style` +2. 在 Code Style 页面的 scheme 菜单中点击 manage profiles 选项 +在下拉列表中选择 `Import Scheme`, 接着选择 `IntelliJ IDEA code style XML` 导入 xml 文件 +3. 输入你的格式名称,方便在不同工程之间进行识别,最后别忘了 ⏎ 来保存更改. + +设置完成后,IDEA 会帮助你自动 reformat 代码 + + + + diff --git a/content/en/latest/contribution-guidelines/committer/_index.md b/content/en/latest/contribution-guidelines/committer/_index.md new file mode 100755 index 000000000000..e68c97ab2eb5 --- /dev/null +++ b/content/en/latest/contribution-guidelines/committer/_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "Committer Guidelines" +linkTitle: "Committer Guidelines" +description: "Dubbo Committer Guidelines" +weight: 2 +--- + + + diff --git a/content/en/latest/contribution-guidelines/committer/apache-dubbo-page_dev.md b/content/en/latest/contribution-guidelines/committer/apache-dubbo-page_dev.md new file mode 100644 index 000000000000..b7cf70f36c4a --- /dev/null +++ b/content/en/latest/contribution-guidelines/committer/apache-dubbo-page_dev.md @@ -0,0 +1,34 @@ +--- +type: docs +title: "官方 Dubbo 主页的维护" +linkTitle: "官方主页" +weight: 5 +--- + + +Apache 有一个官方的网站,用来维护所有的孵化项目的信息。每一个孵化项目在这个网站下都有一个信息页。 +Dubbo 的信息页地址是 [https://incubator.apache.org/projects/dubbo.html](https://incubator.apache.org/projects/dubbo.html)。 + +当项目发生比较大的变化,比如新的 committer 的加入,新的 PMC 的当选,或是新版本的 Release 等,都需要将这些更新信息维护到这个页面。 + +这个官方网站的项目地址是[https://svn.apache.org/repos/asf/incubator/public/trunk](https://svn.apache.org/repos/asf/incubator/public/trunk)。 + +维护这个页面的方法如下: + +1.安装 SVN。若是 Mac OS X 系统或是 Linux 系统,则自带了 SVN。若是 Windows 系统,则请首先自行安装 SVN。 + +2.用 SVN 将这个[项目](https://svn.apache.org/repos/asf/incubator/public/trunk) checkout 下来 。 + +3.修改 content/projects/dubbo.xml 文件,并保存。 + +4.安装 ANT。执行 trunk 目录下的 build.sh 或者 build.bat 脚本构建项目。 + +5.构建完成后,可以用浏览器打开 target/site/projects/dubbo.html 文件,预览修改是否生效。 + +6.用 SVN 的 commit 命令将 dubbo.xml 文件提交到服务器,并且不要提交 dubbo.html 文件(因为服务器端会定时自动构建)。 +此过程会要求输入Apache id和密码。 + +参考: + +1. http://incubator.apache.org/guides/website.html +2. https://svn.apache.org/repos/asf/incubator/public/trunk/README.txt diff --git a/content/en/latest/contribution-guidelines/committer/label-an-issue-guide_dev.md b/content/en/latest/contribution-guidelines/committer/label-an-issue-guide_dev.md new file mode 100644 index 000000000000..fba6232e3d35 --- /dev/null +++ b/content/en/latest/contribution-guidelines/committer/label-an-issue-guide_dev.md @@ -0,0 +1,32 @@ +--- +type: docs +title: "给问题打标签" +linkTitle: "问题标签" +weight: 3 +--- + +如果您正在处理一个问题,请记得**给这个问题标记一个或者多个您认为有意义的标签**。有了标签,其他开发人员就会很轻松地识别出问题,以便对其进行分类并跟踪进度。 + +对于需要编码和发版修复的 issues 和 pull requests,需要您**将其标记为 [milestone](https://github.com/apache/incubator*dubbo/milestones)**。 + +一些常用的标签: + +* 请求帮助 + * help wanted + * good first issue +* 优先级 + * priority/blocker + * priority/high + * priority/low + * priority/normal +* 状态 + * status/need-triage + * status/DO-NOT-MERGE + * status/READY-TO-MERGE + * status/invalid + * status/wontfix +* 类型 + * type/bug + * type/documentation + * type/enhancement + * type/feature diff --git a/content/en/latest/contribution-guidelines/committer/new-committer-guide_dev.md b/content/en/latest/contribution-guidelines/committer/new-committer-guide_dev.md new file mode 100644 index 000000000000..806a5c3f2693 --- /dev/null +++ b/content/en/latest/contribution-guidelines/committer/new-committer-guide_dev.md @@ -0,0 +1,87 @@ +--- +type: docs +title: "Apache 提交者注册流程" +linkTitle: "注册流程" +weight: 1 +--- + + +## 一、Apache 提交者的产生 + +### 项目孵化初始化提交者 + +项目孵化阶段,在孵化项目提案中,会有初始化提交者列表这一选项。确认你是初始化提交者的一员。项目在 apache 孵化器社区投票通过后,提交者可以开始准备注册账户了。可以参看[孵化器 wiki](https://wiki.apache.org/incubator/) + +### 活跃的贡献者被选举为提交者 + +在后期的开发过程中,活跃的贡献者可以被选举为提交者。见[如何成为 committer](https://www.apache.org/dev/new-committers-guide.html#becoming-a-committer) + +## 二、个人开发者提交 ICLA + +### 1、选择 apache id +在[ apache 提交者列表页](http://people.apache.org/committer-index.html)查看已经注册过的 apache id, + +### 2、个人提交者授权协议(ICLA): +下载[ ICLA 模板](https://www.apache.org/licenses/icla.pdf),查找可用的 id。将 icla.pdf 个人信息填写正确后打印,签名、扫描、并当做附件发送邮件给秘书 secretary@apache.org,秘书会帮忙创建 apache 用户 id。同时会创建一个 your_id@apache.org 的邮箱,可以在[ apache 提交者列表页](http://people.apache.org/committer-index.html)查看查找用户是否已经创建。 + +### 3、导师帮助提交用户id创建请求 +导师将帮助提交 apache 账户创建请求给 root 邮件组,会有人帮助建立 id。一般需要2天时间账户会建立,请等待并在[ apache 提交者列表页](http://people.apache.org/committer-index.html)查看查找用户是否已经创建。 + +## 三、加入apache开发者组 +1. 登陆 [Apache 账户工具](https://id.apache.org/),在登陆页面点击"忘记密码"设置始化密码,会有一封密码重置邮件发送到 forward 邮箱(在孵化项目提案中提交的开发者邮件) +2. 关于 apache 邮箱:apache.org 邮箱并没有自己的邮件内容存储服务器。它需要借用其他邮件提供商的邮件内容存储、分发功能。在很多投票环节是建议使用 apache 邮箱的。 + 这里就有一个问题,怎么在其它邮箱里面配置 apache.org 邮箱转发功能: + * 收件箱:收取发送到 apache.org 的邮件。这个在第一步配置好 Apache 账户工具的 forward 邮箱就可以用 forward 邮箱收取邮件了 + * 发件箱:将发出的邮件显示发件邮箱为 apache.org 邮箱。请参考:[设置 apache 邮箱指南](https://reference.apache.org/committer/email)和[ gmail 邮箱设置方式](http://gmailblog.blogspot.com/2009/07/send-mail-from-another-address-without.html)。 其他邮箱服务的设置方式不方便找到,gmail 的最方便,建议换成 gmail 邮箱(不是广告)。 +3. 修改编辑页面的 homepage url,[apache 提交者列表页](http://people.apache.org/committer-index.html)中你的账户能加主页链接。 +4. 修改编辑页面的 github 账户(username),提交确认后两个小时内会有邮件邀请你加入 github.com/apache-committers 组。这期间可以阅读[ ASF 工作方式](http://www.apache.org/foundation/how-it-works.html#developers)以对 ASF 开发做一些基本了解。 +## 四、提交者获得对项目的写权限 + +[GitBox 账户链接工具](https://gitbox.apache.org/setup/)的操作 + +### Apache账户授权 +按照提示授权对 Apache 账户的 OAuth 协议登入 + +### Github账户授权 +按照提示授权对 github 账户的 OAuth 协议登入 + +### 在 github.com 设置 github 账户两因素授权(2FA) +按照[授权 GitHub 2FA wiki](https://help.github.com/articles/configuring-two-factor-authentication-via-a-totp-mobile-app/) 操作如下: +* 在手机安装 “google 身份验证器” app +* 按照[授权 GitHub 2FA wiki](https://help.github.com/articles/configuring-two-factor-authentication-via-a-totp-mobile-app/) 一步一步操作。 + + 在[两因素授权验证](https://github.com/settings/two_factor_authentication/verify)界面,不建议选择用手机扫描二维码,因为有些手机会扫描不出来。 + 请打开手机 “google 身份验证器” app,点“+”选择“输入提供的秘钥”: 在“账户名” input 框写入 github 账户。在“您的秘钥” input 框写入:打开的网页中 "enter this text code" 链接里面的文本。在 app 中点击"添加" 后,将为此账户生成6位数字动态。将此6位数字写入网页中的文本框,然后点 “Enable”。这样 2FA 就设置成功了。 + +* 退出并重新登陆 github,输入用户名、密码后会多一步动态密码的填写,该动态密码就是 google 身份验证器上面的动态密码 + +* 大概需要半个小时,会有邮件通知你已经加入了 xx project-committers 开发者组。你也可以进入 [apache teams](https://github.com/orgs/apache/teams) 页面查看。 + +* 2FA 提交后,你已经 clone 的项目会有权限校验问题,解决方法为下面二选一: + * 申请 Access Token: + 在 github 上生成 access token 后,指令行需要密码的地方就粘贴token。 + 参考官网[帮助链接一](https://help.github.com/articles/https-cloning-errors/#provide-access-token-if-2fa-enabled)和[帮助链接二](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) + * 改用 ssh: + 在命令行执行 ssh-keygen 命令, 然后把pub文件中的内容粘贴到 github 上 + +* 注意:一定要保证 github 的 2FA 为 "enable" 状态。当你将 2FA 设置为 "off" 时候,将会被对应的 apache committer 写权限组除名,直到你再次设置成功为止。 + +## 五、其他 + +### The Apache Way +详情请参考 [wiki](http://apache.org/foundation/governance/) + +社区重于代码,如果某问题或者方案没有在社区(邮件列表)讨论过,就当没有发生过 + +### 添加你的名字 + +请访问孵化器[主页](https://incubator.apache.org/projects/dubbo.html)将你的名字添加到上面. 具体可以参考这个[文档](http://dubbo.apache.org/docs/3.0/zh-cn/docs/developers/committer-guide/apache-dubbo-page_dev.html). + +请访问 Dubbo[官方网站](http://dubbo.apache.org/docs/3.0/zh-cn/docs/developers/developers/developers_dev.html) 将你的名字添加到上面. + +### 小福利 + +Jetbrains 给 apache 提交者一个小福利,就是可以免费使用 idea 的全产品系列。具体注册地址为:https://www.jetbrains.com/shop/eform/apache?product=ALL + +### 相关 wiki +https://www.apache.org/dev/new-committers-guide.html diff --git a/content/en/latest/contribution-guidelines/committer/release-guide_dev.md b/content/en/latest/contribution-guidelines/committer/release-guide_dev.md new file mode 100644 index 000000000000..4cf48266147a --- /dev/null +++ b/content/en/latest/contribution-guidelines/committer/release-guide_dev.md @@ -0,0 +1,483 @@ +--- +type: docs +title: "如何准备 Apache Release" +linkTitle: "发版准备" +weight: 2 +--- + + +## 理解 Apache 发布的内容和流程 + +总的来说,Source Release 是 Apache 关注的重点,也是发布的必须内容;而 Binary Release 是可选项,Dubbo 可以选择是否发布二进制包到 Apache 仓库或者发布到 Maven 中央仓库。 + +请参考以下链接,找到更多关于 ASF 的发布指南: + +- [Apache Release Guide](http://www.apache.org/dev/release-publishing) +- [Apache Release Policy](http://www.apache.org/dev/release.html) +- [Maven Release Info](http://www.apache.org/dev/publishing-maven-artifacts.html) + +## 本地构建环境准备 + +主要包括签名工具、Maven 仓库认证相关准备 + +### 安装GPG + +详细文档请参见[这里](https://www.gnupg.org/download/index.html), Mac OS 下配置如下 + +```sh +$ brew install gpg +$ gpg --version #检查版本,应该为2.x +``` + +### 用gpg生成key + +根据提示,生成 key + + ```shell + $ gpg --full-gen-key + gpg (GnuPG) 2.0.12; Copyright (C) 2009 Free Software Foundation, Inc. + This is free software: you are free to change and redistribute it. + There is NO WARRANTY, to the extent permitted by law. + + Please select what kind of key you want: + (1) RSA and RSA (default) + (2) DSA and Elgamal + (3) DSA (sign only) + (4) RSA (sign only) + Your selection? 1 + RSA keys may be between 1024 and 4096 bits long. + What keysize do you want? (2048) 4096 + Requested keysize is 4096 bits + Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years + Key is valid for? (0) + Key does not expire at all + Is this correct? (y/N) y + + GnuPG needs to construct a user ID to identify your key. + + Real name: Robert Burrell Donkin + Email address: rdonkin@apache.org + Comment: CODE SIGNING KEY + You selected this USER-ID: + "Robert Burrell Donkin (CODE SIGNING KEY) " + + Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O + You need a Passphrase to protect your secret key. # 填入密码,以后打包过程中会经常用到 + ``` + +### 查看 key id + +```sh +$ gpg --list-keys +pub rsa4096/28681CB1 2018-04-26 # 28681CB1就是key id +uid [ultimate] liujun (apache-dubbo) +sub rsa4096/D3D6984B 2018-04-26 + +# 通过key id发送public key到keyserver +$ gpg --keyserver pgpkeys.mit.edu --send-key 28681CB1 +# 其中,pgpkeys.mit.edu为随意挑选的keyserver,keyserver列表为:https://sks-keyservers.net/status/,为相互之间是自动同步的,选任意一个都可以。 +``` +如果有多个 public key,设置默认 key。修改`~/.gnupg/gpg.conf` + +```sh +# If you have more than 1 secret key in your keyring, you may want to +# uncomment the following option and set your preferred keyid. +default-key 28681CB1 +``` +如果有多个 public key, 也可以删除无用的 key: + +```sh +### 先删除私钥,再删除公钥 +$ gpg --yes --delete-secret-keys shenglicao2@gmail.com ###老的私钥,指明邮箱即可 +$ gpg --delete-keys 1808C6444C781C0AEA0AAD4C4D6A8007D20DB8A4 +``` + +> PS: 最新版本经过实测,本地没有gpg.conf这个文件,因此如果在执行过程中遇到签名失败,可以参考这个文章:https://blog.csdn.net/wenbo20182/article/details/72850810 或 https://d.sb/2016/11/gpg-inappropriate-ioctl-for-device-errors + +由于公钥服务器没有检查机制,任何人都可以用你的名义上传公钥,所以没有办法保证服务器上的公钥的可靠性。 +通常,你可以在网站上公布一个公钥指纹,让其他人核对下载到的公钥是否为真。 +```sh +# fingerprint参数生成公钥指纹: +$ gpg --fingerprint liujun +pub rsa4096 2019-10-17 [SC] + 1376 A2FF 67E4 C477 5739 09BD 7DB6 8550 D366 E4C0 +uid [ultimate] liujun (CODE SIGNING KEY) +sub rsa4096 2019-10-17 [E] +``` +登录 https://id.apache.org, 将上面的 fingerprint (即 1376 A2FF 67E4 C477 5739 09BD 7DB6 8550 D366 E4C0) +粘贴到自己的用户信息中 OpenPGP Public Key Primary Fingerprint + +### 设置 Apache 中央仓库 + +Dubbo 项目的父 pom 为 Apache pom(2.7.0 以上版本需要,2.6.x 发布版本不需要此操作) + +```xml + +org.apache +apache +19 + +``` + + 添加以下内容到 .m2/settings.xml + 所有密码请使用 [maven-encryption-plugin](http://maven.apache.org/guides/mini/guide-encryption.html)加密后再填入 + +```xml + +... + + + + apache.snapshots.https + + + + + + apache.releases.https + + + + ... + + + gpg.passphrase + + + + +``` + +## 打包&上传 + +### 准备分支 + +从主干分支拉取新分支作为发布分支,如现在要发布$`{release_version}`版本,则从2.6.x拉出新分支`${release_version}-release`,此后`${release_version}` Release Candidates涉及的修改及打标签等都在`${release_version}-release`分支进行,最终发布完成后合入主干分支。 + +### 编译打包 + +首先,在`${release_version}-release`分支验证maven组件打包、source源码打包、签名等是否都正常工作。**2.6.x记得要使用1.6进行编译打包** + +```shell +$ mvn clean install -Prelease +$ mvn deploy +``` + +上述命令将snapshot包推送到maven中央仓库 + +### ~~用maven-release-plugin发布~~ (`废弃`,参考后一步) + +~~先用dryRun验证是否ok~~ + +```shell +$ mvn release:prepare -Prelease -Darguments="-DskipTests" -DautoVersionSubmodules=true -Dusername=YOUR GITHUB ID-DdryRun=true +``` + +~~验证通过后,执行release:prepare~~ + +```shell +$ mvn release:clean +$ mvn release:prepare -Prelease -Darguments="-DskipTests" -DautoVersionSubmodules=true -Dusername=YOUR GITHUB ID -DpushChanges=false +``` + +> 执行release插件时,如果指定了`-DpushChanges=true`, 插件会自动提交到远端的GitHub仓库中,此时就需要输入GitHub的密码,注意不是输入web页面的登录密码,而是一个`Personal access tokens`,获取方式详见[这里](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) + +> 这里有一点要注意的是tag, 在执行过程中,需要选择发布的artifactId, 下一个版本artifactId以及发布版本的tag, tag默认的是dubbo-parent-xxxx,需要改成dubbo-xxxx + +执行完上述步骤后,你会发现: +1. `source-release.zip` 和 `bin-release.zip`包已经生成在`dubbo-distribution`目录下,请解压并检查文件是否完整 +2. 本地已经打出相应的tag,同时新增一个commit,名叫`[maven-release-plugin] prepare release dubbo-x.x.x` +3. 分支版本自动升级为`${release_version+1}-SNAPSHOT`,同时新增一个commit,名叫`[[maven-release-plugin] prepare for next development iteration` + +> 如果指定了`-DpushChanges=true`, 则本地提交会自动推送到远端的GitHub仓库。根据经验,建议不要指定为true,请设置为false,待本地检查通过之后再手动提交 + +~~执行release:perform,做staging发布~~ + +```shell +$ mvn -Prelease release:perform -Darguments="-DskipTests" -DautoVersionSubmodules=true -Dusername=YOUR GITHUB ID +``` + +此时插件会自动下载远端的tag对应的源码,编译后,将所有Artifacts发布到配置的远程[maven仓库](http://repository.apache.org),处于staging状态。 + +### 使用mvn deploy进行deploy + +> 要求:maven 3.5+ + +修改pom文件中的版本号,从2.7.x-SNAPSHOT改为2.7.x, 目前有3个地方需要修改。建议全文搜索。 + +```shell +$ mvn clean install -Prelease +$ mvn deploy -Prelease -DskipTests +``` + +所有被deploy到远程[maven仓库](http://repository.apache.org)的Artifacts都会处于staging状态 + +#### 注意点 + +- 在deploy执行过程中,有可能因为网络等原因被中断,如果是这样,可以重新开始执行。 +- deploy执行到maven仓库的时候,请确认下包的总量是否正确。多次出现了包丢失的情况,特别是dubbo-parent包。 + + +## 准备Apache发布 + +1. 准备svn本机环境(Apache使用svn托管项目的发布内容) + +2. 将dubbo checkout到本地目录 + + ```shell + $ svn checkout https://dist.apache.org/repos/dist/dev/dubbo + # 假定本地目录为 ~/apache/dubbo + ``` + +3. 当前发布版本为${release_version},新建目录 + + ```shell + $ cd ~/apache/dubbo # dubbo svn根目录 + $ mkdir ${release_version} + ``` + +4. 添加public key到[KEYS](https://dist.apache.org/repos/dist/dev/dubbo/KEYS)文件并提交到SVN仓库(第一次做发布的人需要做这个操作,具体操作参考KEYS文件里的说明)。KEYS主要是让参与投票的人在本地导入,用来校验sign的正确性 + + ```sh + $ (gpg --list-sigs && gpg --armor --export ) >> KEYS + ``` + +5. 拷贝`dubbo-distribution/dubbo-apache-release/target`下的source相关的包到svn本地仓库`dubbo/${release_version}` + +6. 生成sha512签名 + + 针对`src.zip` + + ```shell + $ shasum -a 512 apache-dubbo-${release_version}-src.zip >> apache-dubbo-${release_version}-src.zip.sha512 + ``` + + 针对`bin-release.zip`,需要增加`-b`参数,表明是一个二进制文件 + + ```shell + $ shasum -b -a 512 apache-dubbo-${release_version}-bin.zip >> apache-dubbo-${release_version}-bin.zip.sha512 + ``` + + +7. 如果有binary release要同时发布 + + 在`dubbo-distribution/dubbo-apache-release/target`目录下,拷贝`bin.zip`以及`bin.zip.asc`到svn本地仓库`dubbo/${release_version}`,参考第6步,生成sha512签名。 + +8. 提交到Apache svn + + ```shell + $ svn status + $ svn commit -m 'prepare for ${release_version} RC1' + ``` + +9. 关闭Maven的staging仓库 + + 此步骤为发布2.7.0及以上版本必须要的步骤。在此之前请先确保所有的artifact都是ok的。登录http://repository.apache.org,点击左侧的`Staging repositories`,然后搜索Dubbo关键字,会出现一系列的仓库,选择你最近上传的仓库,然后点击上方的Close按钮,这个过程会进行一系列检查,检查通过以后,在下方的Summary标签页上出现一个连接,请保存好这个链接,需要放在接下来的投票邮件当中。链接应该是类似这样的: https://repository.apache.org/content/repositories/orgapachedubbo-1015 + + > 请注意点击Close可能会出现失败,通常是网络原因,只要重试几次就可以了。可以点击Summary旁边的Activity标签来确认。 + +## 验证Release Candidates + +详细的检查列表请参考官方的[check list](https://wiki.apache.org/incubator/IncubatorReleaseChecklist) + +首先,从一下地址下载要发布的Release Candidate到本地环境: + +
+https://dist.apache.org/repos/dist/dev/dubbo/${release_version}/
+
+ +然后,开始验证环节,验证包含但不限于以下内容和形式 + +### 检查签名和hash等信息 + +#### 检查sha512哈希 + +```sh +$ shasum -c apache-dubbo-${release_version}-src.zip.sha512 +$ shasum -c apache-dubbo-${release_version}-bin.zip.sha512 +``` + +#### 检查gpg签名 + +如果是第一次检查,需要首先导入公钥。 + +```sh + $ curl https://dist.apache.org/repos/dist/dev/dubbo/KEYS >> KEYS # download public keys to local directory + $ gpg --import KEYS # import keys + $ gpg —-edit-key liujun + > trust # type trust command +``` +然后使用如下命令检查签名 + + ```sh +gpg --verify apache-dubbo-3.0.4-src.zip.asc apache-dubbo-3.0.4-src.zip +gpg --verify apache-dubbo-3.0.4-bin.zip.asc apache-dubbo-3.0.4-bin.zip + ``` + + +### 检查源码包的文件内容 + +解压缩`apache-dubbo-${release_version}-src.zip`,进行如下检查: + +- DISCLAIMER exists +- LICENSE and NOTICE exists and contents are good +- All files and no binary files exist +- All files has standard ASF License header +- Can compile from source +- All unit tests can pass + ```sh + mvn clean test # This will run all unit tests + # you can also open rat and style plugin to check if every file meets requirements. + mvn clean test -Drat.skip=false -Dcheckstyle.skip=false + ``` +- Release candidates match with corresponding tags, you can find tag link and hash in vote email. + - check the version number in pom.xml are the same + - check there are no extra files or directories in the source package, for example, no empty directories or useless log files,这里需要注意换行符是否一致 + `diff -r a rc_dir tag_dir` + - check the top n tag commits, dive into the related files and check if the source package has the same changes + +### 检查三方依赖的合规性 + +按照Apache基金会合规性规定,源码或者是二进制分发包中均不能包含Category X的依赖,其中就常见的是包含了GPL/LGPL的依赖,即使是传递依赖也不行。因此在发版的时候需要通过以下的命令进行检查: + +```sh +mvn license:add-third-party -Dlicense.useMissingFile +find . -name THIRD-PARTY.txt | xargs grep -E 'GPL|General Public License' | grep -v Apache | grep -v MIT | grep -v CDDL +``` + +如果一个依赖提供了双协议或多重协议,可以选择与Apache最兼容的一个协议。 + +你可以参考此文章:[ASF第三方许可证策](https://apache.org/legal/resolved.html) + + +### 检查二进制包的文件内容 + +解压缩`apache-dubbo-${release_version}-bin.zip`,进行如下检查: + +* Check signatures are good +* LICENSE and NOTICE exists and contents are good + +注意,如果二进制包里面引入了第三方依赖,则需要更新LICENSE,加入第三方依赖的LICENSE,如果第三方依赖的LICENSE是Apache 2.0,并且对应的项目中包含了NOTICE,还需要更新NOTICE文件 + +## 进入投票 + +dubbo毕业之后,投票分只需要一次: + +1. Dubbo社区投票,发起投票邮件到dev@dubbo.apache.org。在社区开发者Review,经过至少72小时并统计到3个同意发版的binding票后(只有PMC的票才是binding),即可进入下一阶段的投票。 + +Dubbo社区投票邮件模板: + +```text +Hello Dubbo Community, + +This is a call for vote to release Apache Dubbo version 2.7.2. + +The release candidates: +https://dist.apache.org/repos/dist/dev/dubbo/2.7.2/ + +The staging repo: +https://repository.apache.org/content/repositories/orgapachedubbo-1005 + +Git tag for the release: +https://github.com/apache/dubbo/tree/dubbo-2.7.2 + +Hash for the release tag: +afab04c53edab38d52275d2a198ea1aff7a4f41e + +Release Notes: +https://github.com/apache/dubbo/releases/tag/untagged-4775c0a22c60fca55118 + +The artifacts have been signed with Key : 28681CB1, which can be found in the keys file: +https://dist.apache.org/repos/dist/dev/dubbo/KEYS + +The vote will be open for at least 72 hours or until necessary number of votes are reached. + +Please vote accordingly: + +[ ] +1 approve +[ ] +0 no opinion +[ ] -1 disapprove with the reason + +Thanks, +The Apache Dubbo Team +``` + + +宣布投票结果模板: +```text +We’ve received 3 +1 binding votes and one +1 non-binding vote: + ++1 binding, Ian Luo ++1 binding, Huxing Zhang ++1 binding, Jun Liu + ++1 non-binding, Jerrick + +I will start to release today. + +Best regards, +The Apache Dubbo Team +``` + +## 正式发布 + +1. 将[dev](https://dist.apache.org/repos/dist/dev/dubbo)目录下的发布包添加到[release](https://dist.apache.org/repos/dist/release/dubbo)目录下,KEYS有更新的,也需要同步更新。 +2. 删除[dev](https://dist.apache.org/repos/dist/dev/dubbo)目录下的发布包 +3. 删除[release](https://dist.apache.org/repos/dist/release/dubbo)目录下上一个版本的发布包,这些包会被自动保存在[这里](https://archive.apache.org/dist/dubbo) +4. 此步骤为发布2.7.0及以上版本必须要的步骤。在此之前请先确保所有的artifact都是ok的。登录http://repository.apache.org,点击左侧的`Staging repositories`,然后搜索Dubbo关键字,会出现一系列的仓库,选择你最近上传的仓库,然后点击上方的Release按钮. +5. 发布GitHub上的[release notes](https://github.com/apache/dubbo/releases) +6. 修改GitHub的Readme文件,将版本号更新到最新发布的版本 +7. 在官网下载[页面](http://dubbo.apache.org/en-us/blog/download.html)上添加最新版本的下载链接。最新的下载链接应该类似[这样](https://www.apache.org/dyn/closer.cgi?path=dubbo/$VERSION/apache-dubbo-$VERSION-source-release.zip). 同时更新以前版本的下载链接,改为类似[这样](https://archive.apache.org/dist/dubbo/$VERSION/apache-dubbo-$VERSION-bin-release.zip). 具体可以参考过往的[下载链接](https://github.com/apache/dubbo-website/blob/asf-site/blog/en-us/download.md) [可以参考] (https://github.com/apache/dubbo-website/pull/887) +8. 合并`${release-version}-release`分支到对应的主干分支, 然后删除相应的release分支,例如: `git push origin --delete 2.7.0-release` +9. 发邮件到 `dev@dubbo.apache.org` + 宣布release邮件模板: + +```text +Hello Community, + +The Apache Dubbo team is pleased to announce that the +2.6.6 has just been released. + +Apache Dubbo™ is a high-performance, java based, open source +RPC framework. Dubbo offers three key functionalities, which include +interface based remote call, fault tolerance & load balancing, and +automatic service registration & discovery. + +Both the source release[1] and the maven binary release[2] are available +now, you can also find the detailed release notes here[3]. + + +If you have any usage questions, or have problems when upgrading or find +any problems about enhancements included in this release, please don’t +hesitate to let us know by sending feedback to this mailing list or filing +an issue on GitHub[4]. + + +[1] http://dubbo.apache.org/en-us/blog/download.html +[2] https://repo1.maven.org/maven2/org/apache/dubbo/dubbo +[3] https://github.com/apache/dubbo/releases +[4] https://github.com/apache/dubbo/issues + +``` + + +## 完成Maven Convenient Binary发布(可选) + +**repository.apache.org** nexus仓库的权限已经申请,参见[jira](https://issues.apache.org/jira/browse/INFRA-16451) + +发布jar包到maven仓库,首先访问[repository.apache.org](https://repository.apache.org), 选择`staging repository`, 点击`release`按钮。等待一段时间之后,在[这里](https://repository.apache.org/content/repositories/releases/org/apache/dubbo/)确认完整性和正确性. 发布到Maven中央仓库则还需要等待一段时间。可以在[这里](https://repo.maven.apache.org/maven2/org/apache/dubbo)进行确认。 + +## FAQ + +#### gpg: signing failed: Inappropriate ioctl for device + +If you've encountered this error, try the following commands: + +``` +export GPG_TTY=$(tty) +``` diff --git a/content/en/latest/contribution-guidelines/committer/website-guide_dev.md b/content/en/latest/contribution-guidelines/committer/website-guide_dev.md new file mode 100644 index 000000000000..99a787f2bd84 --- /dev/null +++ b/content/en/latest/contribution-guidelines/committer/website-guide_dev.md @@ -0,0 +1,11 @@ +--- +type: docs +title: "网站向导" +linkTitle: "网站向导" +weight: 3 +--- + + +1. Apache Dubbo 的网站仓库是 https://github.com/apache/dubbo-website +2. 网站构建完毕后,它会被自动发布到 dubbo.apache.org,您也可以通过 https://selfserve.apache.org 手动触发(需要使用 Apache 账号登陆) + diff --git a/content/en/latest/contribution-guidelines/contributor/_index.md b/content/en/latest/contribution-guidelines/contributor/_index.md new file mode 100755 index 000000000000..720899649032 --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "Contributor Guidelines" +linkTitle: "Contributor Guidelines" +description: "Dubbo Contributor Guidelines" +weight: 1 +--- + + + diff --git a/content/en/latest/contribution-guidelines/contributor/become-a-committer_dev.md b/content/en/latest/contribution-guidelines/contributor/become-a-committer_dev.md new file mode 100644 index 000000000000..976f8d8f4edd --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/become-a-committer_dev.md @@ -0,0 +1,20 @@ +--- +type: docs +title: "如何成为 Dubbo Committer" +linkTitle: "成为 Committer" +weight: 1 +--- + +每个人都可以成为 Apache 项目的贡献者。作为一个贡献者只是意味着你对项目感兴趣并以某种方式做出贡献,从提出合理的问题(这些问题记录了项目并向开发人员提供反馈)到提供新的特性作为补丁。 + +如果你成为对一个项目有价值的贡献者,你有可能被邀请成为一个 committer。committer 是 ASF(Apache软件基金会)中用来表示提交特定项目的人的术语。它给你带来对项目仓库和资源写的权限。 + +在 Dubbo 社区,如果一个 committer 获得大量的优秀成绩,就可以被邀请加入项目管理委员会(PMC)。 + +当您不熟悉ASF使用的开源的开发过程时,有时难以理解的一点,就是我们更重视社区而不是代码。一个强大而健康的社区将受到尊重,成为一个有趣和有益的地方。更重要的是,一个多元化和健康的社区可以长时间的持续支持代码,即使个别公司在这个领域来来往往,也是如此。 + +更多详细信息可以在[这里](https://community.apache.org/contributors/)找到。 + +### 我可以贡献什么? + +请参阅[新的贡献者指南](/zh/docs/contribution-guidelines/contributor/new-contributor-guide_dev)。 diff --git a/content/en/latest/contribution-guidelines/contributor/cla-signing-guide_dev.md b/content/en/latest/contribution-guidelines/contributor/cla-signing-guide_dev.md new file mode 100644 index 000000000000..ad5df4088461 --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/cla-signing-guide_dev.md @@ -0,0 +1,30 @@ +--- +type: docs +title: "CLA 签署向导" +linkTitle: "CLA 签署向导" +weight: 2 +--- + + +以下情况,需要您签署 Apache ICLA: + +* 在 Dubbo 被捐赠给 Apache 之前,您已经为 Dubbo 作出了很多贡献,并且您以前没有签署过 Alibaba-CLA。 +* 您已经为 Dubbo 作出了很多贡献,并且您被邀请成为 Dubbo 提交者,且之前没有签署过 Alibaba-CLA 或者 Apache ICLA。 + +### 步骤 + +* 下载这篇 [pdf 文档](https://www.apache.org/licenses/icla.pdf) +* 编辑该文档,在必要的空格处填上适当的内容 +* 打印 +* 在打印好的文件上签字 +* 扫描 +* 发送一封邮件到secretary@apache.org,并抄送给private@dubbo.apache.org: + * 邮件标题为“ICLA submission” + * 请在邮件正文附上您的github账号链接 + * 请记得将您的ICLA文档放入邮件的附件里 + +### 空格填写必要说明 + +* Mailing address:首选英文格式的公司地址 +* preferred apache id(s):如果您被邀请成为一名提交者,那么需要您填写一个apache账号,否则,可以不填 +* notify project:Dubbo(意思就是Dubbo就是通知您签署ICLA的项目) \ No newline at end of file diff --git a/content/en/latest/contribution-guidelines/contributor/dubbo-extension-guide_dev.md b/content/en/latest/contribution-guidelines/contributor/dubbo-extension-guide_dev.md new file mode 100644 index 000000000000..d8c7443c5ddc --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/dubbo-extension-guide_dev.md @@ -0,0 +1,53 @@ +--- +type: docs +title: "扩展 Dubbo 向导" +linkTitle: "扩展 Dubbo" +weight: 5 +--- + + +Dubbo 使用微内核+插件的设计模式。内核只负责组装插件,Dubbo 的功能都是由扩展点(插件)实现,这就意味着 Dubbo 的所有功能都可以被用户定制的扩展所替代。 + +### Dubbo 生态系统 + +我们建议您将扩展加入到 Dubbo 生态系统。使用这种模式,可以使 Dubbo 的核心仓库更干净,并且可以减少维护工作。更少的代码也可以提高核心仓库的构建速度。 + +### 依赖 + +要实现您自己的 Dubbo 扩展,通常只需依赖 API jar 就可以满足您的需求。例如: + +```xml + + org.apache.dubbo + dubbo-serialization-api + ${dubbo.version} + +``` + +### Src指导 + +通常,要实现特殊的扩展,只需要参考[开发者指南](http://dubbo.apache.org/#/docs/dev/build.md?lang=en-us),实现Dubbo必要的接口和合适的扩展即可。除此之外,还有一些其它的事项需要注意: + +1. 良好的测试,您需要编写单元测试和冒烟测试以消除潜在的 bug。 +2. 没有警告,如有不可避免的警告,请使用 @SuppressWarnings 阻止它,但是请不要乱用。 +3. README。添加必要的自述以说明如何使用扩展,以及需要注意的事项。 +4. 许可证:请确保使用Apache License 2.0。 + +### 通知社区 + +1. 提交您的代码到 [github](https://github.com)。 +2. 加入邮件列表(建议)。点击[这里](https://github.com/apache/dubbo/wiki/Mailing-list-subscription-guide)查看如何加入邮件列表。 +3. 发送一封邮件到 dev@incubator.dubbo.apache.org 通知社区。 +4. 通常,发送邮件之后,社区会对您的扩展进行讨论,dubbo 组的管理员会联系您转移您的项目到 dubbo 生态系统。 + +### 转移项目到dubbo生态系统 + +1. dubbo 组的管理员会请您将您的项目的所有者转让给 dubbo。 +2. dubbo 组的管理员会在 dubbo 组下新建一个项目并邀请您加入到这个项目。 +3. 一旦您接受邀请,您可以将您的项目转移到 dubbo 组下的新项目里。 +4. dubbo 组的成员会对您的项目进行代码审查。随后,您可以对这些代码进行改进。 + + + + + diff --git a/content/en/latest/contribution-guidelines/contributor/mailing-list-subscription-guide_dev.md b/content/en/latest/contribution-guidelines/contributor/mailing-list-subscription-guide_dev.md new file mode 100644 index 000000000000..55300068bdd1 --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/mailing-list-subscription-guide_dev.md @@ -0,0 +1,87 @@ +--- +type: docs +title: "邮件列表订阅向导" +linkTitle: "邮件组向导" +weight: 3 +--- + + +Apache incubator 的 Dubbo 开发者邮件列表(dev@dubbo.apache.org)已经建立,请随时订阅并参考[^1]获取更多细节。 + +你也可以直接查看[历史邮件](https://lists.apache.org/list.html?dev@dubbo.apache.org) + +下面是一个关于 Dubbo 邮件列表订阅的简短指南: + +1. 发一封邮件到 dev-subscribe@dubbo.apache.org,其内容和标题均可为空。随后,您会收到一封邮件,其内容如下: + +``` +from: dev-help@dubbo.apache.org +reply-to: dev-sc.xxxxxxx.xxxxxxxx-hello=example.com@dubbo.apache.org +to: hello@example.com +date: Sat, Feb 24, 2018 at 3:12 PM +subject: confirm subscribe to dev@dubbo.apache.org +mailed-by: apache.org + +Hi! This is the ezmlm program. I'm managing the +dev@dubbo.apache.org mailing list. + +I'm working for my owner, who can be reached +at dev-owner@dubbo.apache.org. + +To confirm that you would like + + hello@example.com + +added to the dev mailing list, please send +a short reply to this address: + + dev-sc.xxxxxxx.xxxxxxxx-hello=example.com@dubbo.apache.org + +Usually, this happens when you just hit the "reply" button. +If this does not work, simply copy the address and paste it into +the "To:" field of a new message. + +or click here: + mailto:dev-sc.xxxxxxx.xxxxxxxx-hello=example.com@dubbo.apache.org + +... +``` + +2. 直接回复邮件,其内容和标题仍然可以为空,随后,您将再次收到一封邮件,其内容如下: + +``` +from: dev-help@dubbo.apache.org +to: hello@example.com +date: Sat, Feb 24, 2018 at 3:14 PM +subject: WELCOME to dev@dubbo.apache.org +mailed-by: apache.org + +Hi! This is the ezmlm program. I'm managing the +dev@dubbo.apache.org mailing list. + +I'm working for my owner, who can be reached +at dev-owner@dubbo.apache.org. + +Acknowledgment: I have added the address + + hello@example.com + +to the dev mailing list. + +Welcome to dev@dubbo.apache.org! + +Please save this message so that you know the address you are +subscribed under, in case you later want to unsubscribe or change your +subscription address. + +... +``` + +3. 到此,邮件列表订阅完毕,从现在开始,您将收到很多发送到该邮件列表的邮件,如果您有更多的问题,只需发送邮件到dev@dubbo.apache.org,就会有人回答您的问题。 + +4. 如果您想取消订阅,只需发送一封邮件到dev-unsubscribe@dubbo.apache.org,收到回复后,请按其指定步骤执行。 + +> 请注意:dev@dubbo.apache.org是有效的。dev@dubbo.incubator.apache.org已经不再使用。 + +[^1] https://apache.org/foundation/mailinglists.html#subscribing + diff --git a/content/en/latest/contribution-guidelines/contributor/new-contributor-guide_dev.md b/content/en/latest/contribution-guidelines/contributor/new-contributor-guide_dev.md new file mode 100644 index 000000000000..40a96d0fb8a6 --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/new-contributor-guide_dev.md @@ -0,0 +1,133 @@ +--- +type: docs +title: "新贡献者向导" +linkTitle: "新手向导" +weight: 2 +--- + + +这篇向导旨在给正在准备向 Dubbo 提交贡献的新手提供指导。 + +### 邮件列表描述 + +邮件列表是 Dubbo 官方推荐的讨论方式,所有与 Dubbo 相关的内容都可以在这里讨论,请点击 [issue](https://github.com/apache/dubbo/issues/1393) 了解更多关于邮件列表订阅的内容 + +如需订阅如下邮件列表,请参考 [邮件列表订阅向导](/zh/docs/contribution-guidelines/contributor/mailing-list-subscription-guide_dev) + +* dev@dubbo.apache.org:开发邮件列表,您在使用或者开发 Dubbo 的过程中遇到的任何问题,都可以在这里进行提问。 +* commits@dubbo.apache.org:所有的提交内容都会推送到这个邮件列表,如果您对 Dubbo 的进展感兴趣,可以订阅这个邮件列表。 +* issues@dubbo.apache.org:所有的 JIRA [issues](https://issues.apache.org/jira/projects/DUBBO/issues) 和修改信息都会推送到这个邮件列表。Dubbo 社区已经决定使用 github issues 代替 JIRA issues,因此大部分 issues 将由 github issues 进行跟踪。JIRA issues 用于跟踪 ASF 相关问题。 + +### 报告问题 + +### 贡献代码 +#### 贡献流程 +此贡献流程适用于所有的Apache Dubbo社区内容,包括但不限于dubbo(主干仓库)、dubbo admin、dubbo website。 + +以下以贡献dubbo(主干仓库)为例,详细说明贡献流程。 + +##### 1. **fork Apache/Dubbo 项目到您的github帐号下** + +##### 2. **克隆您fork的Dubbo代码仓库到您本地** +```shell +git clone ${your fork dubbo repo address,for example:https://github.com/${your github id}/dubbo.git} +cd dubbo +``` + +##### 3. **添加Apache/Dubbo仓库为upstream仓库** +```shell +git remote add upstream https://github.com/apache/dubbo.git + +git remote -v + + origin ${your fork dubbo repo address} (fetch) + origin ${your fork dubbo repo address} (push) + upstream https://github.com/apache/dubbo.git (fetch) + upstream https://github.com/apache/dubbo.git (push) + +git fetch origin +git fetch upstream +``` +##### 4. **我们的工作以issue为驱动,认领一个issue,或者创建一个issue并描述清楚要做什么。** +新人推荐标记为: `good first issue` 的 issue + +##### 5. **选择一个开发的基础分支,通常是 upstream/master, 并基于此创建一个新的本地分支** +upstream/master 分支是目前 2.7.x 版本的开发分支 + +```shell +# 从远程仓库创建分支到本地 +git checkout -b up-dev-issue#${issue-number} upstream/master +``` +为了避免一些不必要的麻烦,我们推荐以 "upstream" 中的分支为基础创建新的本地分支。 +可以以要做的事情的简单描述作为分支名(只要你能看懂就行),通常情况下我们会把issue号包含到分支名中,例如上面的 checkout 命令中的。 + +##### 6. **在本地新建的开发分支上进行各种修改** +首先请保证您阅读并正确设置Dubbo code style, 相关内容请阅读 [编码规范](#编码规范) 。 + +修改时请保证该本地分支上的修改仅和issue相关,并尽量细化,做到一个分支只修改一件事,一个PR只修改一件事。 + +可以在提交注释中添加"#issue号",将该提交与issue关联。 + +##### 7. **将您开发完成后的分支,上传到您fork的仓库** +```shell +git push origin up-dev-issue#${issue-number} +``` + +##### 8. **创建 pull request** + +* 参考[pull request template](https://github.com/apache/dubbo/blob/master/PULL_REQUEST_TEMPLATE.md)中的检查列表 + +Dubbo社区将会Review您的Pull Request,并可能提出修改意见,您可以根据修改意见回到步骤6进行修改,并使用步骤7进行重新提交。 + +##### 9. **如果没有问题,Dubbo社区将会把您的修改合并,恭喜您成为Dubbo的贡献者。** + +#### 特别说明: +* 开源项目一般都是以分支的方式工作,每件事情都创建一个分支。 +* 创建分支时,不要从本地仓库里的分支里创建,而是从指向主仓库的远程仓库创建。 +* 不要一直在同一个分支工作, 一个分支只做一件事情,不要在同一个分支做多件事情。 +* 一直在同一分支中修改,提交都会一直在该分支中。这样就会造成每次PR都会带着之前的所有被merge、未被merge的提交。 +* 一件事情可以是一个issue,也可以是一个issue中的部分(issue太大可以拆解)。 +* 一个分支(一件事情)只提一个PR。 +* 提了PR后,如果PR有问题需要修改,可以继续在这个PR关联的分支修改提交。在PR被merge前,向这个分支继提交都会进入这个PR。 +* 如果只是想纯更新代码,可以从主仓库提PR到你fork的仓库, 源选择主仓库里的分支,目标选你fork的仓库的分支。 +* 这种方式更新代码,你fork的仓库中会多一个提交。如果以你fork的仓库中的分支为源创建分支, 这个提交纪录会被带过去并会在PR中, 所以要以主仓库的分支为源创建分支。 +* issue 认领: 在要认领的issue中回复,明确表式你将处理这个issue。这样社区的PMC和Committer会把该issue assign给你。当然认领前先看下这个issue有没有被别人认领了。 + 为了方便,我们可以把认领的回复统一为: **@i will solve it@**, 当然这不是必须的。 + +#### 编码规范 +请按照[CONTRIBUTING.md](https://github.com/apache/dubbo/blob/master/CONTRIBUTING.md)中的编码规范对自己的代码进行检查。 +##### **代码约定** +我们的代码风格几乎和标准 Java 约定一致(流行IDE的默认设置满足这一点),主要有以下附加限制: +* 如果当前行中有超过 120 个字符,则起一个新的行。 +* 确保所有新的 .java 文件都有一个简单的 JavaDoc 类注释,最好至少有一个关于该类的解释说明。 +* 将ASF许可注释添加到所有新的 .java 文件(从项目中的现有文件复制) +* 请确保没有将 @author 标记添加到您所贡献的文件中,因为 Apache 不使用 @author 标记,其他方式(如cvs)将公平地记录所有您的贡献。 +* 为代码添加一些 JavaDoc,如果您更改命名空间,则需要一些 XSD DOC 元素。 +* 对于新的特征或重要的修复程序,应该添加单元测试。 +* 如果没有其他人使用您的分支,请将它与 master(或主项目中的其他目标分支)同步。 +* 当编写提交消息时,请遵循这些约定,如果您正在修复一个现有问题,请在提交消息的末尾添加 Fixes XXX(其中XXX是问题编号)。 + +##### **代码风格** +我们提供了 IntelliJ idea 的模版文件 dubbo根目录/codestyle/dubbo_codestyle_for_idea.xml,您可以将它导入到IDE。 +如果使用 Eclipse,可以通过参考该文件手动配置。 + +**代码风格检查:** + +1. 安装 checkstyle 插件(IDEA可以在插件市场搜索) +2. 插件安装好后,在IDEA的settings==>tool==>checkstyle中设置: +![checkstyle1](/imgs/dev/checkstyle1.png) +![checkstyle2](/imgs/dev/checkstyle2.png) +![checkstyle3](/imgs/dev/checkstyle3.png) +![checkstyle4](/imgs/dev/checkstyle4.png) + +**注意事项** + +使用 dubbo_codestyle_for_idea.xml 为你的 IDEA 设置代码格式是贡献代码前至关重要的一个步骤,否则你将会无法通过 CI 的代码风格校验,下面几个步骤给你演示了如何配置代码格式: +1. 进入菜单页 Editor > Code Style +2. 在 Code Style 页面的 scheme 菜单中点击 manage profiles 选项 在下拉列表中选择 Import Scheme, 接着选择 IntelliJ IDEA code style XML 导入 xml 文件 +3. 输入你的格式名称,方便在不同工程之间进行识别,最后别忘了 ⏎ 来保存更改. + 设置完成后,IDEA 会帮助你自动 reformat 代码 + +### 参与发布投票 + +参与发布投票是一种重要的贡献社区的方式,Dubbo 社区非常欢迎和鼓励任何人参与投票,每当一个版本需要正式发布的时候,会在开发者邮件列表上进行发布投票,只有当投票取得通过之后,才会正式发布,可以参考这个[检查列表](https://wiki.apache.org/incubator/IncubatorReleaseChecklist)对源码进行合规性检查。如果有任何问题,可以在开发者邮件列表上提问。 diff --git a/content/en/latest/contribution-guidelines/contributor/reporting-security-issues_dev.md b/content/en/latest/contribution-guidelines/contributor/reporting-security-issues_dev.md new file mode 100644 index 000000000000..4b0194156073 --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/reporting-security-issues_dev.md @@ -0,0 +1,26 @@ +--- +type: docs +title: "报告安全问题" +linkTitle: "反馈漏洞" +weight: 4 +--- + +Apache Software Foundation 在消除其软件项目中的安全性问题方面采取严格的立场。Apache Dubbo 对与其功能和特性有关的问题非常敏感并很快提出。 + +## 报告漏洞 + +如果您对 Dubbo 的安全性有担心,或者发现漏洞或潜在威胁,请发送电子邮件至 security@dubbo.apache.org 与 Apache Dubbo 安全团队联系。在邮件中,指定问题或潜在威胁的描述。还敦促您推荐重现和复制问题的方法。Dubbo 社区会在评估和分析调查结果之后与您联系。 + +请先注意在安全电子邮件中报告安全问题,然后再在公共领域公开该问题。 + + +## 漏洞处理 + +漏洞处理过程的概述是: + +* 报告者将漏洞秘密报告给 Apache。 +* 相应项目的安全团队与报告者私下合作来解决漏洞。 +* 制作了包含该修复程序的有关 Apache 产品的新版本。 +* 该漏洞已公开宣布。 + +有关此过程的详细说明,请参见[此处](https://www.apache.org/security/committers.html) diff --git a/content/en/latest/contribution-guidelines/contributor/software-donation-guide_dev.md b/content/en/latest/contribution-guidelines/contributor/software-donation-guide_dev.md new file mode 100644 index 000000000000..f4425f0104da --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/software-donation-guide_dev.md @@ -0,0 +1,112 @@ +--- +type: docs +title: "软件捐献向导" +linkTitle: "捐献向导" +weight: 4 +--- + +在您阅读这篇指南之前,请确保您已经向PMC确认了实际需要的 SGA。 + +如果您向 Apache Dubbo 捐赠了大量的代码或文档,则需要在合并代码或者文档之前签署[软件授权书](https://www.apache.org/licenses/#grants)。 + +### 操作步骤 + +1. 下载这篇[pdf文档](https://www.apache.org/licenses/software-grant-template.pdf) +2. 打印下载好的文档 +3. 按要求填充表格(请看下边示例) +4. 请您的领导在上边签字 +5. 扫描 +6. 将扫描好的文档以邮件的方式发送给secretary@apache.org,并抄送给private@dubbo.apache.org + +### 示例 + +下边是一个文本示例,原始文本可以在[这里](https://www.apache.org/licenses/software-grant.txt)找到 + +``` +License Agreement + + + This License Agreement is entered into as of the _12th_ day of +___April____, __2018__ by ___ABC Software Co., Ltd.____ ("Licensor"), +in favor of The Apache Software Foundation, a Delaware nonstock +membership corporation (the "Foundation"). + + WHEREAS, Licensor owns or has sufficient rights to contribute the +software source code and other related intellectual property as +itemized on Exhibit A ("Software") under the terms of this agreement +to the Foundation for use within Foundation software development +projects ("Projects"). + + NOW, THEREFORE, FOR GOOD AND VALUABLE CONSIDERATION, the receipt +and legal sufficiency of which are hereby acknowledged, the parties +hereto, intending to be legally bound, agree as follows: + +1. Subject to the terms and conditions of this License, Licensor +hereby grants to the Foundation: + + a) a non-exclusive, worldwide, royalty-free, irrevocable + copyright license to reproduce, prepare derivative works of, + publicly display, publicly perform, distribute and sublicense, + internally and externally, the Software and such derivative + works, in source code and object code form; and, + + b) a non-exclusive, worldwide, royalty-free, irrevocable + patent license under Licensed Patents to make, use, sell, + offer to sell, import and otherwise transfer the Software + in source code and object code form. "Licensed Patents" mean + patent claims owned by Licensor which are necessarily + infringed by the use or sale of the Software alone. + +2. Licensor represents that, to Licensor's knowledge, Licensor is +legally entitled to grant the above license. Licensor agrees to notify +the Foundation of any facts or circumstances of which Licensor becomes +aware and which makes or would make Licensor's representations in this +License Agreement inaccurate in any respect. + +3. This Software is provided AS-IS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. NEITHER THE LICENSOR NOR ITS +SUPPLIERS WILL BE LIABLE TO THE FOUNDATION OR ITS LICENSEES FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +THE USE OR DISTRIBUTION OF THE WORK OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +This License Agreement is the entire agreement of the parties +with respect to its subject matter, and may only be amended by a +writing signed by each party. This License Agreement may be +executed in one or more counterparts, each of which shall be +considered an original. + + IN WITNESS WHEREOF, Licensor has executed this License Agreement +as of the date first written above. + + + LICENSOR: + + + Signed By: _____________________________________ <--- Your boss's sign here + + Print Name: _____Lei Li_________________________ <--- Your boss's name here + + Title: ____Director_____________________________ <--- Your boss's title here + + Representing: ____ABC Software Co., Ltd. _______ + + ________________________________________________ + + Contact Name: ____Lei Li________________________ <--- Your boss's name here + + Contact Email: ____lilei@abc.com________________ <--- Your boss's email here + +Exhibit A + +List of software and other intellectual property covered by this agreement: + +* Github address where your code is hosted +* Pull request link +``` + diff --git a/content/en/latest/contribution-guidelines/contributor/test-coverage-guide_dev.md b/content/en/latest/contribution-guidelines/contributor/test-coverage-guide_dev.md new file mode 100644 index 000000000000..b8574daabf88 --- /dev/null +++ b/content/en/latest/contribution-guidelines/contributor/test-coverage-guide_dev.md @@ -0,0 +1,21 @@ +--- +type: docs +title: "测试覆盖率向导" +linkTitle: "测试覆盖率向导" +weight: 7 +--- + +### 写单元测试的收益 +* 单元测试能帮助每个人深入代码细节,了解代码的功能。 +* 通过测试用例我们能发现bug,并提交代码的健壮性。 +* 测试用例同时也是代码的demo用法。 +### 单元测试用例的一些设计原则 +* 应该精心设计好步骤,颗粒度和组合条件。 +* 注意边界条件。 +* 单元测试也应该好好设计,不要写无用的代码。 +* 当你发现一个`方法`很难写单元测试时,如果可以确认这个`方法`是`臭代码`,那么就和开发者一起重构它。 +* Dubbo中用的mock框架是: [mockito](http://site.mockito.org/). 下面是一些开发向导:[mockito tutorial](http://www.baeldung.com/bdd-mockito),[mockito refcard](https://dzone.com/refcardz/mockito) +* TDD(可选):当你开始写一个新的功能时,你可以试着先写测试用例。 +### 测试覆盖率设定值 +* 在现阶段,Delta更改代码的测试覆盖设定值为:>=60%,越高越好。 +* 我们可以在这个页面中看到测试报告: https://codecov.io/gh/apache/dubbo diff --git a/content/en/latest/download/_index.md b/content/en/latest/download/_index.md new file mode 100755 index 000000000000..a63be278f0fb --- /dev/null +++ b/content/en/latest/download/_index.md @@ -0,0 +1,13 @@ + +--- +type: docs +title: "Download" +linkTitle: "Download" +weight: 20 +menu: + main: + weight: 30 +--- + + + diff --git a/content/en/latest/facade-docs/_index.md b/content/en/latest/facade-docs/_index.md new file mode 100755 index 000000000000..acd2f441ce6f --- /dev/null +++ b/content/en/latest/facade-docs/_index.md @@ -0,0 +1,15 @@ + +--- +type: docs +title: "Documentation" +linkTitle: "Documentation" +weight: 20 +menu: + main: + weight: 10 + name: "Documentation" + +--- + + + diff --git a/content/en/latest/facade-docs/contact /_index.md b/content/en/latest/facade-docs/contact /_index.md new file mode 100755 index 000000000000..2fededdd0854 --- /dev/null +++ b/content/en/latest/facade-docs/contact /_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "How to Contact the Dubbo Community?" +linkTitle: "How to Contact the Dubbo Community?" +description: "" +weight: 4 +--- + + + diff --git a/content/en/latest/facade-docs/how/_index.md b/content/en/latest/facade-docs/how/_index.md new file mode 100755 index 000000000000..b46d4e3fa982 --- /dev/null +++ b/content/en/latest/facade-docs/how/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "How to use Dubbo?" +linkTitle: "How to use Dubbo?" +weight: 3 +--- + diff --git a/content/en/latest/facade-docs/how/quick-start.md b/content/en/latest/facade-docs/how/quick-start.md new file mode 100644 index 000000000000..ea2dfbf4a959 --- /dev/null +++ b/content/en/latest/facade-docs/how/quick-start.md @@ -0,0 +1,38 @@ +--- +type: docs +title: "Quick Start" +linkTitle: "Quick Start" +weight: 3 +--- + +{{< blocks/section color="white" height="auto">}} +
+
+
+
+
+

+ Quick start +

+

Dubbo Java SDK

+
+
+
+
+
+
+

+ Quick start +

+

Dubbo Golang SDK

+
+
+
+ +
+
+
+ +{{< /blocks/section >}} + diff --git a/content/en/latest/facade-docs/what/_index.md b/content/en/latest/facade-docs/what/_index.md new file mode 100644 index 000000000000..dca523204b5d --- /dev/null +++ b/content/en/latest/facade-docs/what/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "What is Dubbo?" +linkTitle: "What is Dubbo?" +weight: 1 +description: "" +--- \ No newline at end of file diff --git a/content/en/latest/facade-docs/what/core-concepts.md b/content/en/latest/facade-docs/what/core-concepts.md new file mode 100644 index 000000000000..df31c8282e37 --- /dev/null +++ b/content/en/latest/facade-docs/what/core-concepts.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Core Concepts" +linkTitle: "Core Concepts" +weight: 2 +description: "" +--- \ No newline at end of file diff --git a/content/en/latest/facade-docs/what/ecosystem.md b/content/en/latest/facade-docs/what/ecosystem.md new file mode 100644 index 000000000000..a42cd4b905d5 --- /dev/null +++ b/content/en/latest/facade-docs/what/ecosystem.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Dubbo Ecosystem" +linkTitle: "Dubbo Ecosystem" +weight: 3 +description: "" +--- \ No newline at end of file diff --git a/content/en/latest/facade-docs/what/overview.md b/content/en/latest/facade-docs/what/overview.md new file mode 100644 index 000000000000..7cafaa0f09f7 --- /dev/null +++ b/content/en/latest/facade-docs/what/overview.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Introduction" +linkTitle: "Introduction" +weight: 1 +description: "" +--- \ No newline at end of file diff --git a/content/en/latest/facade-docs/why/_index.md b/content/en/latest/facade-docs/why/_index.md new file mode 100755 index 000000000000..9e4836a117b7 --- /dev/null +++ b/content/en/latest/facade-docs/why/_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "Why use Dubbo?" +linkTitle: "Why use Dubbo?" +description: "" +weight: 2 +--- + + + diff --git a/content/en/latest/facade-docs/why/advantage.md b/content/en/latest/facade-docs/why/advantage.md new file mode 100755 index 000000000000..0a0740bbd1bc --- /dev/null +++ b/content/en/latest/facade-docs/why/advantage.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "Advantage" +linkTitle: "Advantage" +description: "" +weight: 1 +--- + + + diff --git a/content/en/latest/golang-sdk/_index.md b/content/en/latest/golang-sdk/_index.md new file mode 100755 index 000000000000..1b3de410d1b1 --- /dev/null +++ b/content/en/latest/golang-sdk/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "golang" +linkTitle: "golang" +weight: 3 +--- + diff --git a/content/en/latest/java-sdk/_index.md b/content/en/latest/java-sdk/_index.md new file mode 100755 index 000000000000..9d5fefd9e445 --- /dev/null +++ b/content/en/latest/java-sdk/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Dubbo Java SDK" +linkTitle: "Dubbo Java SDK" +weight: 3 +--- + diff --git a/content/en/latest/java-sdk/v2.x/_index.md b/content/en/latest/java-sdk/v2.x/_index.md new file mode 100755 index 000000000000..1935db7ac148 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "v2.x" +linkTitle: "v2.x" +weight: 1 +--- + diff --git a/content/en/latest/java-sdk/v2.x/advanced-features-and-usage/_index.md b/content/en/latest/java-sdk/v2.x/advanced-features-and-usage/_index.md new file mode 100755 index 000000000000..e384131bc8a5 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/advanced-features-and-usage/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Advanced Features & Usage" +linkTitle: "Advanced Features & Usage" +weight: 4 +--- + diff --git a/content/en/latest/java-sdk/v2.x/concepts-and-architecture/_index.md b/content/en/latest/java-sdk/v2.x/concepts-and-architecture/_index.md new file mode 100755 index 000000000000..f911f7417735 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/concepts-and-architecture/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Concepts & Architecture" +linkTitle: "Concepts & Architecture" +weight: 3 +--- + diff --git a/content/en/latest/java-sdk/v2.x/concepts-and-architecture/users.md b/content/en/latest/java-sdk/v2.x/concepts-and-architecture/users.md new file mode 100644 index 000000000000..c9af84c04d7b --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/concepts-and-architecture/users.md @@ -0,0 +1,6 @@ +--- +type: advanced-docs +title: "用户列表" +linkTitle: "用户列表" +weight: 3 +--- diff --git a/content/en/latest/java-sdk/v2.x/contributing/_index.md b/content/en/latest/java-sdk/v2.x/contributing/_index.md new file mode 100755 index 000000000000..bdef0ad150c2 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/contributing/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Contributing" +linkTitle: "Contributing" +weight: 7 +--- + diff --git a/content/en/latest/java-sdk/v2.x/introduction/_index.md b/content/en/latest/java-sdk/v2.x/introduction/_index.md new file mode 100755 index 000000000000..85c937b2965b --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/introduction/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Introduction" +linkTitle: "Introduction" +weight: 1 +--- + diff --git a/content/en/latest/java-sdk/v2.x/introduction/users.md b/content/en/latest/java-sdk/v2.x/introduction/users.md new file mode 100644 index 000000000000..2e10662e47cc --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/introduction/users.md @@ -0,0 +1,6 @@ +--- +type: advanced-docs +title: "Users" +linkTitle: "Users" +weight: 3 +--- diff --git a/content/en/latest/java-sdk/v2.x/quick-start/_index.md b/content/en/latest/java-sdk/v2.x/quick-start/_index.md new file mode 100755 index 000000000000..762f84891d60 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/quick-start/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Quick start" +linkTitle: "Quick start" +weight: 2 +--- + diff --git a/content/en/latest/java-sdk/v2.x/quick-start/quick-start.md b/content/en/latest/java-sdk/v2.x/quick-start/quick-start.md new file mode 100644 index 000000000000..05d66531bf11 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/quick-start/quick-start.md @@ -0,0 +1,8 @@ +--- +type: advanced-docs +title: "快速开始" +linkTitle: "快速开始" +weight: 3 +--- + +链接到各个生态的quick-start \ No newline at end of file diff --git a/content/en/latest/java-sdk/v2.x/reference-manual/_index.md b/content/en/latest/java-sdk/v2.x/reference-manual/_index.md new file mode 100755 index 000000000000..8b31e2586018 --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/reference-manual/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Reference Manual" +linkTitle: "Reference Manual" +weight: 5 +--- + diff --git a/content/en/latest/java-sdk/v2.x/upgrades-and-compatibility/_index.md b/content/en/latest/java-sdk/v2.x/upgrades-and-compatibility/_index.md new file mode 100755 index 000000000000..9160ff74b96c --- /dev/null +++ b/content/en/latest/java-sdk/v2.x/upgrades-and-compatibility/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Upgrades & Compatibility" +linkTitle: "Upgrades & Compatibility" +weight: 6 +--- + diff --git a/content/en/latest/java-sdk/v3.x/_index.md b/content/en/latest/java-sdk/v3.x/_index.md new file mode 100755 index 000000000000..fcb85345b107 --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "v3.x" +linkTitle: "v3.x" +weight: 2 +--- + diff --git a/content/en/latest/java-sdk/v3.x/advanced-features-and-usage/_index.md b/content/en/latest/java-sdk/v3.x/advanced-features-and-usage/_index.md new file mode 100755 index 000000000000..e384131bc8a5 --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/advanced-features-and-usage/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Advanced Features & Usage" +linkTitle: "Advanced Features & Usage" +weight: 4 +--- + diff --git a/content/en/latest/java-sdk/v3.x/concepts-and-architecture/_index.md b/content/en/latest/java-sdk/v3.x/concepts-and-architecture/_index.md new file mode 100755 index 000000000000..f911f7417735 --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/concepts-and-architecture/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Concepts & Architecture" +linkTitle: "Concepts & Architecture" +weight: 3 +--- + diff --git a/content/en/latest/java-sdk/v3.x/concepts-and-architecture/users.md b/content/en/latest/java-sdk/v3.x/concepts-and-architecture/users.md new file mode 100644 index 000000000000..c9af84c04d7b --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/concepts-and-architecture/users.md @@ -0,0 +1,6 @@ +--- +type: advanced-docs +title: "用户列表" +linkTitle: "用户列表" +weight: 3 +--- diff --git a/content/en/latest/java-sdk/v3.x/contributing/_index.md b/content/en/latest/java-sdk/v3.x/contributing/_index.md new file mode 100755 index 000000000000..bdef0ad150c2 --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/contributing/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Contributing" +linkTitle: "Contributing" +weight: 7 +--- + diff --git a/content/en/latest/java-sdk/v3.x/introduction/_index.md b/content/en/latest/java-sdk/v3.x/introduction/_index.md new file mode 100755 index 000000000000..85c937b2965b --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/introduction/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Introduction" +linkTitle: "Introduction" +weight: 1 +--- + diff --git a/content/en/latest/java-sdk/v3.x/introduction/users.md b/content/en/latest/java-sdk/v3.x/introduction/users.md new file mode 100644 index 000000000000..2e10662e47cc --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/introduction/users.md @@ -0,0 +1,6 @@ +--- +type: advanced-docs +title: "Users" +linkTitle: "Users" +weight: 3 +--- diff --git a/content/en/latest/java-sdk/v3.x/quick-start/_index.md b/content/en/latest/java-sdk/v3.x/quick-start/_index.md new file mode 100755 index 000000000000..762f84891d60 --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/quick-start/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Quick start" +linkTitle: "Quick start" +weight: 2 +--- + diff --git a/content/en/latest/java-sdk/v3.x/reference-manual/_index.md b/content/en/latest/java-sdk/v3.x/reference-manual/_index.md new file mode 100755 index 000000000000..8b31e2586018 --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/reference-manual/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Reference Manual" +linkTitle: "Reference Manual" +weight: 5 +--- + diff --git a/content/en/latest/java-sdk/v3.x/upgrades-and-compatibility/_index.md b/content/en/latest/java-sdk/v3.x/upgrades-and-compatibility/_index.md new file mode 100755 index 000000000000..9160ff74b96c --- /dev/null +++ b/content/en/latest/java-sdk/v3.x/upgrades-and-compatibility/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "Upgrades & Compatibility" +linkTitle: "Upgrades & Compatibility" +weight: 6 +--- + diff --git a/content/en/latest/notices/_index.md b/content/en/latest/notices/_index.md new file mode 100755 index 000000000000..e3f4a4846bda --- /dev/null +++ b/content/en/latest/notices/_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "Announcement" +linkTitle: "Announcement" +description: "Dubbo Announcement" +menu: + main: + weight: 60 +--- + diff --git a/content/en/latest/notices/security.md b/content/en/latest/notices/security.md new file mode 100755 index 000000000000..e665d769dfa7 --- /dev/null +++ b/content/en/latest/notices/security.md @@ -0,0 +1,73 @@ + +--- +type: docs +title: "安全漏洞" +linkTitle: "安全漏洞" +description: "安全漏洞说明" +weight: 90 +--- + +## 1. Log4j CVE-2021-44228 漏洞 + +最近,主流日志组件 [log4j2](https://logging.apache.org/log4j/2.x/) 爆出[安全漏洞 CVE-2021-44228](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228)。 + +以下是漏洞 CVE-2021-44228 对 Apache Dubbo 框架的影响总结及用户应对指南。 + +## Dubbo 影响范围 +**该漏洞对 Dubbo 框架使用安全并无影响。** + +Dubbo 本身不强依赖 log4j2 框架,也不会通过依赖传递将 log4j2 带到业务工程中去,因此,正在使用 Dubbo 2.7.x、3.0.x 等版本的用户均无需强制升级 Dubbo 版本。 + +以下是 Dubbo 各组件对 log4j2 的依赖分析,涉及 `dubbo-common`、`dubbo-spring-boot-starter`、`dubbo-spring-boot-actuator`: + +* dubbo-common 包含对 `log4j-core` 的可选依赖,请检查项目自身是否启用了 log4j 依赖,如启用则对应升级即可。 +```xml +[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ dubbo-common --- +[INFO] org.apache.dubbo:dubbo-common:jar:2.7.14-SNAPSHOT +[INFO] +- org.apache.logging.log4j:log4j-api:jar:2.11.1:provided +[INFO] \- org.apache.logging.log4j:log4j-core:jar:2.11.1:provided + +``` + +* dubbo-spring-boot-starter 通过 spring-boot 组件传递了 log4j-api 依赖,log4j-api 本身并无安全问题,升级 log4j-core 组件时注意与 log4j-api 的兼容性 + +```xml +[INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ dubbo-spring-boot-starter --- +[INFO] org.apache.dubbo:dubbo-spring-boot-starter:jar:2.7.14-SNAPSHOT +[INFO] \- org.springframework.boot:spring-boot-starter:jar:2.3.1.RELEASE:compile (optional) +[INFO] \- org.springframework.boot:spring-boot-starter-logging:jar:2.3.1.RELEASE:compile (optional) +[INFO] \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile (optional) +[INFO] \- org.apache.logging.log4j:log4j-api:jar:2.13.3:compile (optional) + +``` + +* dubbo-spring-boot-actuator 通过 spring-boot 组件传递了 log4j-api 依赖,log4j-api 本身并无安全问题,升级 log4j-core 组件时应注意与 log4j-api 的兼容性 + +```xml +[INFO] org.apache.dubbo:dubbo-spring-boot-actuator:jar:2.7.14-SNAPSHOT +[INFO] \- org.springframework.boot:spring-boot-starter-web:jar:2.3.1.RELEASE:compile (optional) +[INFO] \- org.springframework.boot:spring-boot-starter:jar:2.3.1.RELEASE:compile +[INFO] \- org.springframework.boot:spring-boot-starter-logging:jar:2.3.1.RELEASE:compile +[INFO] \- org.apache.logging.log4j:log4j-to-slf4j:jar:2.13.3:compile +[INFO] \- org.apache.logging.log4j:log4j-api:jar:2.13.3:compile +``` + + +## 2. 序列化 +Dubbo 支持序列化协议的扩展,理论上用户可以基于该扩展机制启用任意的序列化协议,这带来了极大的灵活的,但同时也要意识到其中潜藏的安全性风险。 +数据反序列化是最容易被被攻击者利用的一个环节,攻击者利用它执行 RCE 攻击等窃取或破坏服务端数据,用户在切换序列化协议或实现前, +应充分调研目标序列化协议及其框架实现的安全性保障,并提前设置相应的安全措施(如设置黑/白名单)。Dubbo 框架自身并不能保证目标序列化机制的安全性。 + +Dubbo 2.7 官方版本提供的序列化协议有如下几种: +* Hessian2 +* Fastjson +* Kryo +* FST +* JDK +* Protostuff/Protobuf +* Avro +* Gson + +针对以上序列化扩展,在发现或收到相关的漏洞报告之后,Dubbo 官方会跟进并升级依赖到最新的安全版本,但最终的漏洞修复方案取决于序列化的框架实现。 + +> 针对使用 [dubbo hessian2](https://github.com/apache/dubbo-hessian-lite/releases) 版本的用户,Dubbo 官方会保证hessian2序列化机制的安全性并尽可能的修复上报的安全漏洞 diff --git a/content/en/latest/pixiu/_index.md b/content/en/latest/pixiu/_index.md new file mode 100755 index 000000000000..3e7201da0198 --- /dev/null +++ b/content/en/latest/pixiu/_index.md @@ -0,0 +1,8 @@ + +--- +type: advanced-docs +title: "pixiu" +linkTitle: "pixiu" +weight: 3 +--- + diff --git a/content/zh/_index.html b/content/zh/_index.html index edf2df656eaf..387ad5dc3141 100644 --- a/content/zh/_index.html +++ b/content/zh/_index.html @@ -4,53 +4,53 @@ +++ -{{< blocks/cover title="Apache Dubbo" image_anchor="top" height="auto" color="primary" >}} +{{< blocks/cover title="Apache Dubbo" image_anchor="top" height="full" color="secondary" >}}
- }}"> - 快速开始 + + 快速开始 - - DOWNLOAD + + Dubbo3 速览 - - GITHUB - -

Apache Dubbo 是一款高性能、轻量级的开源服务框架

- {{< blocks/link-down color="info" >}} +

Apache Dubbo 是一款微服务框架,为大规模微服务实践提供高性能 RPC 通信、流量治理、可观测性等解决方案,
+ 涵盖 Java、Golang 等多种语言 SDK 实现。

+
+ {{< blocks/link-down color="white" >}} +
{{< /blocks/cover >}} -{{% blocks/lead color="primary" %}} -Apache Dubbo |ˈdʌbəʊ| 提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。 -{{% /blocks/lead %}} - -{{< blocks/section color="dark" >}} -{{% blocks/feature icon="fas fa-exchange-alt" title="面向接口代理的高性能RPC调用" %}} -提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。 -{{% /blocks/feature %}} - -{{% blocks/feature icon="fas fa-random" title="智能负载均衡" %}} -内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。 -{{% /blocks/feature %}} - -{{% blocks/feature icon="fa fa-share-alt" title="服务自动注册与发现" %}} -支持多种注册中心服务,服务实例上下线实时感知。 -{{% /blocks/feature %}} +{{% blocks/lead color="white" %}} +

下一代云原生微服务框架 - Dubbo3

+
+

+ 3.0 版本的正式发布,标志着 Apache Dubbo 正式进入云原生时代。3.0 在通信协议、服务发现、部署架构、服务治理上都对云原生基础设施进行了全面适配, + 提供了Triple、应用级服务发现、Dubbo Mesh等核心特性。 +

+

+ Dubbo3 已被阿里巴巴、饿了么、钉钉、工商银行、小米等在生产环境广泛采用,学习更多用户案例 + {{% /blocks/lead %}} +

-{{% blocks/feature icon="fad fa-expand" title="高度可扩展能力" %}} -遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。 +{{< blocks/section color="100" >}} +{{% blocks/feature icon="fas fa-exchange-alt" title="下一代RPC协议 - Triple" url="./docs/concepts/rpc-protocol/" %}} +Triple 协议是基于 HTTP/2 之上定义的下一代 RPC 通信协议,相比于上一代 Dubbo2 协议,它具有更好的穿透性、通用性、以及面向网关代理场景的高性能表现, +提供了 Reactive Stream 数据交换模型。Triple 实现了对 gRPC 的完全兼容。 {{% /blocks/feature %}} -{{% blocks/feature icon="fas fa-traffic-light" title="运行期流量调度" %}} -内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。 +{{% blocks/feature icon="fas fa-share-alt" title="应用级服务发现" url="./docs/concepts/service-discovery/" %}} +从服务/接口粒度到应用粒度的升级,使得 Dubbo 在集群可伸缩性、连接异构微服务体系上更具优势。应用粒度能以更低的资源消耗支持超百万实例规模集群程; +实现与 Spring Cloud、Kubernetes Service 等异构微服务体系的互联互通。 {{% /blocks/feature %}} -{{% blocks/feature icon="fas fa-sliders-h" title="可视化的服务治理与运维" %}} -提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。 +{{% blocks/feature icon="fa fa-share-alt" title="Mesh 解决方案" %}} +依赖 Dubbo Mesh 将 Dubbo 纳入 Istio 等开源 Mesh 治理体系, +3.0 同时支持 ThinSDK + Sidecar 与 FatSDK(Proxyless)的部署模式,为 Mesh 架构提供了更多选择,尤其对于面临迁移或混合部署的场景, +Dubbo 提供了统一控制面的能力。 {{% /blocks/feature %}} {{< /blocks/section >}} -{{< blocks/section color="white" >}} +{{< blocks/section color="primary" >}} {{% blocks/feature icon="far fa-envelope" title="订阅邮件组" %}} dev@dubbo.apache.org 参与社区的讨论 {{% /blocks/feature %}} diff --git a/content/zh/docs3-building/docs/_index.md b/content/zh/docs3-building/docs/_index.md new file mode 100755 index 000000000000..d029bfea07e2 --- /dev/null +++ b/content/zh/docs3-building/docs/_index.md @@ -0,0 +1,38 @@ + +--- +type: docs +title: "文档" +linkTitle: "文档" +no_list: true +hide_summary: true +menu: + main: + weight: 10 +--- +> 本文档最新版本基于 Dubbo3 编写,由于 Dubbo3 完全兼容 2.7 版本用法,因此本文档中的通用功能(指除 3.x 版本特有功能外)同样适用于 2.7 版本用户。 +> +> 如有需要,请点此查看[老版本文档]() +> + + +Dubbo 是一款包含多种语言实现(Java、Golang等)的 RPC 服务框架,在这里可以查看 Dubbo 核心概念以及每种语言实现的 Demo、用户手册等。 + +* **如果你是第一次接触 Dubbo**,可以通过以下链接快速了解 Dubbo: + * [什么是 Dubbo](what/overview.md) + * [用户案例与生态](what/usecases.md) + * 通过 [**快速开始**](quickstart/) 体验 Dubbo +* 想了解更多 Dubbo 特性与使用细节? + * 选择相应 [**多语言 SDK 实现**](mannual/),并参考**高级特性**或**参考手册** + * [**FAQ**](faq) +* 老用户,想快速了解 **Dubbo 3.0** 相关知识 + * [3.0 特性一览](whatsnew/) + * 查看相应[多语言 SDK 实现](mannual/),了解升级与兼容性 + +#### 相关链接 +Dubbo 的多语言实现及文档: + +Language | OS | Compilers/SDK +-- | -- | -- +Go|Windows, Linux, Mac|Go 1.13+ +Java|Windows, Linux, Mac|Java 8+ + diff --git a/content/zh/docs3-building/docs/contact /_index.md b/content/zh/docs3-building/docs/contact /_index.md new file mode 100755 index 000000000000..5a921e1334ea --- /dev/null +++ b/content/zh/docs3-building/docs/contact /_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "联系社区" +linkTitle: "联系社区" +description: "" +weight: 6 +--- + + + diff --git a/content/zh/docs3-building/docs/mannual/Golang.md b/content/zh/docs3-building/docs/mannual/Golang.md new file mode 100755 index 000000000000..0808bdd0b507 --- /dev/null +++ b/content/zh/docs3-building/docs/mannual/Golang.md @@ -0,0 +1,13 @@ + +--- +type: docs +title: "Golang SDK" +linkTitle: "Golang" +description: "" +weight: 2 +manualLinkTarget: _blank +_build: { render: link } +--- + + + diff --git a/content/zh/docs3-building/docs/mannual/Java.md b/content/zh/docs3-building/docs/mannual/Java.md new file mode 100644 index 000000000000..5302ce31516a --- /dev/null +++ b/content/zh/docs3-building/docs/mannual/Java.md @@ -0,0 +1,13 @@ +--- +type: docs +title: "Java SDK" +linkTitle: "Java" +description: "" +weight: 1 +manualLinkRelref: ../../java-sdk/ +manualLinkTarget: _blank +_build: { render: link } +--- + + + diff --git a/content/zh/docs3-building/docs/mannual/_index.md b/content/zh/docs3-building/docs/mannual/_index.md new file mode 100755 index 000000000000..8197f914cc11 --- /dev/null +++ b/content/zh/docs3-building/docs/mannual/_index.md @@ -0,0 +1,40 @@ + +--- +type: docs +title: "用户手册" +linkTitle: "用户手册" +weight: 5 +no_list: true +--- + + +{{< blocks/section color="white" height="auto" >}} +
+
+
+
+
+

+ Java SDK +

+

Java SDK

+
+
+
+
+
+
+

+ Golang SDK +

+

Golang SDK

+
+
+
+ +
+
+
+ +{{< /blocks/section >}} diff --git a/content/zh/docs3-building/docs/quickstart/_index.md b/content/zh/docs3-building/docs/quickstart/_index.md new file mode 100755 index 000000000000..f152a6e73774 --- /dev/null +++ b/content/zh/docs3-building/docs/quickstart/_index.md @@ -0,0 +1,38 @@ + +--- +type: docs +title: "快速开始" +linkTitle: "快速开始" +weight: 2 +--- + +{{< blocks/section color="white" height="auto">}} +
+
+
+
+
+

+ Quick start +

+

Dubbo Java SDK

+
+
+
+
+
+
+

+ Quick start +

+

Dubbo Golang SDK

+
+
+
+ +
+
+
+ +{{< /blocks/section >}} \ No newline at end of file diff --git a/content/zh/docs3-building/docs/tasks/_index.md b/content/zh/docs3-building/docs/tasks/_index.md new file mode 100755 index 000000000000..302f98ae523d --- /dev/null +++ b/content/zh/docs3-building/docs/tasks/_index.md @@ -0,0 +1,11 @@ + +--- +type: docs +title: "示例实践" +linkTitle: "示例实践" +description: "" +weight: 4 +--- + + + diff --git a/content/zh/docs3-building/docs/tasks/deploy-on-k8s.md b/content/zh/docs3-building/docs/tasks/deploy-on-k8s.md new file mode 100644 index 000000000000..7d35b9c80a12 --- /dev/null +++ b/content/zh/docs3-building/docs/tasks/deploy-on-k8s.md @@ -0,0 +1,11 @@ +--- +type: docs +title: "部署到 Kubernetes" +linkTitle: "如何部署 Dubbo 到 Kubernetes" +weight: 3 +description: "" +--- + + + + diff --git a/content/zh/docs3-building/docs/tasks/dubbo-mesh-proxyless.md b/content/zh/docs3-building/docs/tasks/dubbo-mesh-proxyless.md new file mode 100644 index 000000000000..66e55b8029ad --- /dev/null +++ b/content/zh/docs3-building/docs/tasks/dubbo-mesh-proxyless.md @@ -0,0 +1,9 @@ +--- +type: docs +title: "Dubbo Mesh(Proxyless)部署" +linkTitle: "基于 Proxyless 的 Dubbo Mesh 部署方案" +weight: 3 +description: "" +--- + + diff --git a/content/zh/docs3-building/docs/tasks/dubbo-mesh-sidecar.md b/content/zh/docs3-building/docs/tasks/dubbo-mesh-sidecar.md new file mode 100644 index 000000000000..d334d44f317e --- /dev/null +++ b/content/zh/docs3-building/docs/tasks/dubbo-mesh-sidecar.md @@ -0,0 +1,9 @@ +--- +type: docs +title: "Dubbo Mesh(Sidecar)部署" +linkTitle: "基于 Sidecar 的 Dubbo Mesh 部署方案" +weight: 3 +description: "" +--- + + diff --git a/content/zh/docs3-building/docs/tasks/idl.md b/content/zh/docs3-building/docs/tasks/idl.md new file mode 100644 index 000000000000..c38d0ae19526 --- /dev/null +++ b/content/zh/docs3-building/docs/tasks/idl.md @@ -0,0 +1,204 @@ +--- +type: docs +title: "使用 IDL + Protobuf 跨语言定义服务" +linkTitle: "使用 IDL + Protobuf 跨语言定义服务" +weight: 3 +description: "" +--- + +服务是 Dubbo 中的核心概念,一个服务代表一组 RPC 方法的集合,服务是面向用户编程、服务发现机制等的基本单位。Dubbo 开发的基本流程是:用户定义 RPC 服务,通过约定的配置 +方式将 RPC 声明为 Dubbo 服务,然后就可以基于服务 API 进行编程了。对服务提供者来说是提供 RPC 服务的具体实现,而对服务消费者来说则是使用特定数据发起服务调用。 + +下面从定义服务、编译服务、配置并加载服务三个方面说明如何快速的开发 Dubbo 服务。 + +## 定义服务 +Dubbo3 推荐使用 IDL 定义跨语言服务,如您更习惯使用特定语言的服务定义方式,请移步[多语言 SDK](../mannual)查看。 + +```text +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.apache.dubbo.demo"; +option java_outer_classname = "DemoServiceProto"; +option objc_class_prefix = "DEMOSRV"; + +package demoservice; + +// The demo service definition. +service DemoService { + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} + +``` + +以上是使用 IDL 定义服务的一个简单示例,我们可以把它命名为 `DemoService.proto`,proto 文件中定义了 RPC 服务名称 `DemoService` 与方法签名 +`SayHello (HelloRequest) returns (HelloReply) {}`,同时还定义了方法的入参结构体、出参结构体 `HelloRequest` 与 `HelloReply`。 +IDL 格式的服务依赖 Protobuf 编译器,用来生成可以被用户调用的客户端与服务端编程 API,Dubbo 在原生 Protobuf Compiler 的基础上提供了适配多种语言的特有插件,用于适配 Dubbo 框架特有的 API 与编程模型。 + +> 使用 Dubbo3 IDL 定义的服务只允许一个入参与出参,这种形式的服务签名有两个优势,一是对多语言实现更友好,二是可以保证服务的向后兼容性,依赖于 Protobuf 序列化的兼容性,我们可以很容易的调整传输的数据结构如增、删字段等,完全不用担心接口的兼容性。 + +## 编译服务 +根据当前采用的语言,配置相应的 Protobuf 插件,编译后将生产语言相关的服务定义 stub。 + +### Java +Java 语言生成的 stub 如下,核心是一个接口定义 +```java +@javax.annotation.Generated( +value = "by Dubbo generator", +comments = "Source: DemoService.proto") +public interface DemoService { + static final String JAVA_SERVICE_NAME = "org.apache.dubbo.demo.DemoService"; + static final String SERVICE_NAME = "demoservice.DemoService"; + + org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request); + + CompletableFuture sayHelloAsync(org.apache.dubbo.demo.HelloRequest request); +} +``` + +### Golang + +Go 语言生成的 stub 如下,这个 stub 里存了用户定义的接口和数据的类型。 + +```go +func _DUBBO_Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + base := srv.(dgrpc.Dubbo3GrpcService) + args := []interface{}{} + args = append(args, in) + invo := invocation.NewRPCInvocation("SayHello", args, nil) + if interceptor == nil { + result := base.GetProxyImpl().Invoke(ctx, invo) + return result.Result(), result.Error() + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/main.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + result := base.GetProxyImpl().Invoke(context.Background(), invo) + return result.Result(), result.Error() + } + return interceptor(ctx, in, info, handler) +} +``` + + +## 配置并加载服务 +提供端负责提供具体的 Dubbo 服务实现,也就是遵循 RPC 签名所约束的格式,去实现具体的业务逻辑代码。在实现服务之后,要将服务实现注册为标准的 Dubbo 服务, +之后 Dubbo 框架就能根据接收到的请求转发给服务实现,执行方法,并将结果返回。 + +消费端的配置会更简单一些,只需要声明 IDL 定义的服务为标准的 Dubbo 服务,框架就可以帮助开发者生成相应的 proxy,开发者将完全面向 proxy 编程, +基本上 Dubbo 所有语言的实现都保证了 proxy 依据 IDL 服务定义暴露标准化的接口。 + +### Java +提供端,实现服务 +```java +public class DemoServiceImpl implements DemoService { + private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class); + + @Override + public HelloReply sayHello(HelloRequest request) { + logger.info("Hello " + request.getName() + ", request from consumer: " + RpcContext.getContext().getRemoteAddress()); + return HelloReply.newBuilder() + .setMessage("Hello " + request.getName() + ", response from provider: " + + RpcContext.getContext().getLocalAddress()) + .build(); + } + + @Override + public CompletableFuture sayHelloAsync(HelloRequest request) { + return CompletableFuture.completedFuture(sayHello(request)); + } +} +``` + +提供端,注册服务(以 Spring XML 为例) +```xml + + +``` + +消费端,引用服务 +```xml + +``` + +消费端,使用服务 proxy +```java +public void callService() throws Exception { + ... + DemoService demoService = context.getBean("demoService", DemoService.class); + HelloRequest request = HelloRequest.newBuilder().setName("Hello").build(); + HelloReply reply = demoService.sayHello(request); + System.out.println("result: " + reply.getMessage()); +} +``` + +### Golang + +提供端,实现服务 + +```go +type User struct { + ID string + Name string + Age int32 + Time time.Time +} + +type UserProvider struct { +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) { + gxlog.CInfo("req:%#v", req) + rsp := User{"A001", "Alex Stocks", 18, time.Now()} + gxlog.CInfo("rsp:%#v", rsp) + return &rsp, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "org.apache.dubbo.User" +} + +func main() { + hessian.RegisterPOJO(&User{}) + config.SetProviderService(new(UserProvider)) +} +``` + +消费端,使用服务 proxy + +```go +func main() { + config.Load() + user := &pkg.User{} + err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user) + if err != nil { + os.Exit(1) + return + } + gxlog.CInfo("response result: %v\n", user) +} +``` + +## 查看完整示例 +* [Java IDL Quick Start]() +* [Golang IDL Quick Start]() diff --git a/content/zh/docs3-building/docs/tasks/traffic-management.md b/content/zh/docs3-building/docs/tasks/traffic-management.md new file mode 100644 index 000000000000..d4beaf1d966e --- /dev/null +++ b/content/zh/docs3-building/docs/tasks/traffic-management.md @@ -0,0 +1,15 @@ +--- +type: docs +title: "流量管理" +linkTitle: "如何使用 Dubbo 的流量治理能力" +weight: 3 +description: "" +--- + +* 超时 +* 路由规则 +* 动态配置 +* 多注册中心 + + + diff --git a/content/zh/docs3-building/docs/what/_index.md b/content/zh/docs3-building/docs/what/_index.md new file mode 100644 index 000000000000..0b96c4f68266 --- /dev/null +++ b/content/zh/docs3-building/docs/what/_index.md @@ -0,0 +1,17 @@ +--- +type: docs +title: "什么是 Dubbo" +linkTitle: "什么是 Dubbo" +weight: 1 +description: "" +--- + +Apache Dubbo 是一款 RPC 微服务框架,提供了包括 Java、Golang 等在内的多种语言 SDK 实现,如果你是, +* 第一次接触 Dubbo,请继续阅读本章了解 Dubbo 提供的丰富功能及核心概念; +* 想快速体验 Dubbo,请参考 [快速开始](../quickstart) +* 高级用法请参考 [多语言 SDK 文档](../mannual) +* 关注 Dubbo3 + * [3.0 设计理念与核心功能](../whatsnew) + * [如何迁移到 Dubbo3](../../java-sdk/upgrades-and-compatibility) + + diff --git a/content/zh/docs3-building/docs/what/architecture.md b/content/zh/docs3-building/docs/what/architecture.md new file mode 100644 index 000000000000..a1cbf95880de --- /dev/null +++ b/content/zh/docs3-building/docs/what/architecture.md @@ -0,0 +1,77 @@ +--- +type: docs +title: "总体架构" +linkTitle: "总体架构" +weight: 2 +--- + +作为一个微服务框架,Dubbo sdk 跟随着微服务组件被部署在分布式集群各个位置,为了在分布式环境下实现各个微服务组件间的协作, +Dubbo 定义了一些中心化组件,这包括: +* 注册中心。协调 Consumer 与 Provider 之间的地址注册与发现 +* 配置中心。 + * 存储 Dubbo 启动阶段的全局配置,保证配置的跨环境共享与全局一致性 + * 负责服务治理规则(路由规则、动态配置等)的存储与推送。 +* 元数据中心。 + * 接收 Provider 上报的服务接口元数据,为 Admin 等控制台提供运维能力(如服务测试、接口文档等) + * 作为服务发现机制的补充,提供额外的接口/方法级别配置信息的同步能力,相当于注册中心的额外扩展 + +![threecenters](/imgs/v3/concepts/threecenters.png) + +上图完整的描述了 Dubbo 微服务组件与各个中心的交互过程。 + +以上三个中心并不是运行 Dubbo 的必要条件,用户完全可以根据自身业务情况决定只启用其中一个或多个,以达到简化部署的目的。通常情况下,所有用户都会以独立的注册中心 +以开始 Dubbo 服务开发,而配置中心、元数据中心则会在微服务演进的过程中逐步的按需被引入进来。 + +## 注册中心 + +注册中心扮演着非常重要的角色,它承载着服务注册和服务发现的职责。目前Dubbo支持以下两种粒度的服务发现和服务注册,分别是接口级别和应用级别,注册中心可以按需进行部署: + +- 在传统的Dubbo SDK使用姿势中,如果仅仅提供直连模式的RPC服务,不需要部署注册中心。 +- 无论是接口级别还是应用级别,如果需要Dubbo SDK自身来做服务注册和服务发现,则可以选择部署注册中心,在Dubbo中集成对应的注册中心。 + +- 在Dubbo + Mesh 的场景下,随着 Dubbo 服务注册能力的弱化,Dubbo内的注册中心也不再是必选项,其职责开始被控制面取代,如果采用了Dubbo + Mesh的部署方式,无论是ThinSDK的mesh方式还是Proxyless的mesh方式,都不再需要独立部署注册中心。 + +而注册中心并不依赖于配置中心和元数据中心,如下图所示: + +![centers-registry](/imgs/v3/concepts/centers-registry.png) + +该图中没有部署配置中心和元数据中心,在Dubbo中会默认将注册中心的实例同时作为配置中心和元数据中心,这是Dubbo的默认行为,如果确实不需要配置中心或者元数据中心的能力,可在配置中关闭,在注册中心的配置中有两个配置分别为use-as-config-center和use-as-metadata-center,将配置置为false即可。 + +## 元数据中心 + +元数据中心在2.7.x版本开始支持,随着应用级别的服务注册和服务发现在Dubbo中落地,元数据中心也变的越来越重要。在以下几种情况下会需要部署元数据中心: + +1. 对于一个原先采用老版本Dubbo搭建的应用服务,在迁移到Dubbo 3时,Dubbo 3 会需要一个元数据中心来维护RPC服务与应用的映射关系(即接口与应用的映射关系),因为如果采用了应用级别的服务发现和服务注册,在注册中心中将采用“应用 —— 实例列表”结构的数据组织形式,不再是以往的“接口 —— 实例列表”结构的数据组织形式,而以往用接口级别的服务注册和服务发现的应用服务在迁移到应用级别时,得不到接口与应用之间的对应关系,从而无法从注册中心得到实例列表信息,所以Dubbo为了兼容这种场景,在Provider端启动时,会往元数据中心存储接口与应用的映射关系。 +2. 为了让注册中心更加聚焦与地址的发现和推送能力,减轻注册中心的负担,元数据中心承载了所有的服务元数据、大量接口/方法级别配置信息等,无论是接口粒度还是应用粒度的服务发现和注册,元数据中心都起到了重要的作用。 + +如果有以上两种需求,都可以选择部署元数据中心,并通过Dubbo的配置来集成该元数据中心。 + +元数据中心并不依赖于注册中心和配置中心,用户可以自由选择是否集成和部署元数据中心,如下图所示: + +![centers-metadata](/imgs/v3/concepts/centers-metadata.png) + +该图中不配备配置中心,意味着可以不需要全局管理配置的能力。该图中不配备注册中心,意味着可能采用了Dubbo mesh的方案,也可能不需要进行服务注册,仅仅接收直连模式的服务调用。 + +## 配置中心 + +配置中心与其他两大中心不同,它无关于接口级还是应用级,它与接口并没有对应关系,它仅仅与配置数据有关,即使没有部署注册中心和元数据中心,配置中心也能直接被接入到Dubbo应用服务中。在整个部署架构中,整个集群内的实例(无论是Provider还是Consumer)都将会共享该配置中心集群中的配置,如下图所示: +![centers-config](/imgs/v3/concepts/centers-config.png) + +该图中不配备注册中心,意味着可能采用了Dubbo mesh的方案,也可能不需要进行服务注册,仅仅接收直连模式的服务调用。 + +该图中不配备元数据中心,意味着Consumer可以从Provider暴露的MetadataService获取服务元数据,从而实现RPC调用 + +## 保证三大中心高可用的部署架构 + +虽然三大中心已不再是Dubbo应用服务所必须的,但是在真实的生产环境中,一旦已经集成并且部署了该三大中心,三大中心还是会面临可用性问题,Dubbo需要支持三大中心的高可用方案。在Dubbo中就支持多注册中心、多元数据中心、多配置中心,来满足同城多活、两地三中心、异地多活等部署架构模式的需求。 + +Dubbo SDK对三大中心都支持了Multiple模式。 + +- 多注册中心:Dubbo 支持多注册中心,即一个接口或者一个应用可以被注册到多个注册中心中,比如可以注册到ZK集群和Nacos集群中,Consumer也能够从多个注册中心中进行订阅相关服务的地址信息,从而进行服务发现。通过支持多注册中心的方式来保证其中一个注册中心集群出现不可用时能够切换到另一个注册中心集群,保证能够正常提供服务以及发起服务调用。这也能够满足注册中心在部署上适应各类高可用的部署架构模式。 +- 多配置中心:Dubbo支持多配置中心,来保证其中一个配置中心集群出现不可用时能够切换到另一个配置中心集群,保证能够正常从配置中心获取全局的配置、路由规则等信息。这也能够满足配置中心在部署上适应各类高可用的部署架构模式。 + +- 多元数据中心:Dubbo 支持多元数据中心:用于应对容灾等情况导致某个元数据中心集群不可用,此时可以切换到另一个元数据中心集群,保证元数据中心能够正常提供有关服务元数据的管理能力。 + +拿注册中心举例,下面是一个多活场景的部署架构示意图: + +![multiple-registry-deployment-architecture](/imgs/v3/concepts/multiple-registry-deployment-architecture.png) diff --git a/content/zh/docs3-building/docs/what/ecosystem.md b/content/zh/docs3-building/docs/what/ecosystem.md new file mode 100644 index 000000000000..6cd59c98d378 --- /dev/null +++ b/content/zh/docs3-building/docs/what/ecosystem.md @@ -0,0 +1,57 @@ +--- +type: docs +title: "Dubbo 生态" +linkTitle: "生态系统" +weight: 3 +description: "" +--- + +### 多语言实现 +* Golang +* Java +* Rust +* Javascript +* Python +* PHP + +### Dashboard +* Dubbo-admin + +### 中心化组件 +* 注册中心 + * Zookeeper + * Nacos + * Kubernetes +* 元数据中心 + * Zookeeper + * Nacos + * Redis +* 配置中心 + * Zookeeper + * Nacos + * Redis + * Apollo + +### 协议与互通性 +* 如何实现与 gRPC 体系互通 +* 如何实现与 Spring Cloud 体系互通 + +### SPI 集成 +* dubbo-spi-extensions +* 参见 SPI 扩展说明 + +### 网关组件 +* Apache Shenyu(Incubating) +* Apache APISIX +* Apache Dubbo-pixiu +* Tengine + +### 链路追踪 +* Zipkin +* Apache Skywalking + +### 其他微服务组件 +* Sentinel +* Seata + + diff --git a/content/zh/docs3-building/docs/what/overview.md b/content/zh/docs3-building/docs/what/overview.md new file mode 100644 index 000000000000..9021b8c2fdbb --- /dev/null +++ b/content/zh/docs3-building/docs/what/overview.md @@ -0,0 +1,113 @@ +--- +type: docs +title: "Dubbo 简介" +linkTitle: "了解 Dubbo" +weight: 1 +description: "" +--- + +Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,并提供了 Java、Golang 等多种语言的 SDK 实现。使用 Dubbo 开发的微服务,将原生具备相互之间的远程地址发现与通信能力, +同时利用 Dubbo 提供的丰富服务治理特性,可以实现诸如服务发现、负载均衡、流量调度等服务治理诉求。Dubbo 被设计为高度可扩展,用户几乎可以在任意功能点去定制自己的实现以满足自身业务需求。 + +3.0 版本(Dubbo3)开始,我们将 Dubbo 定义为面向云原生的下一代 RPC 服务框架。3.0 基于 [Dubbo 2.x]() 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全性等几大方向上进行了全面升级。 + +### Dubbo 是什么 + +Apache Dubbo 是一款开源 RPC 服务框架,它最初在 2008 年由 Alibaba 捐献开源,并且很快成为了国内开源服务框架选型的事实标准框架,得到了各行各业的广泛应用。在 2017 年,Dubbo 正式捐献到 Apache 软件基金会并成为 Apache 顶级项目,目前 Dubbo3 已经是一站式的微服务解决方案提供: +* 基于 HTTP/2 的 [Triple 协议]()以及面向代理 API 的编程体验。 +* 强大的[流量治理能力](),如地址发现、负载均衡、路由选址、动态配置等。 +* [多语言 SDK 实现](),涵盖 Java、Golang、Javascript 等,更多语言实现将会陆续发布。 +* 灵活的适配与扩展能力,可轻松与微服务体系其他组件如 Tracing、Transaction 等适配。 +* [Service Mesh 解决方案](),同时支持 Sidecar、Proxyless 等灵活的 Mesh 部署方案。 + +Apache Dubbo 总体架构能很好的满足企业的大规模微服务实践,因为它从设计之初就是为了解决超大规模微服务集群实践问题,不论是阿里巴巴还是工商银行、中国平安、携程等社区用户,它们都通过多年的大规模生产环境流量对 Dubbo 的稳定性与性能进行了充分验证,因此,Dubbo 在解决业务落地与规模化实践方面有着无可比拟的优势: +* 开箱即用 + * 易用性高,如 Java 版本的面向接口代理特性能实现本地透明调用 + * 功能丰富,基于原生库或轻量扩展即可实现绝大多数的微服务治理能力 +* 面向超大规模微服务集群设计 + * 极致性能,高性能的 RPC 通信协议设计与实现 + * 横向可扩展,轻松支持百万规模集群实例的地址发现与流量治理 +* [高度可扩展]() + * 调用过程中对流量及协议的拦截扩展,如 Filter、Router、LB 等 + * 微服务治理组件扩展,如 Registry、Config Center、Metadata Center 等 +* 企业级微服务治理能力 + * 国内共有云厂商支持的事实标准服务框架 + * 多年企业实践经验考验,参考[用户实践案例](../../users) + +### Dubbo 基本工作流程 + +![dubbo-rpc](/imgs/v3/concepts/rpc.png) + +Dubbo 首先是一款 RPC 框架,它定义了自己的 RPC 通信协议与编程方式。如上图所示,用户在使用 Dubbo 时首先需要定义好 Dubbo 服务;其次,是在将 Dubbo 服务部署上线之后,依赖 Dubbo 的应用层通信协议实现数据交换,Dubbo 所传输的数据都要经过序列化,而这里的[序列化协议]()是完全可扩展的。 +使用 Dubbo 的第一步就是定义 Dubbo 服务,服务在 Dubbo 中的定义就是完成业务功能的一组方法的集合,可以选择使用与某种语言绑定的方式定义,如在 Java 中 Dubbo 服务就是有一组方法的 Interface 接口,也可以使用语言中立的 Protobuf Buffers [IDL 定义服务]()。定义好服务之后,服务端(Provider)需要提供服务的具体实现,并将其声明为 Dubbo 服务,而站在服务消费方(Consumer)的视角,通过调用 Dubbo 框架提供的 API 可以获得一个服务代理(stub)对象,然后就可以像使用本地服务一样对服务方法发起调用了。 +在消费端对服务方法发起调用后,Dubbo 框架负责将请求发送到部署在远端机器上的服务提供方,提供方收到请求后会调用服务的实现类,之后将处理结果返回给消费端,这样就完成了一次完整的服务调用。如图中的 Request、Response 数据流程所示。 +>需要注意的是,在 Dubbo 中,我们提到服务时,通常是指 RPC 粒度的、提供某个具体业务增删改功能的接口或方法,与一些微服务概念书籍中泛指的服务并不是一个概念。 + +在分布式系统中,尤其是随着微服务架构的发展,应用的部署、发布、扩缩容变得极为频繁,作为 RPC 消费方,如何定动态的发现服务提供方地址成为 RPC 通信的前置条件。Dubbo 提供了自动的地址发现机制,用于应对分布式场景下机器实例动态迁移的问题。如下图所示,通过引入注册中心来协调提供方与消费方的地址,提供者启动之后向注册中心注册自身地址,消费方通过拉取或订阅注册中心特定节点,动态的感知提供方地址列表的变化。 + +![arch-service-discovery](/imgs/v3/architecture.png) + +地址发现解决了实例变更的问题,但微服务环境下的服务治理诉求同样变得非常复杂,用户需要考虑 Dubbo 服务治理的问题如服务测试、服务元数据管理、流量管控、动态行为调整等,为此, Dubbo 架构引入了配置中心、元数据中心进一步拓展了其服务治理边界。 + +![how-dubbo-works](/imgs/v3/concepts/threecenters.png) + +随着云原生架构的发展,更多的微服务组件及能力正下沉到以 Kubernetes 为代表的基础设施层。一方面传统微服务开发框架应剔除一些冗余机制,积极的适配到基础设施层以做到能力复用;另一方面微服务框架生命周期、服务治理等能力应更好地与 Kubernetes 服务编排机制融合。更近一步的,以 Service Mesh 为代表的微服务架构给微服务开发带来了新的选择,Dubbo3 也完成了对 Kubernetes、Mesh 的适配。 + +![mix-mesh](/imgs/v3/mesh/mix-mesh.png) + +### Dubbo 核心特性 + +#### 高性能 RPC 通信协议 +跨进程或主机的服务通信是 Dubbo 的一项基本能力,Dubbo RPC 以预先定义好的协议编码方式将请求数据(Request)发送给后端服务,并接收服务端返回的计算结果(Response)。RPC 通信对用户来说是完全透明的,使用者无需关心请求是如何发出去的、发到了哪里,每次调用只需要拿到正确的调用结果就行。除了同步模式的 Request-Response 通信模型外,Dubbo3 还提供更丰富的通信模型选择: +* 消费端异步请求(Client Side Asynchronous Request-Response) +* 提供端异步执行(Server Side Asynchronous Request-Response) +* 消费端请求流(Request Streaming) +* 提供端响应流(Response Streaming) +* 双向流式通信(Bidirectional Streaming) + +具体可参见[可选协议列表]()、[Triple协议]() + +#### 自动服务(地址)发现 +Dubbo 的服务发现机制,让微服务组件之间可以独立演进并任意部署,消费端可以在无需感知对端部署位置与 IP 地址的情况下完成通信。Dubbo 提供的是 Client-Based 的服务发现机制,使用者可以有多种方式启用服务发现: +* 使用独立的注册中心组件,如 Nacos、Zookeeper、Consul、Etcd 等。 +* 将服务的组织与注册交给底层容器平台,如 Kubernetes,这被理解是一种更云原生的使用方式 + +#### 运行态流量管控 +透明地址发现让 Dubbo 请求可以被发送到任意 IP 实例上,这个过程中流量被随机分配。当需要对流量进行更丰富、更细粒度的管控时,就可以用到 Dubbo 的流量管控策略,Dubbo 提供了包括负载均衡、流量路由、请求超时、流量降级、重试等策略,基于这些基础能力可以轻松的实现更多场景化的路由方案,包括金丝雀发布、A/B测试、权重路由、同区域优先等,更酷的是,Dubbo 支持流控策略在运行态动态生效,无需重新部署。具体可参见: +* [路由说明]() +* [负载均衡]() +* [服务治理示例]() + +#### 丰富的扩展组件及生态 +Dubbo 强大的服务治理能力不仅体现在核心框架上,还包括其优秀的扩展能力以及周边配套设施的支持。通过 Filter、Router、Protocol 等几乎存在于每一个关键流程上的扩展点定义,我们可以丰富 Dubbo 的功能或实现与其他微服务配套系统的对接,包括 Transaction、Tracing 目前都有通过 SPI 扩展的实现方案,具体可以参见 Dubbo 扩展性的详情,也可以在 [apache/dubbo-spi-extensions](https://github.com/apache/dubbo-spi-extensions) 项目中发现与更多的扩展实现。具体可参见: +* [Dubbo 生态]() +* [官方扩展组件]() +* [Dubbo 可扩展性设计]() + +#### 面向云原生设计 + +Dubbo 从设计上是完全遵循云原生微服务开发理念的,这体现在多个方面,首先是对云原生基础设施与部署架构的支持,包括 Kubernetes、Service Mesh 等,另一方面,Dubbo 众多核心组件都已面向云原生升级,包括 Triple 协议、统一路由规则、对多语言支持。值得一提的是,如何使用 Dubbo 支持弹性伸缩的服务如 Serverless 也在未来计划之中,这包括利用 Native Image 提高 Dubbo 的启动速度与资源消耗等。 + +结合当前版本,本节主要从以下两点展开 Dubbo 的云原生特性 +* 容器调度平台(Kubernetes) +* Service Mesh + +##### Kubernetes +Dubbo 微服务要支持 Kubernetes 平台调度,最基础的就是实现 dubbo 服务生命周期与容器生命周期的对齐,这包括 Dubbo 的启动、销毁、服务注册等生命周期事件。相比于以往 Dubbo 自行定义生命周期事件,并要求开发人员在运维实践过程中遵守约定,Kubernetes 底层基础设施定义了严格的组件生命周期事件(probe),转而要求 Dubbo 去按约定适配。 + +Kubernetes Service 是另一个层面的适配,这体现了服务定义与注册向云原生底层基础设施下沉的趋势。在这种模式下,用户不再需要搭建额外的注册中心组件,Dubbo 消费端节点能自动对接到 Kubernetes(API-Server 或 DNS),根据服务名(Kubernetes Service Name) 查询到实例列表(Kubernetes endpoints)。 此时服务是通过标准的 Kubernetes Service API 定义,并被调度到各个节点。 + +##### Service Mesh + +Service Mesh 在业界得到了广泛的传播与认可,并被认为是下一代的微服务架构,这主要是因为它解决了很多棘手的问题,包括透明升级、多语言、依赖冲突、流量治理等。Service Mesh 的典型架构是通过部署独立的 Sidecar 组件来拦截所有的出口与入口流量,并在 Sidecar 中集成丰富的流量治理策略如负载均衡、路由等,除此之外,Service Mesh 还需要一个控制面(Control Panel)来实现对 Sidecar 流量的管控,即各种策略下发。我们在这里称这种架构为经典 Mesh。 + +然而任何技术架构都不是完美的,经典 Mesh 在实施层面也面临成本过高的问题 +1. 需要运维控制面(Control Panel) +2. 需要运维 Sidecar +3. 需要考虑如何从原有 SDK 迁移到 Sidecar +4. 需要考虑引入 Sidecar 后整个链路的性能损耗 + +为了解决 Sidecar 引入的相关成本问题,Dubbo 引入了另一种变相的 Mesh 架构 - Proxyless Mesh,顾名思义,Proxyless Mesh 就是指没有 Sidecar 的部署,转而由 Dubbo SDK 直接与控制面交互,其架构图如下 + + +可以设想,在不同的组织、不同的发展阶段,未来以 Dubbo 构建的微服务将会允许有三种部署架构:传统 SDK、基于 Sidecar 的 Service Mesh、脱离 Sidecar 的 Proxyless Mesh。基于 Sidecar 的 Service Mesh,即经典的 Mesh 架构,独立的 sidecar 运行时接管所有的流量,脱离 Sidecar 的 Proxyless Mesh,富 SDK 直接通过 xDS 与控制面通信。Dubbo 微服务允许部署在物理机、容器、Kubernetes 平台之上,能做到以 Admin 为控制面,以统一的流量治理规则进行治理。 diff --git a/content/zh/docs3-building/docs/what/usecases.md b/content/zh/docs3-building/docs/what/usecases.md new file mode 100644 index 000000000000..723ca4e20d8c --- /dev/null +++ b/content/zh/docs3-building/docs/what/usecases.md @@ -0,0 +1,9 @@ +--- +type: docs +title: "用户案例" +linkTitle: "用户案例" +weight: 4 +manualLinkRelref: ../../users/ +manualLinkTarget: _blank +_build: { render: link } +--- \ No newline at end of file diff --git a/content/zh/docs3-building/docs/whatsnew/_index.md b/content/zh/docs3-building/docs/whatsnew/_index.md new file mode 100755 index 000000000000..a1271d1a58d9 --- /dev/null +++ b/content/zh/docs3-building/docs/whatsnew/_index.md @@ -0,0 +1,31 @@ + +--- +type: docs +title: "3.0 速览" +linkTitle: "3.0 速览" +weight: 3 +content: + - 核心特性: + - "[Triple]()" + - "[Examples]()" + - 关注迁移: + - "[Java](../java-sdk/)" + - "[Golang](../golang-sdk/upgrades-and-compatibility/)" + - 其他问题: + - "[性能指标](../java-sdk/)" + - "[相关资源](../golang-sdk/upgrades-and-compatibility/)" +cascade: + - show_banner: true +--- + +[背景](background.md) 部分带你快速了解 Dubbo3 的设计背景、总体架构与核心特性、与典型用户如阿里巴巴 HSF2 的关系等。也可以通过如下部分了解更多: +* 小白用户,**快速浏览 Dubbo3 核心特性:** + * [下一代通信协议 - Triple](triple.md) + * [百万实例集群的秘密 - 应用级服务发现](servicediscovery3.md) + * [Dubbo Mesh](mesh.md) +* Dubbo3 的兼容性与迁移成本? + * [Java - 迁移指南](../../java-sdk/) + * [Golang - 迁移指南](../../golang-sdk/) +* 其他 **Dubbo3 相关资源:** + * 更多资料,如性能指标、高级特性说明等请参考 [多语言 SDK 实现](../mannual/) + * [演讲与线下活动](../../activities/) diff --git a/content/zh/docs3-building/docs/whatsnew/background.md b/content/zh/docs3-building/docs/whatsnew/background.md new file mode 100644 index 000000000000..ded8a5751913 --- /dev/null +++ b/content/zh/docs3-building/docs/whatsnew/background.md @@ -0,0 +1,66 @@ +--- +type: docs +title: "背景" +linkTitle: "背景" +weight: 1 +--- +Dubbo3 的设计与开发有两个大的背景。 + +**首先,如何更好的满足企业实践诉求。** Dubbo 自 2011 由阿里巴巴捐献开源以来,一直是众多大型企业微服务实践的首选开源服务框架。在此期间,企业架构经历了从 SOA 架构到微服务架构变迁,Dubbo 社区自身也在不断的更新迭代以更好的满足企业诉求。然而 Dubbo2 架构上的局限逐渐在实践中凸显:1.协议,Dubbo2 协议以性能、简洁著称,但却在云原生时代遇到越来越多的通用性、穿透性问题;2.可伸缩性,Dubbo2 在可伸缩性上依旧远超很多其他框架,但随着微服务带来更多应用与实例我们不得不思考如何应对更大规模集群的实战;3.服务治理易用性,如更丰富的流量治理、可观测性、智能负载均衡等。 + +**其次,适配云原生技术栈的发展。** 微服务让业务开发演进更灵活、快捷的同时,也带来了一些它独有的特征和需求:如微服务之后组件数量越来越多,如何解决各个组件的稳定性,如何快速的水平扩容等,以 Docker、Kubernetes、Service Mesh 为代表的云原生基础设施为解决这些问题带来了一些新的选择。随着更多的微服务组件及能力正下沉到以 Kubernetes 为代表的基础设施层,传统微服务开发框架应剔除一些冗余机制,积极的适配到基础设施层以做到能力复用,微服务框架生命周期、服务治理等能力应更好地与 Kubernetes 服务编排机制融合; 以 Service Mesh 为代表微服务架构给微服务开发带来的新的选择,Sidecar 给多语言、透明升级、流量管控等带来的优势,但同时也带来运维复杂性、性能损耗等弊端,因此基于服务框架的传统微服务体系还将是主流,长期仍将占据半壁江山,在长时间内将会维持混合部署将会维持混合部署状态。 + +### 总体目标 +Dubbo3 依旧保持了 2.x 的经典架构,以解决微服务进程间通信为主要职责,通过丰富的服务治理(如地址发现、流量管理等)能力来更好的管控微服务集群;Dubbo3 对原有框架的升级是全面的,体现在核心 Dubbo 特性的几乎每个环节,通过升级实现了稳定性、性能、伸缩性、易用性的全面提升。 + +![architecture-1](../../../imgs/v3/concepts/architecture-1.png) + +* **通用的通信协议。** 全新的 RPC 协议应摒弃私有协议栈,以更通用的 HTTP/2 协议为传输层载体,借助 HTTP 协议的标准化特性,解决流量通用性、穿透性等问题,让协议能更好的应对前后端对接、网关代理等场景;支持 Stream 通信模式,满足不同业务通信模型诉求的同时给集群带来更大的吞吐量。 +* **面向百万集群实例,集群高度可伸缩。** 随着微服务实践的推广,微服务集群实例的规模也在不停的扩展,这得益于微服务轻量化、易于水平扩容的特性,同时也给整个集群容量带来了负担,尤其是一些中心化的服务治理组件;Dubbo3 需要解决实例规模扩展带来的种种资源瓶颈问题,实现真正的无限水平扩容。 +* **更丰富的编程模型,更小的业务侵入。** 在开发态业务应用面向 Dubbo SDK 编程,在运行态 SDK 与业务应用运行在同一个进程,SDK 的易用性、稳定性与资源消耗将在很大程度上影响业务应用;因此 3.0 应该具备更抽象的 API、更友好的配置模式、更少的侵占业务应用资源、具备更高的可用性。 +* **更易用、更丰富的服务治理能力。** 微服务的动态特性给治理工作带来了很高的复杂性,而 Dubbo 这方面一直做的不错,是最早的一批治理能力定义者与实践者;3.0 需面向更丰富的场景化,提供诸如可观测性、安全性、灰度发布、错误注入、外部化配置、统一的治理规则等能力。 +* **全面拥抱云原生。** + +### 面向企业生产实践痛点 +Dubbo2 仍旧是国内首选开源服务框架,被广泛应用在互联网、金融保险、软件企业、传统企业等几乎所有数字化转型企业中,久经规模化生产环境检验。以 Dubbo2 的贡献者和典型用户阿里巴巴为例,阿里巴巴基于 Dubbo2 在内部维护的 HSF2 框架经历了历次双十一峰值考验,每天数十亿次的 RPC 调用,治理着超过千万的服务实例。在长期的优化和实践积累中,阿里巴巴有了对下一代服务框架的设想与方案,在内部开始了快速演进,并快速的被贡献到 Apache 社区,如同阿里巴巴一样,其他用户的实践诉求与痛点也在开源社区快速的积累,形成了一致的方向和技术方案,可以说 Dubbo3 的诞生就来自于超大基数的企业用户积累,为了更好的满足他们的实践诉求。 + +![dubbo3-hsf](../../../imgs/v3/concepts/dubbo-hsf.png) + +Dubbo3 融合了阿里巴巴 HSF2 及其他社区企业的大量服务治理经验,当前 Dubbo3 已经被全面应用到生产实践环境,用户包括阿里巴巴电商、饿了么、钉钉、考拉、阿里云、小米、工商银行、风火递、平安健康等。社区与用户的合作形成的良性循环极大的促进了 Dubbo3 的发展,阿里巴巴已经以社区版 Dubbo3 完全取代了内部维护的 HSF2 框架,他们的实践经验一方面推动 Dubbo3 的稳定性,另一方面正够源源不断的将服务治理实践经验输出到开源社区。 + +### 面向百万集群实例,横向可扩容 +随着微服务实践经验的积累,微服务被拆分成更细粒度,部署到越来越多的机器实例,以支撑不断增长的业务规模。在众多的 Dubbo2 企业用户中,尤其是以金融保险、互联网为代表的规模化企业开始遇到集群容量瓶颈问题(典型的请参照[工商银行实践案例]()): +* 服务发现过程 + * 注册中心数据存储规模达到容量瓶颈 + * 数据注册&推送效率严重下降 +* Dubbo 进程 + * 侵占更多机器资源,导致业务资源利用率降低 + * 频繁 GC 影响业务稳定性 + +Dubbo3 在设计上很好的解决了这些问题,通过全新设计实现的服务治理(服务发现)模型,可以实现服务发现链路上的数据传输、数据存储量平均下降 90% 左右;同时 Dubbo3 自身在业务进程中变得更轻量、更稳定,实现提升资源利用率 50%。 + +Dubbo3 一个更大的优势在于其对整体架构稳定性的提升,新的服务发现架构使得对于整个集群容量、可伸缩性评估变得更容易、更准确。 + +![capacity](../../../imgs/v3/concepts/capacity.png) + +如果将应用开发粗略划分为业务开发、运维部署两个层次,其中变化比较频繁的因素包括服务(接口)、应用、机器实例。在 2.x 时代,所有这三个因素的增长都会影响微服务集群的总体容量,尤其是接口增减带来的波动,对整体容量评估是非常不透明的。而在 3.0 中集群容量变化仅与应用名、机器实例两个因素相关,而我们容量评估的对象往往都是应用与实例,因此整个集群集群变的更稳定透明。 + +### 云原生 +在云原生时代,底层基础设施的变革正深刻影响应用的部署、运维甚至开发过程,往上也影响了 Dubbo3 微服务技术方案的选型与部署模式。 + +#### 下一待 RPC 协议 +新一代的 Triple 协议基于 HTTP/2 作为传输层,具备更好的网关、代理穿透性,原生支持 Stream 通信语义,兼容 gRPC 协议。 + +#### 多语言友好 +Dubbo3 从服务定义、RPC 协议、序列化、服务治理等多个方面都已经将多语言友好性作为重点考量因素,目前提供了 Java、Golang 稳定的多语言版本,更多语言版本的 3.0 实现如 Rust、Javascript、C/C++、C# 等在开发建设中。 + +#### Kubernetes +Dubbo3 开发的应用可以原生部署到 Kubernetes 平台,Dubbo3 在地址、生命周期等已设计可与 Kubernetes 等容器调度平台对齐;对于要进一步复用 Kubernetes 底层基础设施能力的用户来说,Dubbo3 也已对接到了原生的 Kubernetes Service 体系。 + +#### Serivice Mesh +Service Mesh 强调控制面在微服务治理中的作用,在一定程度上推动了控制面通信协议、职责范围的扩展与标准化;传统 Mesh 架构下的 Sidecar 模型强调旁路代理对于流量的统一管控,以实现透明升级、多语言无感、无业务侵入等特性。 + +Dubbo3 提供了基于自身思考的 Dubbo Mesh 解决方案,强调了控制面对微服务集群的统一管控,而在部署架构上,同时支持 sicecar 与无 sidecar 的 proxyless 部署架构,使用 Dubbo Mesh 的用户基于自身的业务特点将有更多的部署架构选择。 + +#### 异构体系互通 +我们正看到越来越多的异构微服务体系互通的诉求,典型如 Dubbo、Spring Cloud、gRPC 等。有些是因为技术栈迁移,有些是组织合并后需要实现业务互调,Dubbo3 借助于新的服务发现模型以及可灵活扩展的 RPC 协议,可以成为 \ No newline at end of file diff --git a/content/zh/docs3-building/docs/whatsnew/mesh.md b/content/zh/docs3-building/docs/whatsnew/mesh.md new file mode 100644 index 000000000000..3a88a01ae13b --- /dev/null +++ b/content/zh/docs3-building/docs/whatsnew/mesh.md @@ -0,0 +1,61 @@ +--- +type: docs +title: "Dubbo Mesh" +linkTitle: "Dubbo Mesh" +weight: 4 +--- +通过以下示例快速查看使用&部署示例 +* [Dubbo Sidecar Mesh]() +* [Dubbo Proxyless Mesh]() + +## Service Mesh +在云原生背景下,如果我们将 Service Mesh 理解为底层基础设施,则在 Mesh 架构中,以往耦合在业务进程中的微服务治理部分能力正被 Mesh 接管,传统微服务框架更注重 RPC 协议与编程模型。以下是时下流行的 Mesh 产品 Istio 的架构图: + +![istio](/imgs/istio.png) + +在 Mesh 架构下 +* 统一的控制面提供证书管理、可观测性、流量治理等能力 +* Sidecar 让 SDK 更轻量、侵入性更小,更好的实现透明升级、流量拦截等 + +## Dubbo Mesh +Dubbo Mesh 从设计理念上更强调控制面的统一管控、标准化与治理能力,而在数据面给出了更多的选择,包括 Sidecar Mesh 与 Proxyless Mesh 等部署模式。 +多种部署模型给企业提供了更多选择,通过混合部署的模型,在实现服务治理控制面的共享的同时,可以更好的应对不同场景的部署要求,适应复杂的基础设施环境并从总体上提升架构的可用性。 + +![dubbo-sidecar](/imgs/dubbo-sidecar.png) + +### Sidecar Mesh + +![dubbo-sidecar](/imgs/dubbo-sidecar.png) + +如上图所示,Dubbo 可以与 Sidecar 部署在同一个 Pod 或容器中,通过在外围部署一个独立的控制平面,实现对流量和治理的统一管控。控制面与 SIcecar 之间通过图中虚线所示的 xDS 协议进行配置分发,而 Dubbo 进程间的通信不再是直连模式,转而通过 Sidecar 代理,Sidecar 拦截所有进出流量,并完成路由寻址等服务治理任务。 + +Sidecar 模式的 Mesh 架构有很多优势,如平滑升级、多语言、业务侵入小等,但也带来了一些额外的问题,比如: +* Sidecar 通信带来了额外的性能损耗,这在复杂拓扑的网络调用中将变得尤其明显。 +* Sidecar 的存在让应用的声明周期管理变得更加复杂。 +* 部署环境受限,并不是所有的环境都能满足 Sidecar 部署与请求拦截要求。 + +### Proxyless Mesh +![dubbo-proxyless](/imgs/dubbo-proxyless.png) + +针对上述问题,Dubbo 社区自很早之前就做了 Dubbo 直接对接到控制面的设想与思考,并在国内开源社区率先提出了 Proxyless Mesh 的概念,Proxyless 概念最开始是谷歌提出来的。 +Proxyless 模式使得微服务又回到了 2.x 时代的部署架构,如上图所示,和我们上面看的 Dubbo 经典服务治理模式非常相似,所以说这个模式并不新鲜, Dubbo 从最开始就是这么样的设计模式。但相比于 Mesh 架构,Dubbo2 并没有强调控制面的统一管控,而这点恰好是 Service Mesh 所强调的,强调对流量、可观测性、证书等的标准化管控与治理,也是 Mesh 理念先进的地方。 + +通过不同语言版本的 Dubbo3 SDK 直接实现 xDS 协议解析,实现 Dubbo 与 Control Plane 的直接通信,进而实现控制面对流量管控、服务治理、可观测性、安全等的统一管控,规避 Sidecar 模式带来的性能损耗与部署架构复杂性。 + +在 Dubbo3 Proxyless 架构模式下,Dubbo 进程将直接与控制面通信,Dubbo 进程之间也继续保持直连通信模式,我们可以看出 Proxyless 架构的优势: +* 没有额外的 Proxy 中转损耗,因此更适用于性能敏感应用 +* 更有利于遗留系统的平滑迁移 +* 架构简单,容易运维部署 +* 适用于几乎所有的部署环境 + +### 高级用法 +Dubbo SDK 提供了非常灵活的配置来控制服务治理行为,如接口粒度的服务地址发现能力、接口粒度的配置同步等,这些能力让应用的开发和部署更加灵活。而在通用的 Mesh 部署方案或产品下一些高级功能可能受限,从总体上影响了易用性与灵活性,为此 Dubbo 计划提供了一些变种的部署方案或控制面定制产品,以满足深度用户的一些高级需求。 + + + + + + + + + diff --git a/content/zh/docs3-building/docs/whatsnew/servicediscovery3.md b/content/zh/docs3-building/docs/whatsnew/servicediscovery3.md new file mode 100644 index 000000000000..ed73940a7370 --- /dev/null +++ b/content/zh/docs3-building/docs/whatsnew/servicediscovery3.md @@ -0,0 +1,11 @@ +--- +type: docs +title: "应用级服务发现" +linkTitle: "应用级服务发现" +weight: 3 +--- + + + + + diff --git a/content/zh/docs3-building/docs/whatsnew/triple.md b/content/zh/docs3-building/docs/whatsnew/triple.md new file mode 100644 index 000000000000..51a68a9c06a9 --- /dev/null +++ b/content/zh/docs3-building/docs/whatsnew/triple.md @@ -0,0 +1,9 @@ +--- +type: docs +title: "Triple 协议" +linkTitle: "Triple 协议" +weight: 2 +--- + + + diff --git a/content/zh/docs3-building/golang-sdk/_index.md b/content/zh/docs3-building/golang-sdk/_index.md new file mode 100755 index 000000000000..937577306c5c --- /dev/null +++ b/content/zh/docs3-building/golang-sdk/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Golang" +linkTitle: "Golang" +weight: 3 +--- + diff --git a/content/zh/docs3-building/java-sdk/_index.md b/content/zh/docs3-building/java-sdk/_index.md new file mode 100755 index 000000000000..eaaa2dacd989 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/_index.md @@ -0,0 +1,23 @@ + +--- +type: docs +title: "Java" +weight: 3 +#no_list: true +#hide_summary: true +content: + - 快速开始: + - "[Spring Boot 快速开始](quick-start/)" + - 概念与架构: + - "[性能与基准测试](concepts-and-architecture/)" + - 高级特性: + - "[常用高级配置方式](advanced-features-and-usage/)" + - 参考手册: + - "[可扩展性:Filter、Router 等](reference-manual/)" + - "[元数据中心与服务测试](reference-manual/)" + - 升级与兼容性: + - "[3.0 迁移指南](upgrades-and-compatibility/)" + - "[Triple 协议与Stream 流式接口](upgrades-and-compatibility/)" +--- + +{{% docs/content_box %}} diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/_index.md new file mode 100755 index 000000000000..1a8ace15f09b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "高级特性和用法" +linkTitle: "高级特性和用法" +weight: 4 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/observability/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/observability/_index.md new file mode 100755 index 000000000000..7370904db766 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/observability/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "可观测性类特性" +linkTitle: "可观测性类特性" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/_index.md new file mode 100755 index 000000000000..62104f7e5080 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "其他" +linkTitle: "其他" +weight: 6 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/graceful-shutdown.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/graceful-shutdown.md new file mode 100644 index 000000000000..afdae3810d15 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/graceful-shutdown.md @@ -0,0 +1,41 @@ +--- +type: docs +title: "优雅停机" +linkTitle: "优雅停机" +weight: 2 +description: "让 Dubbo 服务完成优雅停机" +--- + +Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用 `kill -9 PID` 等强制关闭指令,是不会执行优雅停机的,只有通过 `kill PID` 时,才会执行。 + +## 原理 + +服务提供方 + +* 停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。 +* 然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。 + +服务消费方 + +* 停止时,不再发起新的调用请求,所有新的调用在客户端即报错。 +* 然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。 + +## 设置方式 + +设置优雅停机超时时间,缺省超时时间是 10 秒,如果超时则强制关闭。 + +```properties +# dubbo.properties +dubbo.service.shutdown.wait=15000 +``` + +如果 ShutdownHook 不能生效,可以自行调用: + +```java +DubboShutdownHook.destroyAll(); +``` + +{{% alert title="建议" color="primary" %}} +使用 tomcat 等容器部署的场景,建议通过扩展 ContextListener 等自行调用以下代码实现优雅停机 +{{% /alert %}} + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/lifecycle.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/lifecycle.md new file mode 100644 index 000000000000..71ef8cc89d08 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/lifecycle.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "Kubernetes 生命周期探针" +linkTitle: "Kubernetes 生命周期探针" +weight: 6 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/logger-strategy.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/logger-strategy.md new file mode 100644 index 000000000000..59c60744fa04 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/logger-strategy.md @@ -0,0 +1,29 @@ +--- +type: docs +title: "日志框架适配" +linkTitle: "日志框架适配" +weight: 5 +description: "在 Dubbo 中适配日志框架" +--- + +自 `2.2.1` 开始,dubbo 开始内置 log4j、slf4j、jcl、jdk 这些日志框架的适配[1],也可以通过以下方式显式配置日志输出策略: + +0. 命令行 + + ```sh + java -Ddubbo.application.logger=log4j + ``` + +0. 在 `dubbo.properties` 中指定 + + ``` + dubbo.application.logger=log4j + ``` + +0. 在 `dubbo.xml` 中配置 + + ```xml + + ``` + +[1]: 自定义扩展可以参考 [日志适配扩展](../../../dev/impls/logger-adapter) diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/service-container.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/service-container.md new file mode 100644 index 000000000000..50f704b2f268 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/service-container.md @@ -0,0 +1,67 @@ +--- +type: docs +title: "自定义服务容器" +linkTitle: "自定义服务容器" +weight: 1 +description: "使用 Dubbo 中的服务容器" +--- + +服务容器是一个 standalone 的启动程序,因为后台服务不需要 Tomcat 或 JBoss 等 Web 容器的功能,如果硬要用 Web 容器去加载服务提供方,增加复杂性,也浪费资源。 + +服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。 + +服务容器的加载内容可以扩展,内置了 spring, jetty, log4j 等加载,可通过[容器扩展点](../../references/spis/container)进行扩展。配置配在 java 命令的 -D 参数或者 `dubbo.properties` 中。 + +## 容器类型 + +### Spring Container + +* 自动加载 `META-INF/spring` 目录下的所有 Spring 配置。 +* 配置 spring 配置加载位置: + + ```properties + dubbo.spring.config=classpath*:META-INF/spring/*.xml + ``` + +### Jetty Container + +* 启动一个内嵌 Jetty,用于汇报状态。 +* 配置: + * `dubbo.jetty.port=8080`:配置 jetty 启动端口 + * `dubbo.jetty.directory=/foo/bar`:配置可通过 jetty 直接访问的目录,用于存放静态文件 + * `dubbo.jetty.page=log,status,system`:配置显示的页面,缺省加载所有页面 + + +### Log4j Container + +* 自动配置 log4j 的配置,在多进程启动时,自动给日志文件按进程分目录。 +* 配置: + * `dubbo.log4j.file=/foo/bar.log`:配置日志文件路径 + * `dubbo.log4j.level=WARN`:配置日志级别 + * `dubbo.log4j.subdirectory=20880`:配置日志子目录,用于多进程启动,避免冲突 + +## 容器启动 + +缺省只加载 spring + +```sh +java org.apache.dubbo.container.Main +``` + +通过 main 函数参数传入要加载的容器 + +```sh +java org.apache.dubbo.container.Main spring jetty log4j +``` + +通过 JVM 启动参数传入要加载的容器 + +```sh +java org.apache.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j +``` + +通过 classpath 下的 `dubbo.properties` 配置传入要加载的容器 + +``` +dubbo.container=spring,jetty,log4j +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/set-host.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/set-host.md new file mode 100644 index 000000000000..93f5e4d926a5 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/others/set-host.md @@ -0,0 +1,75 @@ +--- +type: docs +title: "主机地址自定义暴露" +linkTitle: "主机地址自定义暴露" +weight: 3 +description: "自定义 Dubbo 服务对外暴露的主机地址" +--- + +## 背景 + +在 Dubbo 中, Provider 启动时主要做两个事情,一是启动 server,二是向注册中心注册服务。启动 server 时需要绑定 socket,向注册中心注册服务时也需要发送 socket 唯一标识服务地址。 + +1. `dubbo`中不设置`host`时默认`host`是什么? +2. 那在`dubbo`中如何指定服务的`host`,我们是否可以用hostname或domain代替IP地址作为`host`? +3. 在使用docker时,有时需要设置端口映射,此时,启动server时绑定的socket和向注册中心注册的socket使用不同的端口号,此时又该如何设置? + +#### dubbo 中不设置 host 时默认 host 是什么 + +一般的 dubbo 协议配置如下: +``` xml + ... + + ... +``` + +可以看到,只配置了端口号,没有配置 host,此时设置的 host 又是什么呢? + +查看代码发现,在 `org.apache.dubbo.config.ServiceConfig#findConfigedHosts()` 中,通过 `InetAddress.getLocalHost().getHostAddress()` 获取默认 host。其返回值如下: + +1. 未联网时,返回 127.0.0.1 +2. 在阿里云服务器中,返回私有地址,如: 172.18.46.234 +3. 在本机测试时,返回公有地址,如: 30.5.10.11 + +#### 那在 dubbo 中如何指定服务的 socket? + +除此之外,可以通过 `dubbo.protocol` 或 `dubbo.provider `的 `host` 属性对 `host` 进行配置,支持IP地址和域名,如下: + +``` xml + ... + + ... +``` + +#### 在使用 docker 时,有时需要设置端口映射,此时,启动 server 时绑定的 socket 和向注册中心注册的 socket 使用不同的端口号,此时又该如何设置? + +见 [dubbo 通过环境变量设置 host](https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-docker) + +有些部署场景需要动态指定服务注册的地址,如 docker bridge 网络模式下要指定注册宿主机 ip 以实现外网通信。dubbo 提供了两对启动阶段的系统属性,用于设置对外通信的ip、port地址。 + +* DUBBO_IP_TO_REGISTRY --- 注册到注册中心的ip地址 +* DUBBO_PORT_TO_REGISTRY --- 注册到注册中心的port端口 +* DUBBO_IP_TO_BIND --- 监听ip地址 +* DUBBO_PORT_TO_BIND --- 监听port端口 + +以上四个配置项均为可选项,如不配置 dubbo 会自动获取 ip 与端口,请根据具体的部署场景灵活选择配置。 +dubbo 支持多协议,如果一个应用同时暴露多个不同协议服务,且需要为每个服务单独指定 ip 或 port,请分别在以上属性前加协议前缀。 如: + +* HESSIAN_DUBBO_PORT_TO_BIND hessian协议绑定的port +* DUBBO_DUBBO_PORT_TO_BIND dubbo协议绑定的port +* HESSIAN_DUBBO_IP_TO_REGISTRY hessian协议注册的ip +* DUBBO_DUBBO_PORT_TO_BIND dubbo协议注册的ip + +PORT_TO_REGISTRY 或 IP_TO_REGISTRY 不会用作默认 PORT_TO_BIND 或 IP_TO_BIND,但是反过来是成立的 +如设置 PORT_TO_REGISTRY=20881 IP_TO_REGISTRY=30.5.97.6,则 PORT_TO_BIND IP_TO_BIND 不受影响 +如果设置 PORT_TO_BIND=20881 IP_TO_BIND=30.5.97.6,则默认 PORT_TO_REGISTRY=20881 IP_TO_REGISTRY=30.5.97.6 + +## 总结 + + 1. 可以通过`dubbo.protocol`或`dubbo.provider`的`host`属性对`host`进行配置,支持IP地址和域名.但此时注册到注册中心的IP地址和监听IP地址是同一个值 + 2. 为了解决在虚拟环境或局域网内consumer无法与provider通信的问题,可以通过环境变量分别设置注册到注册中心的IP地址和监听IP地址,其优先级高于`dubbo.protocol`或`dubbo.provider`的`host`配置 + +## 参考 + + 1. [Proposal: support hostname or domain in service discovery.](https://github.com/apache/dubbo/issues/2043) + 2. [dubbo通过环境变量设置host](https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-docker) diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/_index.md new file mode 100755 index 000000000000..205a163149fb --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "性能优化类特性" +linkTitle: "性能优化类特性" +weight: 5 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/reference-config-cache.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/reference-config-cache.md new file mode 100644 index 000000000000..f5467cd3a984 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/reference-config-cache.md @@ -0,0 +1,43 @@ +--- +type: docs +title: "服务引用配置对象缓存" +linkTitle: "服务引用配置对象缓存" +weight: 2 +description: "在 Dubbo 中缓存 ReferenceConfig" +--- + +`ReferenceConfig` 实例很重,封装了与注册中心的连接以及与提供者的连接,需要缓存。否则重复生成 `ReferenceConfig` 可能造成性能问题并且会有内存和连接泄漏。在 API 方式编程时,容易忽略此问题。 + +因此,自 `2.4.0` 版本开始, dubbo 提供了简单的工具类 `ReferenceConfigCache`用于缓存 `ReferenceConfig` 实例。 + +使用方式如下: + +```java +ReferenceConfig reference = new ReferenceConfig(); +reference.setInterface(XxxService.class); +reference.setVersion("1.0.0"); +...... +ReferenceConfigCache cache = ReferenceConfigCache.getCache(); +// cache.get方法中会缓存 Reference对象,并且调用ReferenceConfig.get方法启动ReferenceConfig +XxxService xxxService = cache.get(reference); +// 注意! Cache会持有ReferenceConfig,不要在外部再调用ReferenceConfig的destroy方法,导致Cache内的ReferenceConfig失效! +// 使用xxxService对象 +xxxService.sayHello(); +``` + +消除 Cache 中的 `ReferenceConfig`,将销毁 `ReferenceConfig` 并释放对应的资源。 + +```java +ReferenceConfigCache cache = ReferenceConfigCache.getCache(); +cache.destroy(reference); +``` + +缺省 `ReferenceConfigCache` 把相同服务 Group、接口、版本的 `ReferenceConfig` 认为是相同,缓存一份。即以服务 Group、接口、版本为缓存的 Key。 + +可以修改这个策略,在 `ReferenceConfigCache.getCache` 时,传一个 `KeyGenerator`。详见 `ReferenceConfigCache` 类的方法。 + +```java +KeyGenerator keyGenerator = new ... +ReferenceConfigCache cache = ReferenceConfigCache.getCache(keyGenerator ); +``` + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/simplify-registry-data.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/simplify-registry-data.md new file mode 100644 index 000000000000..dfd466fa0efb --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/simplify-registry-data.md @@ -0,0 +1,262 @@ +--- +type: docs +title: "注册信息简化" +linkTitle: "注册信息简化" +weight: 3 +description: "减少注册中心上服务的注册数据" +--- + +## 背景 + +Dubbo provider 中的服务配置项有接近 [30 个配置项](http://dubbo.apache.org/en-us/docs/user/references/xml/dubbo-service.html)。 排除注册中心服务治理需要之外,很大一部分配置项是 provider 自己使用,不需要透传给消费者。这部分数据不需要进入注册中心,而只需要以 key-value 形式持久化存储。 + +Dubbo consumer 中的配置项也有 [20+个配置项](http://dubbo.apache.org/en-us/docs/user/references/xml/dubbo-reference.html)。在注册中心之中,服务消费者列表中只需要关注 application,version,group,ip,dubbo 版本等少量配置,其他配置也可以以 key-value 形式持久化存储。 + +这些数据是以服务为维度注册进入注册中心,导致了数据量的膨胀,进而引发注册中心(如 zookeeper)的网络开销增大,性能降低。 + +## 现有功能 sample + +当前现状一个简单展示。通过这个展示,分析下为什么需要做简化配置。 + +参考 sample 子工程: dubbo-samples-simplified-registry/dubbo-samples-simplified-registry-nosimple (跑 sample 前,先跑下 ZKClean 进行配置项清理) + +dubbo-provider.xml配置 + +``` + + + + +``` + +启动 provider 的 main 方法之后,查看 zookeeper 的叶子节点(路径为:/dubbo/org.apache.dubbo.samples.simplified.registry.nosimple.api.DemoService/providers 目录下)的内容如下: + +``` +dubbo%3A%2F%2F30.5.124.158%3A20880%2Forg.apache.dubbo.samples.simplified.registry.nosimple.api.DemoService +%3Fanyhost%3Dtrue%26application%3Dsimplified-registry-xml-provider%26async%3Dtrue%26dubbo%3D +2.0.2%26**executes**%3D4500%26generic%3Dfalse%26group%3Ddubbo-simple%26interface%3D +org.apache.dubbo.samples.simplified.registry.nosimple.api.DemoService%26methods%3D +sayHello%26**owner**%3Dvict%26pid%3D2767%26**retries**%3D7%26revision%3D1.2.3%26side%3D +provider%26**timeout**%3D5300%26timestamp%3D1542361152795%26valid%3Dtrue%26version%3D1.2.3 +``` + +从加粗字体中能看到有:executes, retries, owner, timeout。但是这些字段不是每个都需要传递给 dubbo ops 或者 dubbo consumer。 同样的,consumer 也有这个问题,可以在例子中启动 Consumer 的 main 方法进行查看。 + +## 设计目标和宗旨 + +期望简化进入注册中心的 provider 和 consumer 配置数量。 +期望将部分配置项以其他形式存储。这些配置项需要满足:不在服务调用链路上,同时这些配置项不在注册中心的核心链路上(服务查询,服务列表)。 + +## 配置 + +简化注册中心的配置,只在 2.7 之后的版本中进行支持。 +开启 provider 或者 consumer 简化配置之后,默认保留的配置项如下: + +provider: + +| Constant Key | Key | remark | +| ------ | ------ | ------ | +| APPLICATION_KEY | application | | +| CODEC_KEY | codec | | +| EXCHANGER_KEY | exchanger | | +| SERIALIZATION_KEY | serialization | | +| CLUSTER_KEY | cluster | | +| CONNECTIONS_KEY | connections | | +| DEPRECATED_KEY | deprecated | | +| GROUP_KEY | group | | +| LOADBALANCE_KEY | loadbalance | | +| MOCK_KEY | mock | | +| PATH_KEY | path | | +| TIMEOUT_KEY | timeout | | +| TOKEN_KEY | token | | +| VERSION_KEY | version | | +| WARMUP_KEY | warmup | | +| WEIGHT_KEY | weight | | +| TIMESTAMP_KEY | timestamp | | +| DUBBO_VERSION_KEY | dubbo | | +| SPECIFICATION_VERSION_KEY | **specVersion** | 新增,用于表述dubbo版本,如2.7.0 | + + +consumer: + +| Constant Key | Key | remark | +| ------ | ------ | ------ | +| APPLICATION_KEY | application | | +| VERSION_KEY | version | | +| GROUP_KEY | group | | +| DUBBO_VERSION_KEY | dubbo | | +| SPECIFICATION_VERSION_KEY | **specVersion** | 新增,用于表述dubbo版本,如2.7.0 | + +Constant Key 表示来自于类 org.apache.dubbo.common.Constants 的字段。 + +下面介绍几种常用的使用方式。所有的 sample,都可以查看[sample-2.7](https://github.com/dubbo/dubbo-samples/tree/master) + +### 方式1. 配置dubbo.properties + +sample 在 dubbo-samples-simplified-registry/dubbo-samples-simplified-registry-xml 工程下 (跑 sample 前,先跑下ZKClean 进行配置项清理) + +dubbo.properties + +```properties + +dubbo.registry.simplified=true +dubbo.registry.extra-keys=retries,owner +``` + +怎么去验证呢? + +##### provider端验证 + +provider端配置 + +```xml + + + + + + + + +``` + +得到的 zookeeper 的叶子节点的值如下: + +``` +dubbo%3A%2F%2F30.5.124.149%3A20880%2Forg.apache.dubbo.samples.simplified.registry.nosimple.api.DemoService%3F +application%3Dsimplified-registry-xml-provider%26dubbo%3D2.0.2%26group%3Ddubbo-simple%26**owner**%3D +vict%26**retries**%3D7%26**timeout**%3D5300%26timestamp%3D1542594503305%26version%3D1.2.3 +``` + +和上面的**现有功能 sample**进行对比,上面的 sample 中,executes, retries, owner, timeout 四个配置项都进入了注册中心。但是本实例不是: +* 配置了:dubbo.registry.simplified=true, 默认情况下,timeout 在默认的配置项列表,所以还是会进入注册中心; +* 配置了:dubbo.registry.extra-keys=retries,owner , 所以 retries,owner 也会进入注册中心。 + +总结:timeout,retries,owner 进入了注册中心,而 executes 没有进入。 + +consumer 端配置 + +```xml + + + + + + + + + + + +``` + +得到的 zookeeper 的叶子节点的值如下: + +``` +consumer%3A%2F%2F30.5.124.149%2Forg.apache.dubbo.samples.simplified.registry.nosimple.api.DemoService%3F +actives%3D6%26application%3Dsimplified-registry-xml-consumer%26category%3D +consumers%26check%3Dfalse%26dubbo%3D2.0.2%26group%3Ddubbo-simple%26owner%3Dvvv%26version%3D1.2.3 +``` + +* 配置了:dubbo.registry.simplified=true , 默认情况下,application,version,group,dubbo 在默认的配置项列表,所以还是会进入注册中心; + + +### 方式2. 声明spring bean + +sample在dubbo-samples-simplified-registry/dubbo-samples-simplified-registry-annotation 工程下 (跑 sample 前,先跑下ZKClean 进行配置项清理) + +##### Provider配置 + +privide 端 bean 配置: + +```java +// 等同于dubbo.properties配置,用@Bean形式进行配置 +@Bean +public RegistryConfig registryConfig() { + RegistryConfig registryConfig = new RegistryConfig(); + registryConfig.setAddress("zookeeper://127.0.0.1:2181"); + registryConfig.setSimplified(true); + registryConfig.setExtraKeys("retries,owner"); + return registryConfig; +} +``` + +```java +// 暴露服务 +@Service(version = "1.1.8", group = "d-test", executes = 4500, retries = 7, owner = "victanno", timeout = 5300) +public class AnnotationServiceImpl implements AnnotationService { + @Override + public String sayHello(String name) { + System.out.println("async provider received: " + name); + return "annotation: hello, " + name; + } +} +``` + +和上面 sample 中的 dubbo.properties 的效果是一致的。结果如下: +* 默认情况下,timeout 在默认的配置项列表,所以还是会进入注册中心; +* 配置了 retries,owner 作为额外的 key 进入注册中心 , 所以 retries,owner 也会进入注册中心。 + +总结:timeout,retries,owner 进入了注册中心,而 executes 没有进入。 + +##### Consumer配置 + +consumer 端 bean 配置: + +```java +@Bean +public RegistryConfig registryConfig() { + RegistryConfig registryConfig = new RegistryConfig(); + registryConfig.setAddress("zookeeper://127.0.0.1:2181"); + registryConfig.setSimplified(true); + return registryConfig; + } +``` + +消费服务: + +```java +@Component("annotationAction") +public class AnnotationAction { + + @Reference(version = "1.1.8", group = "d-test", owner = "vvvanno", retries = 4, actives = 6, timeout = 4500) + private AnnotationService annotationService; + public String doSayHello(String name) { + return annotationService.sayHello(name); + } +} +``` + +和上面 sample 中 consumer 端的配置是一样的。结果如下: +* 默认情况下,application,version,group,dubbo 在默认的配置项列表,所以还是会进入注册中心。 + +###### 注意: +如果一个应用中既有provider又有consumer,那么配置需要合并成: + +```java +@Bean +public RegistryConfig registryConfig() { + RegistryConfig registryConfig = new RegistryConfig(); + registryConfig.setAddress("zookeeper://127.0.0.1:2181"); + registryConfig.setSimplified(true); + //只对provider生效 + registryConfig.setExtraKeys("retries,owner"); + return registryConfig; +} +``` + +## 后续规划 + +本版本还保留了大量的配置项,接下来的版本中,会逐渐删除所有的配置项。 diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/_index.md new file mode 100755 index 000000000000..9fad36f07ddc --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "线程模型" +linkTitle: "线程模型" +weight: 1 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/consumer.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/consumer.md new file mode 100644 index 000000000000..88f2a0460691 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/consumer.md @@ -0,0 +1,45 @@ +--- +type: docs +title: "消费端线程模型" +linkTitle: "消费端线程模型" +weight: 2 +description: "Dubbo 消费端线程池模型用法" +--- + +2.7.5 版本对整个调用链路做了全面的优化,根据压测结果显示,总体 QPS 性能提升将近 30%,同时也减少了调用过程中的内存分配开销。其中一个值得提及的设计点是 2.7.5 引入了 Servicerepository 的概念,在服务注册阶段提前生成 ServiceDescriptor 和 MethodDescriptor,以减少 RPC 调用阶段计算 Service 原信息带来的资源消耗。 + +## 消费端线程池模型优化 + +对 2.7.5 版本之前的 Dubbo 应用,尤其是一些消费端应用,当面临需要消费大量服务且并发数比较大的大流量场景时(典型如网关类场景),经常会出现消费端线程数分配过多的问题,具体问题讨论可参见 [Need a limited Threadpool in consumer side #2013](https://github.com/apache/dubbo/issues/2013) + +改进后的消费端线程池模型,通过复用业务端被阻塞的线程,很好的解决了这个问题。 + +#### 老的线程池模型 + +![消费端线程池.png](/imgs/user/consumer-threadpool0.png) + +我们重点关注 Consumer 部分: + +1. 业务线程发出请求,拿到一个 Future 实例。 +2. 业务线程紧接着调用 future.get 阻塞等待业务结果返回。 +3. 当业务数据返回后,交由独立的 Consumer 端线程池进行反序列化等处理,并调用 future.set 将反序列化后的业务结果置回。 +4. 业务线程拿到结果直接返回 + + + +**2.7.5 版本引入的线程池模型** + +![消费端线程池新.png](/imgs/user/consumer-threadpool1.png) + +1. 业务线程发出请求,拿到一个 Future 实例。 +2. 在调用 future.get() 之前,先调用 ThreadlessExecutor.wait(),wait 会使业务线程在一个阻塞队列上等待,直到队列中被加入元素。 +3. 当业务数据返回后,生成一个 Runnable Task 并放入 ThreadlessExecutor 队列 +4. 业务线程将 Task 取出并在本线程中执行:反序列化业务数据并 set 到 Future。 +5. 业务线程拿到结果直接返回 + +这样,相比于老的线程池模型,由业务线程自己负责监测并解析返回结果,免去了额外的消费端线程池开销。 + +关于性能优化,在接下来的版本中将会持续推进,主要从以下两个方面入手: + +1. RPC 调用链路。目前能看到的点包括:进一步减少执行链路的内存分配、在保证协议兼容性的前提下提高协议传输效率、提高 Filter、Router 等计算效率。 +2. 服务治理链路。进一步减少地址推送、服务治理规则推送等造成的内存、cpu 资源消耗。 diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/provider.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/provider.md new file mode 100644 index 000000000000..f0398b854925 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/performance/threading-model/provider.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "服务端线程模型" +linkTitle: "服务端线程模型" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/_index.md new file mode 100755 index 000000000000..c63ca0354bba --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "RPC请求类特性" +linkTitle: "RPC请求类特性" +weight: 3 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/accesslog.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/accesslog.md new file mode 100644 index 000000000000..459506362507 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/accesslog.md @@ -0,0 +1,21 @@ +--- +type: docs +title: "调用信息记录" +linkTitle: "调用信息记录" +weight: 13 +description: "配置 Dubbo 的访问日志" +--- + +如果你想记录每一次请求信息,可开启访问日志,类似于apache的访问日志。**注意**:此日志量比较大,请注意磁盘容量。 + +将访问日志输出到当前应用的log4j日志: + +```xml + +``` + +将访问日志输出到指定文件: + +```xml + +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/async-call.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/async-call.md new file mode 100644 index 000000000000..7304ad3b15ce --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/async-call.md @@ -0,0 +1,222 @@ +--- +type: docs +title: "异步调用" +linkTitle: "异步调用" +weight: 3 +description: "在 Dubbo 中发起异步调用" +--- + + +从 2.7.0 开始,Dubbo 的所有异步编程接口开始以 [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) 为基础 + +基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小。 + +![/user-guide/images/future.jpg](/imgs/user/future.jpg) + + +## 使用 CompletableFuture 签名的接口 + +需要服务提供者事先定义 CompletableFuture 签名的服务,接口定义指南如下: + +Provider端异步执行将阻塞的业务从Dubbo内部线程池切换到业务自定义线程,避免Dubbo线程池的过度占用,有助于避免不同服务间的互相影响。异步执行无异于节省资源或提升RPC响应性能,因为如果业务执行需要阻塞,则始终还是要有线程来负责执行。 + +{{% alert title="注意" color="warning" %}} +Provider 端异步执行和 Consumer 端异步调用是相互独立的,你可以任意正交组合两端配置 +- Consumer同步 - Provider同步 +- Consumer异步 - Provider同步 +- Consumer同步 - Provider异步 +- Consumer异步 - Provider异步 + {{% /alert %}} + + +## 定义 CompletableFuture 签名的接口 + +服务接口定义: + +```java +public interface AsyncService { + CompletableFuture sayHello(String name); +} +``` + +服务实现: + +```java +public class AsyncServiceImpl implements AsyncService { + @Override + public CompletableFuture sayHello(String name) { + RpcContext savedContext = RpcContext.getContext(); + // 建议为supplyAsync提供自定义线程池,避免使用JDK公用线程池 + return CompletableFuture.supplyAsync(() -> { + System.out.println(savedContext.getAttachment("consumer-key1")); + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "async response from provider."; + }); + } +} +``` + +通过 `return CompletableFuture.supplyAsync() `,业务执行已从 Dubbo 线程切换到业务线程,避免了对 Dubbo 线程池的阻塞。 + + + +## 使用AsyncContext + +Dubbo 提供了一个类似 Servlet 3.0 的异步接口`AsyncContext`,在没有 CompletableFuture 签名接口的情况下,也可以实现 Provider 端的异步执行。 + +服务接口定义: + +```java +public interface AsyncService { + String sayHello(String name); +} +``` + +服务暴露,和普通服务完全一致: + +```xml + + +``` + +服务实现: + +```java +public class AsyncServiceImpl implements AsyncService { + public String sayHello(String name) { + final AsyncContext asyncContext = RpcContext.startAsync(); + new Thread(() -> { + // 如果要使用上下文,则必须要放在第一句执行 + asyncContext.signalContextSwitch(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + // 写回响应 + asyncContext.write("Hello " + name + ", response from provider."); + }).start(); + return null; + } +} +``` + +注意接口的返回类型是 `CompletableFuture`。 + +XML引用服务: + +```xml + +``` + +调用远程服务: + +```java +// 调用直接返回CompletableFuture +CompletableFuture future = asyncService.sayHello("async call request"); +// 增加回调 +future.whenComplete((v, t) -> { + if (t != null) { + t.printStackTrace(); + } else { + System.out.println("Response: " + v); + } +}); +// 早于结果输出 +System.out.println("Executed before response return."); +``` + +## 使用 RpcContext + +在 consumer.xml 中配置: + +```xml + + + +``` + +调用代码: + +```java +// 此调用会立即返回null +asyncService.sayHello("world"); +// 拿到调用的Future引用,当结果返回后,会被通知和设置到此Future +CompletableFuture helloFuture = RpcContext.getContext().getCompletableFuture(); +// 为Future添加回调 +helloFuture.whenComplete((retValue, exception) -> { + if (exception == null) { + System.out.println(retValue); + } else { + exception.printStackTrace(); + } +}); +``` + +或者,你也可以这样做异步调用: + +```java +CompletableFuture future = RpcContext.getContext().asyncCall( + () -> { + asyncService.sayHello("oneway call request1"); + } +); + +future.get(); +``` + + + +## 重载服务接口 + +如果你只有这样的同步服务定义,而又不喜欢 RpcContext 的异步使用方式。 + +```java +public interface GreetingsService { + String sayHi(String name); +} +``` + +那还有一种方式,就是利用 Java 8 提供的 default 接口实现,重载一个带有 CompletableFuture 签名的方法。 + +> CompletableFuture 签名的方法目前只支持 Dubbo 协议,其他协议由于第三方实现问题,需要视具体情况而定。 + +有两种方式来实现: + +1. 提供方或消费方自己修改接口签名 + +```java +public interface GreetingsService { + String sayHi(String name); + + // AsyncSignal is totally optional, you can use any parameter type as long as java allows your to do that. + default CompletableFuture sayHi(String name, AsyncSignal signal) { + return CompletableFuture.completedFuture(sayHi(name)); + } +} +``` + +1. Dubbo 官方提供 compiler hacker,编译期自动重写同步方法,请[在此](https://github.com/dubbo/dubbo-async-processor#compiler-hacker-processer)讨论和跟进具体进展。 + + + +你也可以设置是否等待消息发出: [^1] + +- `sent="true"` 等待消息发出,消息发送失败将抛出异常。 +- `sent="false"` 不等待消息发出,将消息放入 IO 队列,即刻返回。 + +```xml + +``` + +如果你只是想异步,完全忽略返回值,可以配置 `return="false"`,以减少 Future 对象的创建和管理成本: + +```xml + +``` + +[^1]: 异步总是不等待返回 \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/attachment.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/attachment.md new file mode 100644 index 000000000000..7d337114169a --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/attachment.md @@ -0,0 +1,38 @@ +--- +type: docs +title: "调用链路传递隐式参数" +linkTitle: "调用链路传递隐式参数" +weight: 5 +description: "通过 Dubbo 中的 Attachment 在服务消费方和提供方之间隐式传递参数" +--- + +可以通过 `RpcContext` 上的 `setAttachment` 和 `getAttachment` 在服务消费方和提供方之间进行参数的隐式传递。 + +{{% alert title="注意" color="primary" %}} +path, group, version, dubbo, token, timeout 几个 key 是保留字段,请使用其它值。 +{{% /alert %}} + +![/user-guide/images/context.png](/imgs/user/context.png) + +#### 在服务消费方端设置隐式参数 + +`setAttachment` 设置的 KV 对,在完成下面一次远程调用会被清空,即多次远程调用要多次设置。 + +```xml +RpcContext.getContext().setAttachment("index", "1"); // 隐式传参,后面的远程调用都会隐式将这些参数发送到服务器端,类似cookie,用于框架集成,不建议常规业务使用 +xxxService.xxx(); // 远程调用 +// ... +``` + +#### 在服务提供方端获取隐式参数 + +```java +public class XxxServiceImpl implements XxxService { + + public void xxx() { + // 获取客户端隐式传入的参数,用于框架集成,不建议常规业务使用 + String index = RpcContext.getContext().getAttachment("index"); + } +} +``` + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/callback-parameter.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/callback-parameter.md new file mode 100644 index 000000000000..893aecb0cec4 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/callback-parameter.md @@ -0,0 +1,120 @@ +--- +type: docs +title: "服务端对客户端进行回调" +linkTitle: "服务端对客户端进行回调" +weight: 9 +description: "通过参数回调从服务器端调用客户端逻辑" +--- + +参数回调方式与调用本地 callback 或 listener 相同,只需要在 Spring 的配置文件中声明哪个参数是 callback 类型即可。Dubbo 将基于长连接生成反向代理,这样就可以从服务器端调用客户端逻辑。可以参考 [dubbo 项目中的示例代码](https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-callback)。 + +#### 服务接口示例 + +###### CallbackService.java + +```java +package com.callback; + +public interface CallbackService { + void addListener(String key, CallbackListener listener); +} +``` + +###### CallbackListener.java + +```java +package com.callback; + +public interface CallbackListener { + void changed(String msg); +} +``` + +#### 服务提供者接口实现示例 + +```java +package com.callback.impl; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.callback.CallbackListener; +import com.callback.CallbackService; + +public class CallbackServiceImpl implements CallbackService { + + private final Map listeners = new ConcurrentHashMap(); + + public CallbackServiceImpl() { + Thread t = new Thread(new Runnable() { + public void run() { + while(true) { + try { + for(Map.Entry entry : listeners.entrySet()){ + try { + entry.getValue().changed(getChanged(entry.getKey())); + } catch (Throwable t) { + listeners.remove(entry.getKey()); + } + } + Thread.sleep(5000); // 定时触发变更通知 + } catch (Throwable t) { // 防御容错 + t.printStackTrace(); + } + } + } + }); + t.setDaemon(true); + t.start(); + } + + public void addListener(String key, CallbackListener listener) { + listeners.put(key, listener); + listener.changed(getChanged(key)); // 发送变更通知 + } + + private String getChanged(String key) { + return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); + } +} +``` + +#### 服务提供者配置示例 + +```xml + + + + + + + + +``` + +#### 服务消费者配置示例 + +```xml + +``` + +#### 服务消费者调用示例 + +```java +ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml"); +context.start(); + +CallbackService callbackService = (CallbackService) context.getBean("callbackService"); + +callbackService.addListener("foo.bar", new CallbackListener(){ + public void changed(String msg) { + System.out.println("callback1:" + msg); + } +}); +``` + +{{% alert title="提示" color="primary" %}} +`2.0.6` 及其以上版本支持 +{{% /alert %}} diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/context.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/context.md new file mode 100644 index 000000000000..fff2509bbd63 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/context.md @@ -0,0 +1,46 @@ +--- +type: docs +title: "RPC调用上下文" +linkTitle: "RPC调用上下文" +weight: 6 +description: "通过上下文存放当前调用过程中所需的环境信息" +--- + +上下文中存放的是当前调用过程中所需的环境信息。所有配置信息都将转换为 URL 的参数,参见 [schema 配置参考手册](../../references/xml) 中的**对应URL参数**一列。 + +RpcContext 是一个 ThreadLocal 的临时状态记录器,当接收到 RPC 请求,或发起 RPC 请求时,RpcContext 的状态都会变化。比如:A 调 B,B 再调 C,则 B 机器上,在 B 调 C 之前,RpcContext 记录的是 A 调 B 的信息,在 B 调 C 之后,RpcContext 记录的是 B 调 C 的信息。 + +## 服务消费方 + +```java +// 远程调用 +xxxService.xxx(); +// 本端是否为消费端,这里会返回true +boolean isConsumerSide = RpcContext.getContext().isConsumerSide(); +// 获取最后一次调用的提供方IP地址 +String serverIP = RpcContext.getContext().getRemoteHost(); +// 获取当前服务配置信息,所有配置信息都将转换为URL的参数 +String application = RpcContext.getContext().getUrl().getParameter("application"); +// 注意:每发起RPC调用,上下文状态会变化 +yyyService.yyy(); +``` + +## 服务提供方 + +```java +public class XxxServiceImpl implements XxxService { + + public void xxx() { + // 本端是否为提供端,这里会返回true + boolean isProviderSide = RpcContext.getContext().isProviderSide(); + // 获取调用方IP地址 + String clientIP = RpcContext.getContext().getRemoteHost(); + // 获取当前服务配置信息,所有配置信息都将转换为URL的参数 + String application = RpcContext.getContext().getUrl().getParameter("application"); + // 注意:每发起RPC调用,上下文状态会变化 + yyyService.yyy(); + // 此时本端变成消费端,这里会返回false + boolean isProviderSide = RpcContext.getContext().isProviderSide(); + } +} +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/echo-service.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/echo-service.md new file mode 100644 index 000000000000..f9fef53179f2 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/echo-service.md @@ -0,0 +1,29 @@ +--- +type: docs +title: "回声测试" +linkTitle: "回声测试" +weight: 12 +description: "通过回声测试检测 Dubbo 服务是否可用" +--- + +回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否通畅,可用于监控。 + +所有服务自动实现 `EchoService` 接口,只需将任意服务引用强制转型为 `EchoService`,即可使用。 + +Spring 配置: +```xml + +``` + +代码: +```java +// 远程服务引用 +MemberService memberService = ctx.getBean("memberService"); + +EchoService echoService = (EchoService) memberService; // 强制转型为EchoService + +// 回声测试可用性 +String status = echoService.$echo("OK"); + +assert(status.equals("OK")); +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/events-notify.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/events-notify.md new file mode 100644 index 000000000000..45f3b0b2b402 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/events-notify.md @@ -0,0 +1,108 @@ +--- +type: docs +title: "调用触发事件通知" +linkTitle: "调用触发事件通知" +weight: 8 +description: "在调用之前、调用之后、出现异常时的事件通知" +--- + +在调用之前、调用之后、出现异常时,会触发 `oninvoke`、`onreturn`、`onthrow` 三个事件,可以配置当事件发生时,通知哪个类的哪个方法。 + +{{% alert title="提示" color="primary" %}} +支持版本:`2.0.7` 之后 +{{% /alert %}} + +#### 服务提供者与消费者共享服务接口 + +```java +interface IDemoService { + public Person get(int id); +} +``` + +#### 服务提供者实现 + +```java +class NormalDemoService implements IDemoService { + public Person get(int id) { + return new Person(id, "charles`son", 4); + } +} +``` + +#### 服务提供者配置 + +```xml + + + + +``` + +#### 服务消费者 Callback 接口 + +```java +interface Notify { + public void onreturn(Person msg, Integer id); + public void onthrow(Throwable ex, Integer id); +} +``` + +#### 服务消费者 Callback 实现 + +```java +class NotifyImpl implements Notify { + public Map ret = new HashMap(); + public Map errors = new HashMap(); + + public void onreturn(Person msg, Integer id) { + System.out.println("onreturn:" + msg); + ret.put(id, msg); + } + + public void onthrow(Throwable ex, Integer id) { + errors.put(id, ex); + } +} +``` + +#### 服务消费者 Callback 配置 + +```xml + + + + +``` + +`callback` 与 `async` 功能正交分解,`async=true` 表示结果是否马上返回,`onreturn` 表示是否需要回调。 + +两者叠加存在以下几种组合情况: + +* 异步回调模式:`async=true onreturn="xxx"` +* 同步回调模式:`async=false onreturn="xxx"` +* 异步无回调 :`async=true` +* 同步无回调 :`async=false` + +{{% alert title="提示" color="primary" %}} +`async=false` 默认 +{{% /alert %}} + +#### 测试代码 + +```java +IDemoService demoService = (IDemoService) context.getBean("demoService"); +NotifyImpl notify = (NotifyImpl) context.getBean("demoCallback"); +int requestId = 2; +Person ret = demoService.get(requestId); +Assert.assertEquals(null, ret); +//for Test:只是用来说明callback正常被调用,业务具体实现自行决定. +for (int i = 0; i < 10; i++) { + if (!notify.ret.containsKey(requestId)) { + Thread.sleep(200); + } else { + break; + } +} +Assert.assertEquals(requestId, notify.ret.get(requestId).getId()); +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/generic-service.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/generic-service.md new file mode 100644 index 000000000000..5276c4429ec6 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/generic-service.md @@ -0,0 +1,151 @@ +--- +type: docs +title: "泛化调用" +linkTitle: "泛化调用" +weight: 4 +description: "实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求" +--- + +# 实现泛化服务暴露 + +泛接口实现方式主要用于服务器端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的远程服务 Mock 框架,可通过实现 GenericService 接口处理所有服务请求。 + +在 Java 代码中实现 `GenericService` 接口: + +```java +package com.foo; +public class MyGenericService implements GenericService { + + public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException { + if ("sayHello".equals(methodName)) { + return "Welcome " + args[0]; + } + } +} +``` + +## 通过 Spring 暴露泛化实现 + +在 Spring 配置申明服务的实现: + +```xml + + +``` + +## 通过 API 方式暴露泛化实现 + +```java +... +// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口实现 +GenericService xxxService = new XxxGenericService(); + +// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 +ServiceConfig service = new ServiceConfig(); +// 弱类型接口名 +service.setInterface("com.xxx.XxxService"); +service.setVersion("1.0.0"); +// 指向一个通用服务实现 +service.setRef(xxxService); + +// 暴露及注册服务 +service.export(); +``` + +# 实现泛化服务调用 + +泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 `Map` 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 `GenericService` 调用所有服务实现。 + +## 通过 Spring 使用泛化调用 + +在 Spring 配置申明 `generic="true"`: + +```xml + +``` + +在 Java 代码获取 barService 并开始泛化调用: + +```java +GenericService barService = (GenericService) applicationContext.getBean("barService"); +Object result = barService.$invoke("sayHello", new String[] { "java.lang.String" }, new Object[] { "World" }); +``` + +## 通过 API 方式使用泛化调用 + +```java +import org.apache.dubbo.rpc.service.GenericService; +... + +// 引用远程服务 +// 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存 +ReferenceConfig reference = new ReferenceConfig(); +// 弱类型接口名 +reference.setInterface("com.xxx.XxxService"); +reference.setVersion("1.0.0"); +// 声明为泛化接口 +reference.setGeneric(true); + +// 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用 +GenericService genericService = reference.get(); + +// 基本类型以及Date,List,Map等不需要转换,直接调用 +Object result = genericService.$invoke("sayHello", new String[] {"java.lang.String"}, new Object[] {"world"}); + +// 用Map表示POJO参数,如果返回值为POJO也将自动转成Map +Map person = new HashMap(); +person.put("name", "xxx"); +person.put("password", "yyy"); +// 如果返回POJO将自动转成Map +Object result = genericService.$invoke("findPerson", new String[] +{"com.xxx.Person"}, new Object[]{person}); + +... +``` + +## 有关泛化类型的进一步解释 + +假设存在 POJO 如: + +```java +package com.xxx; + +public class PersonImpl implements Person { + private String name; + private String password; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} +``` + +则 POJO 数据: + +```java +Person person = new PersonImpl(); +person.setName("xxx"); +person.setPassword("yyy"); +``` + +可用下面 Map 表示: + +```java +Map map = new HashMap(); +// 注意:如果参数类型是接口,或者List等丢失泛型,可通过class属性指定类型。 +map.put("class", "com.xxx.PersonImpl"); +map.put("name", "xxx"); +map.put("password", "yyy"); +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/group-merger.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/group-merger.md new file mode 100644 index 000000000000..22bba6d35488 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/group-merger.md @@ -0,0 +1,67 @@ +--- +type: docs +title: "分组聚合" +linkTitle: "分组聚合" +weight: 1 +description: "通过分组对结果进行聚合并返回聚合后的结果" +--- + +通过分组对结果进行聚合并返回聚合后的结果,比如菜单服务,用group区分同一接口的多种实现,现在消费方需从每种group中调用一次并返回结果,对结果进行合并之后返回,这样就可以实现聚合菜单项。 + +相关代码可以参考 [dubbo 项目中的示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-merge) + +## 配置 + +搜索所有分组 + +```xml + +``` + +合并指定分组 + +```xml + +``` + +指定方法合并结果,其它未指定的方法,将只调用一个 Group + +```xml + + + +``` + +某个方法不合并结果,其它都合并结果 + +```xml + + + +``` + +指定合并策略,缺省根据返回值类型自动匹配,如果同一类型有两个合并器时,需指定合并器的名称 + +{{% alert title="提示" color="primary" %}} +参见:[合并结果扩展](../../../dev/impls/merger) +{{% /alert %}} + +```xml + + + +``` + +指定合并方法,将调用返回结果的指定方法进行合并,合并方法的参数类型必须是返回结果类型本身 + +```xml + + + +``` + + +{{% alert title="提示" color="primary" %}} +从 `2.1.0` 版本开始支持 +{{% /alert %}} + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-mock.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-mock.md new file mode 100644 index 000000000000..d14a446f51bc --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-mock.md @@ -0,0 +1,106 @@ +--- +type: docs +title: "本地伪装" +linkTitle: "本地伪装" +weight: 10 +description: "如何在 Dubbo 中利用本地伪装实现服务降级" +--- + +本地伪装 [^1] 通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败。 + +在 spring 配置文件中按以下方式配置: + +```xml + +``` + +或 + +```xml + +``` + +在工程中提供 Mock 实现 [^2]: + +```java +package com.foo; +public class BarServiceMock implements BarService { + public String sayHello(String name) { + // 你可以伪造容错数据,此方法只在出现RpcException时被执行 + return "容错数据"; + } +} +``` + +如果服务的消费方经常需要 try-catch 捕获异常,如: + +```java +Offer offer = null; +try { + offer = offerService.findOffer(offerId); +} catch (RpcException e) { + logger.error(e); +} +``` + +请考虑改为 Mock 实现,并在 Mock 实现中 return null。如果只是想简单的忽略异常,在 `2.0.11` 以上版本可用: + +```xml + +``` + +## 进阶用法 + +### return + +使用 `return` 来返回一个字符串表示的对象,作为 Mock 的返回值。合法的字符串可以是: +* *empty*: 代表空,基本类型的默认值,或者集合类的空值 +* *null*: `null` +* *true*: `true` +* *false*: `false` +* *JSON 格式*: 反序列化 JSON 所得到的对象 + +### throw + +使用 `throw` 来返回一个 Exception 对象,作为 Mock 的返回值。 + +当调用出错时,抛出一个默认的 RPCException: + +```xml + +``` + +当调用出错时,抛出指定的 Exception: + +```xml + +``` + +### force 和 fail + +在 `2.6.6` 以上的版本,可以开始在 Spring XML 配置文件中使用 `fail:` 和 `force:`。`force:` 代表强制使用 Mock 行为,在这种情况下不会走远程调用。`fail:` 与默认行为一致,只有当远程调用发生错误时才使用 Mock 行为。`force:` 和 `fail:` 都支持与 `throw` 或者 `return` 组合使用。 + +强制返回指定值: + +```xml + +``` + +强制抛出指定异常: + +```xml + +``` + +### 在方法级别配置 Mock + +Mock 可以在方法级别上指定,假定 `com.foo.BarService` 上有好几个方法,我们可以单独为 `sayHello()` 方法指定 Mock 行为。具体配置如下所示,在本例中,只要 `sayHello()` 被调用到时,强制返回 "fake": + +```xml + + + +``` + +[^1]: Mock 是 Stub 的一个子集,便于服务提供方在客户端执行容错逻辑,因经常需要在出现 RpcException (比如网络失败,超时等)时进行容错,而在出现业务异常(比如登录用户名密码错误)时不需要容错,如果用 Stub,可能就需要捕获并依赖 RpcException 类,而用 Mock 就可以不依赖 RpcException,因为它的约定就是只有出现 RpcException 时才执行。 +[^2]: 在 interface 旁放一个 Mock 实现,它实现 BarService 接口,并有一个无参构造函数 diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-stub.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-stub.md new file mode 100644 index 000000000000..71989d6593e0 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/local-stub.md @@ -0,0 +1,50 @@ +--- +type: docs +title: "本地存根" +linkTitle: "本地存根" +weight: 11 +description: "在 Dubbo 中利用本地存根在客户端执行部分逻辑" +--- + +远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做 ThreadLocal 缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在 API 中带上 Stub,客户端生成 Proxy 实例,会把 Proxy 通过构造函数传给 Stub [^1],然后把 Stub 暴露给用户,Stub 可以决定要不要去调 Proxy。 + +![/user-guide/images/stub.jpg](/imgs/user/stub.jpg) + +在 spring 配置文件中按以下方式配置: + +```xml + +``` + +或 + +```xml + +``` + +提供 Stub 的实现 [^2]: + +```java +package com.foo; +public class BarServiceStub implements BarService { + private final BarService barService; + + // 构造函数传入真正的远程代理对象 + public BarServiceStub(BarService barService){ + this.barService = barService; + } + + public String sayHello(String name) { + // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等 + try { + return barService.sayHello(name); + } catch (Exception e) { + // 你可以容错,可以做任何AOP拦截事项 + return "容错数据"; + } + } +} +``` + +[^1]: Stub 必须有可传入 Proxy 的构造函数。 +[^2]: 在 interface 旁边放一个 Stub 实现,它实现 BarService 接口,并有一个传入远程 BarService 实例的构造函数 diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/parameter-validation.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/parameter-validation.md new file mode 100644 index 000000000000..bdd45fae00f8 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/parameter-validation.md @@ -0,0 +1,197 @@ +--- +type: docs +title: "参数校验" +linkTitle: "参数校验" +weight: 2 +description: "在 Dubbo 中进行参数校验" +--- + +参数验证功能是基于 [JSR303](https://jcp.org/en/jsr/detail?id=303) 实现的,用户只需标识 JSR303 标准的验证 annotation,并通过声明 filter 来实现验证。 + +## Maven 依赖 + +```xml + + javax.validation + validation-api + 1.0.0.GA + + + org.hibernate + hibernate-validator + 4.2.0.Final + +``` + +## 示例 + +### 参数标注示例 + +```java +import java.io.Serializable; +import java.util.Date; + +import javax.validation.constraints.Future; +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Past; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +public class ValidationParameter implements Serializable { + private static final long serialVersionUID = 7158911668568000392L; + + @NotNull // 不允许为空 + @Size(min = 1, max = 20) // 长度或大小范围 + private String name; + + @NotNull(groups = ValidationService.Save.class) // 保存时不允许为空,更新时允许为空 ,表示不更新该字段 + @Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$") + private String email; + + @Min(18) // 最小值 + @Max(100) // 最大值 + private int age; + + @Past // 必须为一个过去的时间 + private Date loginDate; + + @Future // 必须为一个未来的时间 + private Date expiryDate; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Date getLoginDate() { + return loginDate; + } + + public void setLoginDate(Date loginDate) { + this.loginDate = loginDate; + } + + public Date getExpiryDate() { + return expiryDate; + } + + public void setExpiryDate(Date expiryDate) { + this.expiryDate = expiryDate; + } +} +``` + +### 分组验证示例 + +```java +public interface ValidationService { // 缺省可按服务接口区分验证场景,如:@NotNull(groups = ValidationService.class) + @interface Save{} // 与方法同名接口,首字母大写,用于区分验证场景,如:@NotNull(groups = ValidationService.Save.class),可选 + void save(ValidationParameter parameter); + void update(ValidationParameter parameter); +} +``` + +### 关联验证示例 + +```java +import javax.validation.GroupSequence; + +public interface ValidationService { + @GroupSequence(Update.class) // 同时验证Update组规则 + @interface Save{} + void save(ValidationParameter parameter); + + @interface Update{} + void update(ValidationParameter parameter); +} +``` + +### 参数验证示例 + +```java +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; + +public interface ValidationService { + void save(@NotNull ValidationParameter parameter); // 验证参数不为空 + void delete(@Min(1) int id); // 直接对基本类型参数验证 +} +``` + +## 配置 + +### 在客户端验证参数 + +```xml + +``` + +### 在服务器端验证参数 + +```xml + +``` + +{{% alert title="提示" color="primary" %}} +Dubbo 默认支持 hibernate-validator 版本 <=6.x,若使用 hibernate-validator 7.x 版本,请将 validation 参数声明为 jvalidatorNew +{{% /alert %}} + +## 验证异常信息 + +```java +import javax.validation.ConstraintViolationException; +import javax.validation.ConstraintViolationException; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import org.apache.dubbo.examples.validation.api.ValidationParameter; +import org.apache.dubbo.examples.validation.api.ValidationService; +import org.apache.dubbo.rpc.RpcException; + +public class ValidationConsumer { + public static void main(String[] args) throws Exception { + String config = ValidationConsumer.class.getPackage().getName().replace('.', '/') + "/validation-consumer.xml"; + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(config); + context.start(); + ValidationService validationService = (ValidationService)context.getBean("validationService"); + // Error + try { + parameter = new ValidationParameter(); + validationService.save(parameter); + System.out.println("Validation ERROR"); + } catch (RpcException e) { // 抛出的是RpcException + ConstraintViolationException ve = (ConstraintViolationException) e.getCause(); // 里面嵌了一个ConstraintViolationException + Set> violations = ve.getConstraintViolations(); // 可以拿到一个验证错误详细信息的集合 + System.out.println(violations); + } + } +} +``` + +{{% alert title="提示" color="primary" %}} +自 `2.1.0` 版本开始支持, 如何使用可以参考 [dubbo 项目中的示例代码](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-validation) + +验证方式可扩展,扩展方式参见开发者手册中的[验证扩展](../../references/spis/validation) +{{% /alert %}} + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/result-cache.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/result-cache.md new file mode 100644 index 000000000000..4a89565f2fc0 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/rpc/result-cache.md @@ -0,0 +1,38 @@ +--- +type: docs +title: "调用结果缓存" +linkTitle: "调用结果缓存" +weight: 7 +description: "通过缓存结果加速访问速度" +--- + +结果缓存,用于加速热门数据的访问速度,Dubbo 提供声明式缓存,以减少用户加缓存的工作量。 + +## 缓存类型 + +* `lru` 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。 +* `threadlocal` 当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问。 +* `jcache` 与 [JSR107](http://jcp.org/en/jsr/detail?id=107%27) 集成,可以桥接各种缓存实现。 + +缓存类型可扩展,参见:[缓存扩展](../../references/spis/cache) + +## 配置 + +```xml + +``` + +或: + +```xml + + + +``` + +{{% alert title="提示" color="primary" %}} +`2.1.0` 以上版本支持。 + +[示例代码](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-cache) +{{% /alert %}} + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/_index.md new file mode 100755 index 000000000000..1dda223a30b1 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "安全类特性" +linkTitle: "安全类特性" +weight: 4 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/tls.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/tls.md new file mode 100644 index 000000000000..77f6c2471ce0 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/tls.md @@ -0,0 +1,51 @@ +--- +type: docs +title: "TLS支持" +linkTitle: "TLS支持" +weight: 1 +description: "通过 TLS 保证传输安全" +--- + +2.7.5 版本在传输链路的安全性上做了很多工作,对于内置的 Dubbo Netty Server 和新引入的 gRPC 协议都提供了基于 TLS 的安全链路传输机制。 + +TLS 的配置都有统一的入口,如下所示: + +##### Provider 端 + +```java +SslConfig sslConfig = new SslConfig(); +sslConfig.setServerKeyCertChainPath("path to cert"); +sslConfig.setServerPrivateKeyPath(args[1]); +// 如果开启双向 cert 认证 +if (mutualTls) { + sslConfig.setServerTrustCertCollectionPath(args[2]); +} + +ProtocolConfig protocolConfig = new ProtocolConfig("dubbo/grpc"); +protocolConfig.setSslEnabled(true); +``` + + + +##### Consumer 端 + +```java +if (!mutualTls) {} + sslConfig.setClientTrustCertCollectionPath(args[0]); +} else { + sslConfig.setClientTrustCertCollectionPath(args[0]); + sslConfig.setClientKeyCertChainPath(args[1]); + sslConfig.setClientPrivateKeyPath(args[2]); +} +``` + +为尽可能保证应用启动的灵活性,TLS Cert 的指定还能通过 -D 参数或环境变量等方式来在启动阶段根据部署环境动态指定,具体请参见 Dubbo 配置读取规则与 TLS 示例 + +{{% alert title="提示" color="primary" %}} +参考 Dubbo [配置读取规则](../../configuration/configuration-load-process),TLS [示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-ssl) + +如果要使用的是 gRPC 协议,在开启 TLS 时会使用到协议协商机制,因此必须使用支持 ALPN 机制的 Provider,推荐使用的是 netty-tcnative,具体可参见 gRPC Java 社区的[总结]( https://github.com/grpc/grpc-java/blob/master/SECURITY.md) +{{% /alert %}} + + +在服务调用的安全性上,Dubbo 在后续的版本中会持续投入,其中服务发现/调用的鉴权机制预计在接下来的版本中就会和大家见面。 diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/token-authorization.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/token-authorization.md new file mode 100644 index 000000000000..6e28da88aa17 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/security/token-authorization.md @@ -0,0 +1,37 @@ +--- +type: docs +title: "权限控制" +linkTitle: "权限控制" +weight: 2 +description: "通过令牌验证在注册中心控制权限" +--- + +通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者 + +![/user-guide/images/dubbo-token.jpg](/imgs/user/dubbo-token.jpg) + +可以全局设置开启令牌验证: + +```xml + + +``` +或 + +```xml + + +``` + +也可在服务级别设置: + +```xml + + +``` +或 + +```xml + + +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/_index.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/_index.md new file mode 100755 index 000000000000..753a77186cd1 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "服务类特性" +linkTitle: "服务类特性" +weight: 1 +--- + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/multi-versions.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/multi-versions.md new file mode 100644 index 000000000000..551a7be541c1 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/multi-versions.md @@ -0,0 +1,49 @@ +--- +type: docs +title: "服务分版本" +linkTitle: "服务分版本" +weight: 1 +description: "在 Dubbo 中为同一个服务配置多个版本" +--- + +当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。 + +可以按照以下的步骤进行版本迁移: + +1. 在低压力时间段,先升级一半提供者为新版本 +2. 再将所有消费者升级为新版本 +3. 然后将剩下的一半提供者升级为新版本 + +老版本服务提供者配置: + +```xml + +``` + +新版本服务提供者配置: + +```xml + +``` + +老版本服务消费者配置: + +```xml + +``` + +新版本服务消费者配置: + +```xml + +``` + +如果不需要区分版本,可以按照以下的方式配置 [^1]: + +{{% alert title="提示" color="primary" %}} +`2.2.0` 以上版本支持 +{{% /alert %}} + +```xml + +``` diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-downgrade.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-downgrade.md new file mode 100644 index 000000000000..07790c44c425 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-downgrade.md @@ -0,0 +1,27 @@ +--- +type: docs +title: "服务降级" +linkTitle: "服务降级" +weight: 3 +description: "降级 Dubbo 服务" +--- + +可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。 + +向注册中心写入动态配置覆盖规则: + +```java +RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); +Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181")); +registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null")); +``` + +其中: + +* `mock=force:return+null` 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。 +* 还可以改为 `mock=fail:return+null` 表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。 + +{{% alert title="提示" color="primary" %}} +`2.2.0` 以上版本支持 +{{% /alert %}} + diff --git a/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-group.md b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-group.md new file mode 100644 index 000000000000..18a60fd89785 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/advanced-features-and-usage/service/service-group.md @@ -0,0 +1,33 @@ +--- +type: docs +title: "服务分组" +linkTitle: "服务分组" +weight: 2 +description: "使用服务分组区分服务接口的不同实现" +--- + +当一个接口有多种实现时,可以用 group 区分。 + +## 服务 + +```xml + + +``` + +## 引用 + +```xml + + +``` + +任意组: + +```xml + +``` + +{{% alert title="提示" color="primary" %}} +`2.2.0` 以上版本支持,总是只调一个可用组的实现 +{{% /alert %}} diff --git a/content/zh/docs3-building/java-sdk/concepts-and-architecture/_index.md b/content/zh/docs3-building/java-sdk/concepts-and-architecture/_index.md new file mode 100755 index 000000000000..25376686d78f --- /dev/null +++ b/content/zh/docs3-building/java-sdk/concepts-and-architecture/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "概念和架构" +linkTitle: "概念和架构" +weight: 3 +--- + diff --git a/content/zh/docs3-building/java-sdk/concepts-and-architecture/overall-architecture.md b/content/zh/docs3-building/java-sdk/concepts-and-architecture/overall-architecture.md new file mode 100644 index 000000000000..540ed7d024ac --- /dev/null +++ b/content/zh/docs3-building/java-sdk/concepts-and-architecture/overall-architecture.md @@ -0,0 +1,11 @@ +--- +type: docs +title: "总体架构" +linkTitle: "总体架构" +weight: 1 +--- + +### 总体架构图 + + +### 消费端调用原理(选址等) diff --git a/content/zh/docs3-building/java-sdk/concepts-and-architecture/service-discovery.md b/content/zh/docs3-building/java-sdk/concepts-and-architecture/service-discovery.md new file mode 100644 index 000000000000..4af2a11e3944 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/concepts-and-architecture/service-discovery.md @@ -0,0 +1,71 @@ +--- +type: docs +title: "服务发现" +linkTitle: "服务发现" +weight: 2 +--- +服务发现,即消费端自动发现服务地址列表的能力,是微服务框架需要具备的关键能力,借助于自动化的服务发现,微服务之间可以在无需感知对端部署位置与 IP 地址的情况下实现通信。 + +实现服务发现的方式有很多种,Dubbo 提供的是一种 Client-Based 的服务发现机制,通常还需要部署额外的第三方注册中心组件来协调服务发现过程,如常用的 Nacos、Consul、Zookeeper 等,Dubbo 自身也提供了对多种注册中心组件的对接,用户可以灵活选择。 + +Dubbo 基于消费端的自动服务发现能力,其基本工作原理如下图: + +![//imgs/architecture.png](/imgs/architecture.png) + +服务发现的一个核心组件是注册中心,Provider 注册地址到注册中心,Consumer 从注册中心读取和订阅 Provider 地址列表。 +因此,要启用服务发现,需要为 Dubbo 增加注册中心配置: + +以 dubbo-spring-boot-starter 使用方式为例,增加 registry 配置 + +```properties +# application.properties +dubbo + registry + address: zookeeper://127.0.0.1:2181 +``` + +## What's New in Dubbo3 +就使用方式上而言,Dubbo3 与 Dubbo2 的服务发现配置是完全一致的,不需要改动什么内容。但就实现原理上而言,Dubbo3 引入了全新的服务发现模型 - 应用级服务发现, +在工作原理、数据格式上已完全不能兼容老版本服务发现。 + +* Dubbo3 应用级服务发现,以应用粒度组织地址数据 +* Dubbo2 接口级服务发现,以接口粒度组织地址数据 + +Dubbo3 格式的 Provider 地址不能被 Dubbo2 的 Consumer 识别到,反之 Dubbo2 的消费者也不能订阅到 Dubbo3 Provider。 +* 对于新用户,我们提倡直接启用 Dubbo3 的默认行为,即启用应用级服务发现,参见《[应用级服务发现](../../examples/service-discovery)》; +* 对于老用户,要面临如何平滑迁移到应用级服务发现的问题,考虑到老用户的规模如此之大,Dubbo3 默认保持了接口级地址发现的行为,这保证了老用户可以直接无感升级到 Dubbo3。 +而如果要开启应用级服务发现,则需要通过配置显示开启(双注册、双订阅),具体开启与平滑迁移过程,可参见《[地址发现迁移指南](../../migration/migration-service-discovery)》。 + +## 应用级服务发现简介 +概括来说,Dubbo3 引入的应用级服务发现主要有以下优势 +* 适配云原生微服务变革。云原生时代的基础设施能力不断向上释放,像 Kubernetes 等平台都集成了微服务概念抽象,Dubbo3 的应用级服务发现是适配各种微服务体系的通用模型。 +* 提升性能与可伸缩性。支持超大规模集群的服务治理一直以来都是 Dubbo 的优势,通过引入应用级服务发现模型,从本质上解决了注册中心地址数据的存储与推送压力,相应的 Consumer 侧的地址计算压力也成数量级下降;集群规模也开始变得可预测、可评估(与 RPC 接口数量无关,只与实例部署规模相关)。 + +下图是 Dubbo2 的服务发现模型:Provider 注册服务地址,Consumer 经过注册中心协调并发现服务地址,进而对地址发起通信,这是被绝大多数微服务框架的经典服务发现流程。而 Dubbo2 的特殊之处在于,它把 “RPC 接口”的信息也融合在了地址发现过程中,而这部分信息往往是和具体的业务定义密切相关的。 + +![//imgs/v3/concepts/servicediscovery_old.png](/imgs/v3/concepts/servicediscovery_old.png) + +而在接入云原生基础设施后,基础设施融入了微服务概念的抽象,容器化微服务被编排、调度的过程即完成了在基础设施层面的注册。如下图所示,基础设施既承担了注册中心的职责,又完成了服务注册的动作,而 “RPC 接口”这部分信息,由于与具体的业务相关,不可能也不适合被基础设施托管。 + +![//imgs/v3/concepts/servicediscovery_k8s.png](/imgs/v3/concepts/servicediscovery_k8s.png) + +在这样的场景下,对 Dubbo3 的服务注册发现机制提出了两个要求: +Dubbo3 需要在原有服务发现流程中抽象出通用的、与业务逻辑无关的地址映射模型,并确保这部分模型足够合理,以支持将地址的注册行为和存储委托给下层基础设施 +Dubbo3 特有的业务接口同步机制,是 Dubbo3 需要保留的优势,需要在 Dubbo3 中定义的新地址模型之上,通过框架内的自有机制予以解决。 + +这样设计的全新的服务发现模型,在架构兼容性、可伸缩性上都给 Dubbo3 带来了更大的优势。 + +![//imgs/v3/concepts/servicediscovery_mem.png](/imgs/v3/concepts/servicediscovery_mem.png) + +在架构兼容性上,如上文所述,Dubbo3 复用下层基础设施的服务抽象能力成为了可能;另一方面,如 Spring Cloud 等业界其它微服务解决方案也沿用这种模型, +在打通了地址发现之后,使得用户探索用 Dubbo 连接异构的微服务体系成为了一种可能。 + +Dubbo3 服务发现模型更适合构建可伸缩的服务体系,这点要如何理解? +这里先举个简单的例子,来直观的对比 Dubbo2 与 Dubbo3 在地址发现流程上的数据流量变化:假设一个微服务应用定义了 100 个接口(Dubbo 中的服务), +则需要往注册中心中注册 100 个服务,如果这个应用被部署在了 100 台机器上,那这 100 个服务总共会产生 100 * 100 = 10000 个虚拟节点;而同样的应用, +对于 Dubbo3 来说,新的注册发现模型只需要 1 个服务(只和应用有关和接口无关), 只注册和机器实例数相等的 1 * 100 = 100 个虚拟节点到注册中心。 +在这个简单的示例中,Dubbo 所注册的地址数量下降到了原来的 1 / 100,对于注册中心、订阅方的存储压力都是一个极大的释放。更重要的是, +地址发现容量彻底与业务 RPC 定义解耦开来,整个集群的容量评估对运维来说将变得更加透明:部署多少台机器就会有多大负载,不会像 Dubbo2 一样, +因为业务 RPC 重构就会影响到整个集群服务发现的稳定性。 + +请通过以下 blog 了解更多应用级服务发现设计原则细节。 diff --git a/content/zh/docs3-building/java-sdk/concepts-and-architecture/service-invocation.md b/content/zh/docs3-building/java-sdk/concepts-and-architecture/service-invocation.md new file mode 100644 index 000000000000..9426206f6b76 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/concepts-and-architecture/service-invocation.md @@ -0,0 +1,13 @@ +--- +type: docs +title: "服务调用" +linkTitle: "服务调用" +weight: 3 +--- + + +Filter + +Router + +LoadBalance \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/contributing/_index.md b/content/zh/docs3-building/java-sdk/contributing/_index.md new file mode 100755 index 000000000000..24ba35940904 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/contributing/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "参与贡献" +linkTitle: "参与贡献" +weight: 7 +--- + diff --git a/content/zh/docs3-building/java-sdk/contributing/guide.md b/content/zh/docs3-building/java-sdk/contributing/guide.md new file mode 100644 index 000000000000..16c6cd0db6d7 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/contributing/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "贡献指南" +linkTitle: "贡献指南" +weight: 2 +--- diff --git a/content/zh/docs3-building/java-sdk/contributing/overview.md b/content/zh/docs3-building/java-sdk/contributing/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/contributing/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/quick-start/_index.md b/content/zh/docs3-building/java-sdk/quick-start/_index.md new file mode 100755 index 000000000000..d4895cc73e55 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/quick-start/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "快速入门" +linkTitle: "快速入门" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/_index.md new file mode 100755 index 000000000000..aabb12962423 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "参考手册" +linkTitle: "参考手册" +weight: 5 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/_index.md new file mode 100644 index 000000000000..3d139af023ab --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "配置中心参考手册" +linkTitle: "配置中心参考手册" +weight: 7 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/_index.md new file mode 100644 index 000000000000..fa239c2de7ec --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Apollo" +linkTitle: "Apollo" +weight: 4 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/guide.md new file mode 100644 index 000000000000..13dc994973dd --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/apollo/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/_index.md new file mode 100644 index 000000000000..7fed8cf789af --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Nacos" +linkTitle: "Nacos" +weight: 3 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/guide.md new file mode 100644 index 000000000000..13dc994973dd --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/nacos/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/overview/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/overview/_index.md new file mode 100644 index 000000000000..40ea6666d928 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/overview/_index.md @@ -0,0 +1,79 @@ +--- +type: docs +title: "配置中心概述" +linkTitle: "配置中心概述" +weight: 1 +--- + +配置中心在 Dubbo 中承担3个职责: + +1. 外部化配置:启动配置的集中式存储 (简单理解为 dubbo.properties 的外部化存储)。 +2. 服务治理:服务治理规则的存储与通知。 +3. 动态配置:控制动态开关或者动态变更属性值 + +启用动态配置,以 Zookeeper 为例,可查看 [配置中心属性详解](../../references/xml/dubbo-config-center) + +```xml + +``` + +或者 + +```properties +dubbo.config-center.address=zookeeper://127.0.0.1:2181 +``` + +或者 + +```java +ConfigCenterConfig configCenter = new ConfigCenterConfig(); +configCenter.setAddress("zookeeper://127.0.0.1:2181"); +``` + +> 为了兼容 2.6.x 版本配置,在使用 Zookeeper 作为注册中心,且没有显示配置配置中心的情况下,Dubbo 框架会默认将此 Zookeeper 用作配置中心,但将只作服务治理用途。 + +## 外部化配置 +请参考文档 [外部化配置](../configuration/external-config) + +## 动态配置 +[TODO 待完善] + +## 服务治理 + +#### Zookeeper + +默认节点结构: + +![zk-configcenter-governance](/imgs/user/zk-configcenter-governance.jpg) + +- namespace,用于不同配置的环境隔离。 +- config,Dubbo 约定的固定节点,不可更改,所有配置和服务治理规则都存储在此节点下。 +- dubbo,所有服务治理规则都是全局性的,dubbo 为默认节点 +- configurators/tag-router/condition-router/migration,不同的服务治理规则类型,node value 存储具体规则内容 + +#### Apollo + +所有的服务治理规则都是全局性的,默认从公共命名空间 `dubbo` 读取和订阅: + +![apollo-configcenter-governance.jpg](/imgs/user/apollo-configcenter-governance.jpg) + +不同的规则以不同的 key 后缀区分: + +- configurators,[覆盖规则](../../examples/config-rule) +- tag-router,[标签路由](../../examples/routing-rule) +- condition-router,[条件路由](../../examples/condition-router) +- migration, [迁移规则](../../examples/todo) + +#### Nacos + +所有的服务治理规则都是全局的,默认从 namespace: `public` 下进行读取, 通过 dataId: `interface name` 以及 group: `dubbo` 去读取和订阅: + +![nacos-configcenter-governance.jpg](/imgs/user/nacos-configcenter-governance.png) + +不同的规则以 dataId 的后缀区分: + +- configurators,[覆盖规则](../../examples/config-rule) +- tag-router,[标签路由](../../examples/routing-rule) +- condition-router,[条件路由](../../examples/condition-router) +- migration, [迁移规则](../../examples/todo) + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/_index.md new file mode 100644 index 000000000000..2864a6f0b145 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Zookeeper" +linkTitle: "Zookeeper" +weight: 2 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/guide.md new file mode 100644 index 000000000000..37430b92141b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/guide.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config-center/zookeeper/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/_index.md new file mode 100644 index 000000000000..2cb876ce3003 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "配置说明手册" +linkTitle: "配置说明手册" +weight: 1 +description: "以不同的方式来配置你的 Dubbo 应用" +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/_index.md new file mode 100644 index 000000000000..f9674a104a40 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "Annotation 配置手册" +linkTitle: "Annotation 配置手册" +weight: 5 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/description.md b/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/description.md new file mode 100644 index 000000000000..b82b3289ac85 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/description.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "配置项描述" +linkTitle: "配置项描述" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/guide.md new file mode 100644 index 000000000000..4ca6dd6a08fc --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/annotation/guide.md @@ -0,0 +1,97 @@ +--- +type: docs +title: "配置指南" +linkTitle: "配置指南" +weight: 1 +description: "以注解配置的方式来配置你的 Dubbo 应用" +--- + +{{% alert title="提示" color="primary" %}} +需要 `2.6.3` 及以上版本支持。 点此查看 [完整示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-annotation) +{{% /alert %}} + + +## 服务提供方 + +### `Service`注解暴露服务 + +```java +@Service +public class AnnotationServiceImpl implements AnnotationService { + @Override + public String sayHello(String name) { + return "annotation: hello, " + name; + } +} +``` + +### 增加应用共享配置 + +```properties +# dubbo-provider.properties +dubbo.application.name=annotation-provider +dubbo.registry.address=zookeeper://127.0.0.1:2181 +dubbo.protocol.name=dubbo +dubbo.protocol.port=20880 +``` + +### 指定Spring扫描路径 + +```java +@Configuration +@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.impl") +@PropertySource("classpath:/spring/dubbo-provider.properties") +static public class ProviderConfiguration { + +} +``` + +## 服务消费方 + +### `Reference`注解引用服务 + +```java +@Component("annotationAction") +public class AnnotationAction { + + @Reference + private AnnotationService annotationService; + + public String doSayHello(String name) { + return annotationService.sayHello(name); + } +} + +``` + +### 增加应用共享配置 + +```properties +# dubbo-consumer.properties +dubbo.application.name=annotation-consumer +dubbo.registry.address=zookeeper://127.0.0.1:2181 +dubbo.consumer.timeout=3000 +``` + +### 指定Spring扫描路径 + +```java +@Configuration +@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.action") +@PropertySource("classpath:/spring/dubbo-consumer.properties") +@ComponentScan(value = {"org.apache.dubbo.samples.simple.annotation.action"}) +static public class ConsumerConfiguration { + +} +``` + +### 调用服务 + +```java +public static void main(String[] args) throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); + context.start(); + final AnnotationAction annotationAction = (AnnotationAction) context.getBean("annotationAction"); + String hello = annotationAction.doSayHello("world"); +} +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/api/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/api/_index.md new file mode 100644 index 000000000000..80e4fec97b21 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/api/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "API 配置手册" +linkTitle: "API 配置手册" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/api/description.md b/content/zh/docs3-building/java-sdk/reference-manual/config/api/description.md new file mode 100644 index 000000000000..b82b3289ac85 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/api/description.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "配置项描述" +linkTitle: "配置项描述" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/api/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config/api/guide.md new file mode 100644 index 000000000000..fa1b50263033 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/api/guide.md @@ -0,0 +1,315 @@ +--- +type: docs +title: "配置指南" +linkTitle: "配置指南" +weight: 1 +description: "以API 配置的方式来配置你的 Dubbo 应用" +--- + +通过API编码方式组装配置,启动Dubbo,发布及订阅服务。此方式可以支持动态创建ReferenceConfig/ServiceConfig,结合泛化调用可以满足API Gateway或测试平台的需要。 + +> API 属性与XML配置项一一对应,各属性含义请参见:[XML配置参考手册](../../../references/xml/),比如:`ApplicationConfig.setName("xxx")` 对应 `` + +> API使用范围说明:API 仅用于 OpenAPI, ESB, Test, Mock, Gateway 等系统集成,普通服务提供方或消费方,请采用[XML 配置](../xml) 或 +> [注解配置](../annotation) 或 [属性配置](../properties) 方式使用 Dubbo + +> 参考[API示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-api) + +## 服务提供者 + +通过ServiceConfig暴露服务接口,发布服务接口到注册中心。 + +> 注意:为了更好支持Dubbo3的应用级服务发现,推荐使用新的DubboBootstrap API。 + +```java +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.ServiceConfig; +import com.xxx.DemoService; +import com.xxx.DemoServiceImpl; + +public class DemoProvider { + public static void main(String[] args) { + // 服务实现 + DemoService demoService = new DemoServiceImpl(); + + // 当前应用配置 + ApplicationConfig application = new ApplicationConfig(); + application.setName("demo-provider"); + + // 连接注册中心配置 + RegistryConfig registry = new RegistryConfig(); + registry.setAddress("zookeeper://10.20.130.230:2181"); + + // 服务提供者协议配置 + ProtocolConfig protocol = new ProtocolConfig(); + protocol.setName("dubbo"); + protocol.setPort(12345); + protocol.setThreads(200); + + // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口 + // 服务提供者暴露服务配置 + ServiceConfig service = new ServiceConfig(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏 + service.setApplication(application); + service.setRegistry(registry); // 多个注册中心可以用setRegistries() + service.setProtocol(protocol); // 多个协议可以用setProtocols() + service.setInterface(DemoService.class); + service.setRef(demoService); + service.setVersion("1.0.0"); + + // 暴露及注册服务 + service.export(); + + // 挂起等待(防止进程退出) + System.in.read(); + } +} +``` + +## 服务消费者 + +通过ReferenceConfig引用远程服务,从注册中心订阅服务接口。 + +> 注意:为了更好支持Dubbo3的应用级服务发现,推荐使用新的DubboBootstrap API。 + +```java +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ConsumerConfig; +import org.apache.dubbo.config.ReferenceConfig; +import com.xxx.DemoService; + +public class DemoConsumer { + public static void main(String[] args) { + // 当前应用配置 + ApplicationConfig application = new ApplicationConfig(); + application.setName("demo-consumer"); + + // 连接注册中心配置 + RegistryConfig registry = new RegistryConfig(); + registry.setAddress("zookeeper://10.20.130.230:2181"); + + // 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接 + // 引用远程服务 + ReferenceConfig reference = new ReferenceConfig(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏 + reference.setApplication(application); + reference.setRegistry(registry); // 多个注册中心可以用setRegistries() + reference.setInterface(DemoService.class); + reference.setVersion("1.0.0"); + + // 和本地bean一样使用demoService + // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用 + DemoService demoService = reference.get(); + demoService.sayHello("Dubbo"); + } +} +``` + +## Bootstrap API + +通过DubboBootstrap API可以减少重复配置,更好控制启动过程,支持批量发布/订阅服务接口,还可以更好支持Dubbo3的应用级服务发现。 + +```java +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.ServiceConfig; +import com.xxx.DemoService; +import com.xxx.DemoServiceImpl; + +public class DemoProvider { + public static void main(String[] args) { + + ConfigCenterConfig configCenter = new ConfigCenterConfig(); + configCenter.setAddress("zookeeper://127.0.0.1:2181"); + + // 服务提供者协议配置 + ProtocolConfig protocol = new ProtocolConfig(); + protocol.setName("dubbo"); + protocol.setPort(12345); + protocol.setThreads(200); + + // 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口 + // 服务提供者暴露服务配置 + ServiceConfig demoServiceConfig = new ServiceConfig<>(); + demoServiceConfig.setInterface(DemoService.class); + demoServiceConfig.setRef(new DemoServiceImpl()); + demoServiceConfig.setVersion("1.0.0"); + + // 第二个服务配置 + ServiceConfig fooServiceConfig = new ServiceConfig<>(); + fooServiceConfig.setInterface(FooService.class); + fooServiceConfig.setRef(new FooServiceImpl()); + fooServiceConfig.setVersion("1.0.0"); + + ... + + // 通过DubboBootstrap简化配置组装,控制启动过程 + DubboBootstrap.getInstance() + .application("demo-provider") // 应用配置 + .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) // 注册中心配置 + .protocol(protocol) // 全局默认协议配置 + .service(demoServiceConfig) // 添加ServiceConfig + .service(fooServiceConfig) + .start() // 启动Dubbo + .await(); // 挂起等待(防止进程退出) + } +} +``` + +```java +import org.apache.dubbo.config.bootstrap.DubboBootstrap; +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.RegistryConfig; +import org.apache.dubbo.config.ProviderConfig; +import org.apache.dubbo.config.ServiceConfig; +import com.xxx.DemoService; +import com.xxx.DemoServiceImpl; + +public class DemoConsumer { + public static void main(String[] args) { + + // 引用远程服务 + ReferenceConfig demoServiceReference = new ReferenceConfig(); + demoServiceReference.setInterface(DemoService.class); + demoServiceReference.setVersion("1.0.0"); + + ReferenceConfig fooServiceReference = new ReferenceConfig(); + fooServiceReference.setInterface(FooService.class); + fooServiceReference.setVersion("1.0.0"); + + // 通过DubboBootstrap简化配置组装,控制启动过程 + DubboBootstrap bootstrap = DubboBootstrap.getInstance(); + bootstrap.application("demo-consumer") // 应用配置 + .registry(new RegistryConfig("zookeeper://127.0.0.1:2181")) // 注册中心配置 + .reference(demoServiceReference) // 添加ReferenceConfig + .service(fooServiceReference) + .start(); // 启动Dubbo + + ... + + // 和本地bean一样使用demoService + // 通过Interface获取远程服务接口代理,不需要依赖ReferenceConfig对象 + DemoService demoService = DubboBootstrap.getInstance().getCache().get(DemoService.class); + demoService.sayHello("Dubbo"); + + FooService fooService = DubboBootstrap.getInstance().getCache().get(FooService.class); + fooService.greeting("Dubbo"); + } + +} + +``` + +## 其它配置 + +API配置能力与XML配置是等价的,其它的各种配置都可以用API设置。 + +下面只列出不同的地方,其它参见上面的写法。 + +### 基本配置 + +可以在DubboBootstrap中设置全局基本配置,包括应用配置、协议配置、注册中心、配置中心、元数据中心、模块、监控、SSL、provider配置、consumer配置等。 + +```java +// 注册中心 +RegistryConfig registry = new RegistryConfig(); +registry.setAddress("zookeeper://192.168.10.1:2181"); +... + +// 服务提供者协议配置 +ProtocolConfig protocol = new ProtocolConfig(); +protocol.setName("dubbo"); +protocol.setPort(12345); +protocol.setThreads(200); +... + +// 配置中心 +ConfigCenterConfig configCenter = new ConfigCenterConfig(); +configCenter.setAddress("zookeeper://192.168.10.2:2181"); +... + +// 元数据中心 +MetadataReportConfig metadataReport = new MetadataReportConfig(); +metadataReport.setAddress("zookeeper://192.168.10.3:2181"); +... + +// Metrics +MetricsConfig metrics = new MetricsConfig(); +metrics.setProtocol("dubbo"); +... + +// SSL +SslConfig ssl = new SslConfig(); +ssl.setServerKeyCertChainPath("/path/ssl/server-key-cert-chain"); +ssl.setServerPrivateKeyPath("/path/ssl/server-private-key"); +... + +// Provider配置(ServiceConfig默认配置) +ProviderConfig provider = new ProviderConfig(); +provider.setGroup("demo"); +provider.setVersion("1.0.0"); +... + +// Consumer配置(ReferenceConfig默认配置) +ConsumerConfig consumer = new ConsumerConfig(); +consumer.setGroup("demo"); +consumer.setVersion("1.0.0"); +consumer.setTimeout(2000); +... + +DubboBootstrap.getInstance() + .application("demo-app") + .registry(registry) + .protocol(protocol) + .configCenter(configCenter) + .metadataReport(metadataReport) + .module(new ModuleConfig("module")) + .metrics(metrics) + .ssl(ssl) + .provider(provider) + .consumer(consumer) + ... + .start(); + +``` + +### 方法级设置 + +```java +... + +// 方法级配置 +List methods = new ArrayList(); +MethodConfig method = new MethodConfig(); +method.setName("sayHello"); +method.setTimeout(10000); +method.setRetries(0); +methods.add(method); + +// 引用远程服务 +ReferenceConfig reference = new ReferenceConfig(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏 +... +reference.setMethods(methods); // 设置方法级配置 + +... +``` + +### 点对点直连 + +```java + +... + +// 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏 +ReferenceConfig reference = new ReferenceConfig(); +// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心, +// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值, +// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名 +reference.setUrl("dubbo://10.20.130.230:20880/com.xxx.DemoService"); + +... +``` + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/env/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/env/_index.md new file mode 100644 index 000000000000..d52ae588f593 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/env/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "环境变量配置手册" +linkTitle: "环境变量配置手册" +weight: 7 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/env/description.md b/content/zh/docs3-building/java-sdk/reference-manual/config/env/description.md new file mode 100644 index 000000000000..b82b3289ac85 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/env/description.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "配置项描述" +linkTitle: "配置项描述" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/env/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config/env/guide.md new file mode 100644 index 000000000000..7b9af1b902c9 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/env/guide.md @@ -0,0 +1,35 @@ +--- +type: docs +title: "配置指南" +linkTitle: "配置指南" +weight: 1 +description: "在 Dubbo 中自动加载环境变量" +--- + +从 2.7.3 版本开始,Dubbo 会自动从约定 key 中读取配置,并将配置以 Key-Value 的形式写入到URL中。 + +支持的 key 有以下两个: + +1. `dubbo.labels`,指定一些列配置到 URL 中的键值对,通常通过 JVM -D 或系统环境变量指定。 + + 增加以下配置: + + ```properties + # JVM + -Ddubbo.labels = "tag1=value1; tag2=value2" + # 环境变量 + DUBBO_LABELS = "tag1=value1; tag2=value2" + ``` + + 最终生成的 URL 会包含 tag1、tag2 两个 key: `dubbo://xxx?tag1=value1&tag2=value2` + +2. `dubbo.env.keys`,指定环境变量 key 值,Dubbo 会尝试从环境变量加载每个 key + + ```properties + # JVM + -Ddubbo.env.keys = "DUBBO_TAG1, DUBBO_TAG2" + # 环境变量 + DUBBO_ENV_KEYS = "DUBBO_TAG1, DUBBO_TAG2" + ``` + + 最终生成的 URL 会包含 DUBBO_TAG1、DUBBO_TAG2 两个 key: `dubbo://xxx?DUBBO_TAG1=value1&DUBBO_TAG2=value2` \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/overview/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/overview/_index.md new file mode 100644 index 000000000000..c7b42882599b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/overview/_index.md @@ -0,0 +1,196 @@ +--- +type: docs +title: "配置概述" +linkTitle: "配置概述" +weight: 1 +description: "Dubbo配置介绍" +--- + +本文主要介绍Dubbo配置概况,包括配置组件、配置来源、配置方式及配置加载流程。 + +## 配置组件 + +Dubbo框架的配置项比较繁多,为了更好地管理各种配置,将其按照用途划分为不同的组件,最终所有配置项都会汇聚到URL中,传递给后续处理模块。 + +常用配置组件如下: +- application: Dubbo应用配置 +- registry: 注册中心 +- protocol: 服务提供者RPC协议 +- config-center: 配置中心 +- metadata-report: 元数据中心 +- service: 服务提供者配置 +- reference: 远程服务引用配置 +- provider: service的默认配置或分组配置 +- consumer: reference的默认配置或分组配置 +- module: 模块配置 +- monitor: 监控配置 +- metrics: 指标配置 +- ssl: SSL/TLS配置 + +### consumer 与 reference的关系 + +reference可以指定具体的consumer,如果没有指定consumer则会自动使用全局默认的consumer配置。 + +consumer的属性是reference属性的默认值,可以体现在两个地方: + +1. 在刷新属性(属性覆盖)时,先提取其consumer的属性,然后提取reference自身的属性覆盖上去,叠加后的属性集合作为配置来源之一。 +2. 在组装reference的URL参数时,先附加其consumer的属性,然后附加reference自身的属性。 + +> 可以将consumer组件理解为reference组件的虚拟分组,根据需要可以定义多个不同的consumer,不同的consumer设置特定的默认值, +然后在reference中指定consumer或者将 标签嵌套在标签之中。 + +### provider 与 service的关系 + +service可以指定具体的provider,如果没有指定则会自动使用全局默认的provider配置。 +provider的属性是service属性的默认值,覆盖规则类似上面的consumer与reference,也可以将provider理解为service的虚拟分组。 + + +## 配置来源 + +从Dubbo支持的配置来源说起,默认有6种配置来源: + +- JVM System Properties,JVM -D 参数 +- System environment,JVM进程的环境变量 +- Externalized Configuration,外部化配置,从配置中心读取 +- Application Configuration,应用的属性配置,从Spring应用的Environment中提取"dubbo"打头的属性集 +- API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式 +- 从classpath读取配置文件 dubbo.properties + +### 覆盖关系 + +下图展示了配置覆盖关系的优先级,从上到下优先级依次降低: + +![覆盖关系](/imgs/blog/configuration.jpg) + +请参考相关内容:[属性覆盖](../properties#属性覆盖)。 + + +## 配置方式 + +按照驱动方式可以分为以下四种方式: + +### API配置 +以Java编码的方式组织配置,包括Raw API和Bootstrap API,具体请参考[API配置](../api)。 + +### XML配置 +以XML方式配置各种组件,支持与Spring无缝集成,具体请参考[XML配置](../xml)。 + +### Annotation配置 +以注解方式暴露服务和引用服务接口,支持与Spring无缝集成,具体请参考[Annotation配置](../annotation)。 + +### 属性配置 +根据属性Key-value生成配置组件,类似SpringBoot的ConfigurationProperties,具体请参考[属性配置](../properties)。 + +属性配置的另外一个重要的功能特性是[属性覆盖](../properties#属性覆盖),使用外部属性的值覆盖已创建的配置组件属性。 + +如果要将属性配置放到外部的配置中心,请参考[外部化配置](../external-config)。 + +除了外围驱动方式上的差异,Dubbo的配置读取总体上遵循了以下几个原则: + +1. Dubbo 支持了多层级的配置,并按预定优先级自动实现配置间的覆盖,最终所有配置汇总到数据总线URL后驱动后续的服务暴露、引用等流程。 +2. 配置格式以 Properties 为主,在配置内容上遵循约定的 `path-based` 的[命名规范](../properties#配置格式) + + +## 配置加载流程 + +![配置加载流程](/imgs/v3/config/config-load.svg) + +从上图可以看出,配置加载大概分为两个阶段: + +* 第一阶段为DubboBootstrap初始化之前,在Spring context启动时解析处理XML配置/注解配置/Java-config 或者是执行API配置代码,创建config bean并且加入到ConfigManager中。 +* 第二阶段为DubboBootstrap初始化过程,从配置中心读取外部配置,依次处理实例级属性配置和应用级属性配置,最后刷新所有配置实例的属性,也就是[属性覆盖](../properties#属性覆盖)。 + + +## 几种编程配置方式 + +接下来,我们看一下选择不同的开发方式时,对应到 ServiceConfig、ReferenceConfig 等编程接口采集的配置的变化。 + +#### Spring XML + +> 参见[示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-basic) + +```xml + + + + + + + + + + + +``` + +#### Spring Annotation + +> 参见[示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-annotation) + +```java + // AnnotationService服务实现 + + @Service + public class AnnotationServiceImpl implements AnnotationService { + @Override + public String sayHello(String name) { + System.out.println("async provider received: " + name); + return "annotation: hello, " + name; + } + } +``` + +```properties + ## dubbo.properties + +dubbo.application.name=annotation-provider +dubbo.registry.address=zookeeper://127.0.0.1:2181 +dubbo.protocol.name=dubbo +dubbo.protocol.port=20880 +``` + +#### Spring Boot + +> 参见[示例](https://github.com/apache/dubbo-spring-boot-project/tree/master/dubbo-spring-boot-samples) + +```properties + ## application.properties + +# Spring boot application +spring.application.name=dubbo-externalized-configuration-provider-sample + +# Base packages to scan Dubbo Component: @com.alibaba.dubbo.config.annotation.Service +dubbo.scan.base-packages=com.alibaba.boot.dubbo.demo.provider.service + +# Dubbo Application +## The default value of dubbo.application.name is ${spring.application.name} +## dubbo.application.name=${spring.application.name} + +# Dubbo Protocol +dubbo.protocol.name=dubbo +dubbo.protocol.port=12345 + +## Dubbo Registry +dubbo.registry.address=N/A + +## DemoService version +demo.service.version=1.0.0 +``` + +#### API + +> 参考[示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-api) + +```java +public static void main(String[] args) throws IOException { + ServiceConfig service = new ServiceConfig<>(); + service.setApplication(new ApplicationConfig("first-dubbo-provider")); + service.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234")); + service.setInterface(GreetingsService.class); + service.setRef(new GreetingsServiceImpl()); + service.export(); + System.out.println("first-dubbo-provider is running."); + System.in.read(); + } +``` + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/properties/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/properties/_index.md new file mode 100644 index 000000000000..eba4eec50bda --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/properties/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "Properties 配置手册" +linkTitle: "Properties 配置手册" +weight: 5 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/properties/description.md b/content/zh/docs3-building/java-sdk/reference-manual/config/properties/description.md new file mode 100644 index 000000000000..b82b3289ac85 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/properties/description.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "配置项描述" +linkTitle: "配置项描述" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/properties/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config/properties/guide.md new file mode 100644 index 000000000000..409d5b4899fc --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/properties/guide.md @@ -0,0 +1,301 @@ +--- +type: docs +title: "配置指南" +linkTitle: "配置指南" +weight: 1 +description: "以属性配置的方式来配置你的 Dubbo 应用" +--- + +Dubbo属性配置有两个职责: + +1. 定义配置:根据属性创建配置组件实例,类似SpringBoot的`@ConfigurationProperties`的作用。 +2. 属性覆盖:覆盖已存在的配置组件实例的属性值,类似Spring `PropertyOverrideConfigurer` 的作用。 + + +> 一个属性配置的例子 [dubbo-spring-boot-samples](https://github.com/apache/dubbo-spring-boot-project/tree/master/dubbo-spring-boot-samples) + +```properties + ## application.properties + + # Spring boot application + spring.application.name=dubbo-externalized-configuration-provider-sample + + # Base packages to scan Dubbo Component: @com.alibaba.dubbo.config.annotation.Service + dubbo.scan.base-packages=com.alibaba.boot.dubbo.demo.provider.service + + # Dubbo Application + ## The default value of dubbo.application.name is ${spring.application.name} + ## dubbo.application.name=${spring.application.name} + + # Dubbo Protocol + dubbo.protocol.name=dubbo + dubbo.protocol.port=12345 + + ## Dubbo Registry + dubbo.registry.address=N/A + + ## service default version + dubbo.provider.version=1.0.0 +``` + +## 配置来源 + +从Dubbo支持的配置来源说起,默认有6种配置来源: + +- JVM System Properties,JVM -D 参数 +- System environment,JVM进程的环境变量 +- Externalized Configuration,外部化配置,从配置中心读取 +- Application Configuration,应用的属性配置,从Spring应用的Environment中提取"dubbo"打头的属性集 +- API / XML /注解等编程接口采集的配置可以被理解成配置来源的一种,是直接面向用户编程的配置采集方式 +- 从classpath读取配置文件 dubbo.properties + +关于dubbo.properties属性: + +1. 如果在 classpath 下有超过一个 dubbo.properties 文件,比如,两个 jar 包都各自包含了 dubbo.properties,dubbo 将随机选择一个加载,并且打印错误日志。 +2. Dubbo 可以自动加载 classpath 根目录下的 dubbo.properties,但是你同样可以使用 JVM 参数来指定路径:`-Ddubbo.properties.file=xxx.properties`。 + +### 覆盖关系 + +下图展示了配置覆盖关系的优先级,从上到下优先级依次降低: + +![覆盖关系](/imgs/blog/configuration.jpg) + +请参考相关内容:[属性覆盖](../properties#属性覆盖)。 + +## 处理流程 + +属性配置处理流程请查看 [配置加载流程](../overview#配置加载流程)。 + + +## 配置格式 + +目前Dubbo支持的所有配置都是`.properties`格式的,包括`-D`、`Externalized Configuration`等,`.properties`中的所有配置项遵循一种`path-based`的配置格式。 + +在Spring应用中也可以将属性配置放到`application.yml`中,其树层次结构的方式可读性更好一些。 + +```properties +# 应用级配置(无id) +dubbo.{config-type}.{config-item}={config-item-value} + +# 实例级配置(指定id或name) +dubbo.{config-type}s.{config-id}.{config-item}={config-item-value} +dubbo.{config-type}s.{config-name}.{config-item}={config-item-value} + +# 服务接口配置 +dubbo.service.{interface-name}.{config-item}={config-item-value} +dubbo.reference.{interface-name}.{config-item}={config-item-value} + +# 方法配置 +dubbo.service.{interface-name}.{method-name}.{config-item}={config-item-value} +dubbo.reference.{interface-name}.{method-name}.{config-item}={config-item-value} + +# 方法argument配置 +dubbo.reference.{interface-name}.{method-name}.{argument-index}.{config-item}={config-item-value} + +``` + +### 应用级配置(无id) + +应用级配置的格式为:配置类型单数前缀,无id/name。 +```properties +# 应用级配置(无id) +dubbo.{config-type}.{config-item}={config-item-value} +``` + +* 如果该类型的配置不存在任何实例时,则将使用应用级配置的属性创建默认实例。 +* 如果该类型的配置存在一个或多个实例,且没有找到配置实例对应的配置时,则将应用级配置的属性用于属性覆盖。详细请参考[属性覆盖](../properties#属性覆盖)。 + +```properties +dubbo.application.name=demo-provider +dubbo.application.qos-enable=false + +dubbo.registry.address=zookeeper://127.0.0.1:2181 + +dubbo.protocol.name=dubbo +dubbo.protocol.port=-1 +``` + +### 实例级配置(指定id或name) + +针对某个实例的属性配置需要指定id或者name,其前缀格式为:配置类型复数前缀 + id/name。 + +```properties +# 实例级配置(指定id或name) +dubbo.{config-type}s.{config-id}.{config-item}={config-item-value} +dubbo.{config-type}s.{config-name}.{config-item}={config-item-value} + +``` + +* 如果不存在该id或者name的实例,则根据属性创建配置组件实例。 +* 如果已存在相同id或name的实例,则提取该前缀的属性集合用于属性覆盖。详细请参考[属性覆盖](../properties#属性覆盖)。 +* 具体的配置复数形式请参考[单复数配置对照表](../properties#单复数配置对照表) + +```properties +dubbo.registries.unit1.address=zookeeper://127.0.0.1:2181 +dubbo.registries.unit2.address=zookeeper://127.0.0.1:2182 + +dubbo.protocols.dubbo.name=dubbo +dubbo.protocols.dubbo.port=20880 + +dubbo.protocols.hessian.name=hessian +dubbo.protocols.hessian.port=8089 +``` + +### 服务接口配置 + +```properties +dubbo.service.org.apache.dubbo.samples.api.DemoService.timeout=5000 +dubbo.reference.org.apache.dubbo.samples.api.DemoService.timeout=6000 +``` + +### 方法配置 + +方法配置格式: + +```properties +# 方法配置 +dubbo.service.{interface-name}.{method-name}.{config-item}={config-item-value} +dubbo.reference.{interface-name}.{method-name}.{config-item}={config-item-value} + +# 方法argument配置 +dubbo.reference.{interface-name}.{method-name}.{argument-index}.{config-item}={config-item-value} +``` + +方法配置示例: +```properties +dubbo.reference.org.apache.dubbo.samples.api.DemoService.sayHello.timeout=7000 +dubbo.reference.org.apache.dubbo.samples.api.DemoService.sayHello.oninvoke=notifyService.onInvoke +dubbo.reference.org.apache.dubbo.samples.api.DemoService.sayHello.onreturn=notifyService.onReturn +dubbo.reference.org.apache.dubbo.samples.api.DemoService.sayHello.onthrow=notifyService.onThrow +dubbo.reference.org.apache.dubbo.samples.api.DemoService.sayHello.0.callback=true +``` + +等价于XML配置: + +```xml + + + + + +``` + +### 参数配置 + +parameters参数为map对象,支持xxx.parameters=[{key:value},{key:value}]方式进行配置。 +```properties +dubbo.application.parameters=[{item1:value1},{item2:value2}] +dubbo.reference.org.apache.dubbo.samples.api.DemoService.parameters=[{item3:value3}] +``` + +### 传输层配置 + +triple协议采用Http2做底层通信协议,允许使用者自定义Http2的[6个settings参数](https://datatracker.ietf.org/doc/html/rfc7540#section-6.5.2) + +配置格式如下: + +```properties +# 通知对端header压缩索引表的上限个数 +dubbo.rpc.tri.header-table-size=4096 + +# 启用服务端推送功能 +dubbo.rpc.tri.enable-push=false + +# 通知对端允许的最大并发流数 +dubbo.rpc.tri.max-concurrent-streams=2147483647 + +# 声明发送端的窗口大小 +dubbo.rpc.tri.initial-window-size=1048576 + +# 设置帧的最大字节数 +dubbo.rpc.tri.max-frame-size=32768 + +# 通知对端header未压缩的最大字节数 +dubbo.rpc.tri.max-header-list-size=8192 +``` + +等价于yml配置: + +```yaml +dubbo: + rpc: + tri: + header-table-size: 4096 + enable-push: false + max-concurrent-streams: 2147483647 + initial-window-size: 1048576 + max-frame-size: 32768 + max-header-list-size: 8192 +``` + + + +### 属性与XML配置映射规则 + +可以将 xml 的 tag 名和属性名组合起来,用 ‘.’ 分隔。每行一个属性。 + +* `dubbo.application.name=foo` 相当于 `` +* `dubbo.registry.address=10.20.153.10:9090` 相当于 ` ` + +如果在 xml 配置中有超过一个的 tag,那么你可以使用 ‘id’ 进行区分。如果你不指定id,它将作用于所有 tag。 + +* `dubbo.protocols.rmi.port=1099` 相当于 ` ` +* `dubbo.registries.china.address=10.20.153.10:9090` 相当于 `` + + +## 属性覆盖 + +属性覆盖是指用配置的属性值覆盖config bean实例的属性,类似Spring [PropertyOverrideConfigurer](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/PropertyOverrideConfigurer.html) 的作用。 + +> Property resource configurer that overrides bean property values in an application context definition. It pushes values from a properties file into bean definitions. +Configuration lines are expected to be of the following form: +> +> beanName.property=value + +但与`PropertyOverrideConfigurer`的不同之处是,Dubbo的属性覆盖有多个匹配格式,优先级从高到低依次是: + +```properties +#1. 指定id的实例级配置 +dubbo.{config-type}s.{config-id}.{config-item}={config-item-value} + +#2. 指定name的实例级配置 +dubbo.{config-type}s.{config-name}.{config-item}={config-item-value} + +#3. 应用级配置(单数配置) +dubbo.{config-type}.{config-item}={config-item-value} +``` + +属性覆盖处理流程: + +按照优先级从高到低依次查找,如果找到此前缀开头的属性,则选定使用这个前缀提取属性,忽略后面的配置。 + +![属性覆盖流程](/imgs/v3/config/properties-override.svg) + + +## 单复数配置对照表 + +复数配置的命名与普通单词变复数的规则相同: + +1. 字母y结尾时,去掉y,改为ies +2. 字母s结尾时,加es +3. 其它加s + +| Config Type | 单数配置 | 复数配置 | +| --------------------------------- | ------------------------------------------------------------ | ----------------------------------- | +| application | dubbo.application.xxx=xxx | dubbo.applications.{id}.xxx=xxx
dubbo.applications.{name}.xxx=xxx | +| protocol | dubbo.protocol.xxx=xxx | dubbo.protocols.{id}.xxx=xxx
dubbo.protocols.{name}.xxx=xxx | +| module | dubbo.module.xxx=xxx | dubbo.modules.{id}.xxx=xxx
dubbo.modules.{name}.xxx=xxx | +| registry | dubbo.registry.xxx=xxx | dubbo.registries.{id}.xxx=xxx | +| monitor | dubbo.monitor.xxx=xxx | dubbo.monitors.{id}.xxx=xxx | +| config-center | dubbo.config-center.xxx=xxx | dubbo.config-centers.{id}.xxx=xxx | +| metadata-report | dubbo.metadata-report.xxx=xxx | dubbo.metadata-reports.{id}.xxx=xxx | +| ssl | dubbo.ssl.xxx=xxx | dubbo.ssls.{id}.xxx=xxx | +| metrics | dubbo.metrics.xxx=xxx | dubbo.metricses.{id}.xxx=xxx | +| provider | dubbo.provider.xxx=xxx | dubbo.providers.{id}.xxx=xxx | +| consumer | dubbo.consumer.xxx=xxx | dubbo.consumers.{id}.xxx=xxx | +| service | dubbo.service.{interfaceName}.xxx=xxx | 无 | +| reference | dubbo.reference.{interfaceName}.xxx=xxx | 无 | +| method | dubbo.service.{interfaceName}.{methodName}.xxx=xxx
dubbo.reference.{interfaceName}.{methodName}.xxx=xxx | 无 | +| argument | dubbo.service.{interfaceName}.{methodName}.{arg-index}.xxx=xxx | 无 | + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/xml/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/xml/_index.md new file mode 100644 index 000000000000..80f68ebbe012 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/xml/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "XML 配置手册" +linkTitle: "XML 配置手册" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/xml/description.md b/content/zh/docs3-building/java-sdk/reference-manual/config/xml/description.md new file mode 100644 index 000000000000..071c24975dce --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/xml/description.md @@ -0,0 +1,349 @@ +--- +type: docs +title: "配置项描述" +linkTitle: "配置项描述" +weight: 2 +--- + + +# dubbo:application + +应用信息配置。对应的配置类:`org.apache.dubbo.config.ApplicationConfig` + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| name | application | string | 必填 | | 服务治理 | 当前应用名称,用于注册中心计算应用间依赖关系,注意:消费者和提供者应用名不要一样,此参数不是匹配条件,你当前项目叫什么名字就填什么,和提供者消费者角色无关,比如:kylin应用调用了morgan应用的服务,则kylin项目配成kylin,morgan项目配成morgan,可能kylin也提供其它服务给别人使用,但kylin项目永远配成kylin,这样注册中心将显示kylin依赖于morgan | 1.0.16以上版本 | +| version | application.version | string | 可选 | | 服务治理 | 当前应用的版本 | 2.2.0以上版本 | +| owner | owner | string | 可选 | | 服务治理 | 应用负责人,用于服务治理,请填写负责人公司邮箱前缀 | 2.0.5以上版本 | +| organization | organization | string | 可选 | | 服务治理 | 组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等 | 2.0.0以上版本 | +| architecture
| architecture
| string | 可选 | | 服务治理 | 用于服务分层对应的架构。如,intl、china。不同的架构使用不同的分层。 | 2.0.7以上版本 | +| environment | environment | string | 可选 | | 服务治理 | 应用环境,如:develop/test/product,不同环境使用不同的缺省值,以及作为只用于开发测试功能的限制条件 | 2.0.0以上版本 | +| compiler | compiler | string | 可选 | javassist | 性能优化 | Java字节码编译器,用于动态类的生成,可选:jdk或javassist | 2.1.0以上版本 | +| logger | logger | string | 可选 | slf4j | 性能优化 | 日志输出方式,可选:slf4j,jcl,log4j,log4j2,jdk | 2.2.0以上版本 | +| metadata-type | metadata-type |String| 可选 | local | 服务治理 | metadata 传递方式,是以 Provider 视角而言的,Consumer 侧配置无效,可选值有:
remote - Provider 把 metadata 放到远端注册中心,Consumer 从注册中心获取
local - Provider 把 metadata 放在本地,Consumer 从 Provider 处直接获取| 2.7.6以上版本 | + + +# dubbo:argument + +方法参数配置。对应的配置类: `org.apache.dubbo.config.ArgumentConfig`。该标签为 `` 的子标签,用于方法参数的特征描述,比如: + +```xml + + + +``` +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| index | | int | 必填 | | 标识 | 参数索引 | 2.0.6以上版本 | +| type | | String | 与index二选一 | | 标识 | 通过参数类型查找参数的index | 2.0.6以上版本 | +| callback | <metodName><index>.retries | boolean | 可选 | | 服务治理 | 参数是否为callback接口,如果为callback,服务提供方将生成反向代理,可以从服务提供方反向调用消费方,通常用于事件推送. | 2.0.6以上版本 | + + +# dubbo:config-center + +配置中心。对应的配置类:`org.apache.dubbo.config.ConfigCenterConfig` + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 描述 | 兼容性 | +| ---------------- | ---------------------- | ------------------- | -------- | ---------------- | ------------------------------------------------------------ | ------ | +| protocol | config.protocol | string | 可选 | zookeeper | 使用哪个配置中心:apollo、zookeeper、nacos等。
以zookeeper为例
1. 指定protocol,则address可以简化为`127.0.0.1:2181`;
2. 不指定protocol,则address取值为`zookeeper://127.0.0.1:2181` | 2.7.0+ | +| address | config.address | string | 必填 | | 配置中心地址。
取值参见protocol说明 | 2.7.0+ | +| highest-priority | config.highestPriority | boolean | 可选 | true | 来自配置中心的配置项具有最高优先级,即会覆盖本地配置项。 | 2.7.0+ | +| namespace | config.namespace | string | 可选 | dubbo | 通常用于多租户隔离,实际含义视具体配置中心而不同。
如:
zookeeper - 环境隔离,默认值`dubbo`;
apollo - 区分不同领域的配置集合,默认使用`dubbo`和`application` | 2.7.0+ | +| cluster | config.cluster | string | 可选 | | 含义视所选定的配置中心而不同。
如Apollo中用来区分不同的配置集群 | 2.7.0+ | +| group | config.group | string | 可选 | dubbo | 含义视所选定的配置中心而不同。
nacos - 隔离不同配置集
zookeeper - 隔离不同配置集 | 2.7.0+ | +| check | config.check | boolean | 可选 | true | 当配置中心连接失败时,是否终止应用启动。 | 2.7.0+ | +| config-file | config.configFile | string | 可选 | dubbo.properties | 全局级配置文件所映射到的key
zookeeper - 默认路径/dubbo/config/dubbo/dubbo.properties
apollo - dubbo namespace中的dubbo.properties键 | 2.7.0+ | +| timeout | config.timeout | integer | | 3000ms | 获取配置的超时时间 | 2.7.0+ | +| username | | string | | | 如果配置中心需要做校验,用户名
Apollo暂未启用 | 2.7.0+ | +| password | | string | | | 如果配置中心需要做校验,密码
Apollo暂未启用 | 2.7.0+ | +| parameters | | Map | | | 扩展参数,用来支持不同配置中心的定制化配置参数 | 2.7.0+ | +| include-spring-env | | boolean | 可选 | false | 使用Spring框架时支持,为true时,会自动从Spring Environment中读取配置。
默认依次读取
key为dubbo.properties的配置
key为dubbo.properties的PropertySource | 2.7.0+ | + + +# dubbo:consumer + +服务消费者缺省值配置。配置类: `org.apache.dubbo.config.ConsumerConfig` 。同时该标签为 `` 标签的缺省值设置。 + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| timeout | default.timeout | int | 可选 | 1000 | 性能调优 | 远程服务调用超时时间(毫秒) | 1.0.16以上版本 | +| retries | default.retries | int | 可选 | 2 | 性能调优 | 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0,仅在cluster为failback/failover时有效 | 1.0.16以上版本 | +| loadbalance | default.loadbalance | string | 可选 | random | 性能调优 | 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用 | 1.0.16以上版本 | +| async | default.async | boolean | 可选 | false | 性能调优 | 是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程 | 2.0.0以上版本 | +| connections | default.connections | int | 可选 | 100 | 性能调优 | 每个服务对每个提供者的最大连接数,rmi、http、hessian等短连接协议支持此配置,dubbo协议长连接不支持此配置 | 1.0.16以上版本 | +| generic | generic | boolean | 可选 | false | 服务治理 | 是否缺省泛化接口,如果为泛化接口,将返回GenericService | 2.0.0以上版本 | +| check | check | boolean | 可选 | true | 服务治理 | 启动时检查提供者是否存在,true报错,false忽略 | 1.0.16以上版本 | +| proxy | proxy | string | 可选 | javassist | 性能调优 | 生成动态代理方式,可选:jdk/javassist | 2.0.5以上版本 | +| owner | owner | string | 可选 | | 服务治理 | 调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀 | 2.0.5以上版本 | +| actives | default.actives | int | 可选 | 0 | 性能调优 | 每服务消费者每服务每方法最大并发调用数 | 2.0.5以上版本 | +| cluster | default.cluster | string | 可选 | failover | 性能调优 | 集群方式,可选:failover/failfast/failsafe/failback/forking | 2.0.5以上版本 | +| filter | reference.filter | string | 可选 | | 性能调优 | 服务消费方远程调用过程拦截器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| listener | invoker.listener | string | 可选 | | 性能调优 | 服务消费方引用服务监听器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| registry | | string | 可选 | 缺省向所有registry注册 | 配置关联 | 向指定注册中心注册,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A | 2.0.5以上版本 | +| layer | layer | string | 可选 | | 服务治理 | 服务调用者所在的分层。如:biz、dao、intl:web、china:acton。 | 2.0.7以上版本 | +| init | init | boolean | 可选 | false | 性能调优 | 是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。 | 2.0.10以上版本 | +| cache | cache | string/boolean | 可选 | | 服务治理 | 以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等 | Dubbo2.1.0及其以上版本支持 | +| validation | validation | boolean | 可选 | | 服务治理 | 是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验 | Dubbo2.1.0及其以上版本支持 | +| version | version | string | 可选 | | 服务治理 | 在 Dubbo 中为同一个服务配置多个版本 | Dubbo2.2.0及其以上版本支持 | + +# dubbo:method + +方法级配置。对应的配置类: `org.apache.dubbo.config.MethodConfig`。同时该标签为 `` 或 `` 的子标签,用于控制到方法级。 + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| name | | string | 必填 | | 标识 | 方法名 | 1.0.8以上版本 | +| timeout | <methodName>.timeout | int | 可选 | 缺省为的timeout | 性能调优 | 方法调用超时时间(毫秒) | 1.0.8以上版本 | +| retries | <methodName>.retries | int | 可选 | 缺省为<dubbo:reference>的retries | 性能调优 | 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0 | 2.0.0以上版本 | +| loadbalance | <methodName>.loadbalance | string | 可选 | 缺省为的loadbalance | 性能调优 | 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用 | 2.0.0以上版本 | +| async | <methodName>.async | boolean | 可选 | 缺省为<dubbo:reference>的async | 性能调优 | 是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程 | 1.0.9以上版本 | +| sent | <methodName>.sent | boolean | 可选 | true | 性能调优 | 异步调用时,标记sent=true时,表示网络已发出数据 | 2.0.6以上版本 | +| actives | <methodName>.actives | int | 可选 | 0 | 性能调优 | 每服务消费者最大并发调用限制 | 2.0.5以上版本 | +| executes | <methodName>.executes | int | 可选 | 0 | 性能调优 | 每服务每方法最大使用线程数限制- -,此属性只在<dubbo:method>作为<dubbo:service>子标签时有效 | 2.0.5以上版本 | +| deprecated | <methodName>.deprecated | boolean | 可选 | false | 服务治理 | 服务方法是否过时,此属性只在<dubbo:method>作为<dubbo:service>子标签时有效 | 2.0.5以上版本 | +| sticky | <methodName>.sticky | boolean | 可选 | false | 服务治理 | 设置true 该接口上的所有方法使用同一个provider.如果需要更复杂的规则,请使用路由 | 2.0.6以上版本 | +| return | <methodName>.return | boolean | 可选 | true | 性能调优 | 方法调用是否需要返回值,async设置为true时才生效,如果设置为true,则返回future,或回调onreturn等方法,如果设置为false,则请求发送成功后直接返回Null | 2.0.6以上版本 | +| oninvoke | attribute属性,不在URL中体现 | String | 可选 | | 性能调优 | 方法执行前拦截 | 2.0.6以上版本 | +| onreturn | attribute属性,不在URL中体现 | String | 可选 | | 性能调优 | 方法执行返回后拦截 | 2.0.6以上版本 | +| onthrow | attribute属性,不在URL中体现 | String | 可选 | | 性能调优 | 方法执行有异常拦截 | 2.0.6以上版本 | +| cache | <methodName>.cache | string/boolean | 可选 | | 服务治理 | 以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等 | Dubbo2.1.0及其以上版本支持 | +| validation | <methodName>.validation | boolean | 可选 | | 服务治理 | 是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验 | Dubbo2.1.0及其以上版本支持 | + +比如: + +```xml + + + +``` + +# dubbo:module + +模块信息配置。对应的配置类 `org.apache.dubbo.config.ModuleConfig` + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| name | module | string | 必填 | | 服务治理 | 当前模块名称,用于注册中心计算模块间依赖关系 | 2.2.0以上版本 | +| version | module.version | string | 可选 | | 服务治理 | 当前模块的版本 | 2.2.0以上版本 | +| owner | owner | string | 可选 | | 服务治理 | 模块负责人,用于服务治理,请填写负责人公司邮箱前缀 | 2.2.0以上版本 | +| organization | organization | string | 可选 | | 服务治理 | 组织名称(BU或部门),用于注册中心区分服务来源,此配置项建议不要使用autoconfig,直接写死在配置中,比如china,intl,itu,crm,asc,dw,aliexpress等 | 2.2.0以上版本 | + + +# dubbo:monitor + +监控中心配置。对应的配置类: `org.apache.dubbo.config.MonitorConfig` + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| protocol | protocol | string | 可选 | dubbo | 服务治理 | 监控中心协议,如果为protocol="registry",表示从注册中心发现监控中心地址,否则直连监控中心。 | 2.0.9以上版本 | +| address | <url> | string | 可选 | N/A | 服务治理 | 直连监控中心服务器地址,address="10.20.130.230:12080" | 1.0.16以上版本 | + + +# dubbo:parameter + +选项参数配置。对应的配置类:`java.util.Map`。同时该标签为``或``或``或``或``的子标签,用于配置自定义参数,该配置项将作为扩展点设置自定义参数使用。 + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| key | key | string | 必填 | | 服务治理 | 路由参数键 | 2.0.0以上版本 | +| value | value | string | 必填 | | 服务治理 | 路由参数值 | 2.0.0以上版本 | + +比如: + +```xml + + + +``` + +也可以: + +```xml + +``` + +# dubbo:protocol + + +服务提供者协议配置。对应的配置类: `org.apache.dubbo.config.ProtocolConfig`。同时,如果需要支持多协议,可以声明多个 `` 标签,并在 `` 中通过 `protocol` 属性指定使用的协议。 + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| id | | string | 可选 | dubbo | 配置关联 | 协议BeanId,可以在<dubbo:service protocol="">中引用此ID,如果ID不填,缺省和name属性值一样,重复则在name后加序号。 | 2.0.5以上版本 | +| name | <protocol> | string | 必填 | dubbo | 性能调优 | 协议名称 | 2.0.5以上版本 | +| port | <port> | int | 可选 | dubbo协议缺省端口为20880,rmi协议缺省端口为1099,http和hessian协议缺省端口为80;如果没有配置port,则自动采用默认端口,如果配置为-1,则会分配一个没有被占用的端口。Dubbo 2.4.0+,分配的端口在协议缺省端口的基础上增长,确保端口段可控。 | 服务发现 | 服务端口 | 2.0.5以上版本 | +| host | <host> | string | 可选 | 自动查找本机IP | 服务发现 | -服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,-建议不要配置,让Dubbo自动获取本机IP | 2.0.5以上版本 | +| threadpool | threadpool | string | 可选 | fixed | 性能调优 | 线程池类型,可选:fixed/cached | 2.0.5以上版本 | +| threads | threads | int | 可选 | 200 | 性能调优 | 服务线程池大小(固定大小) | 2.0.5以上版本 | +| iothreads | threads | int | 可选 | cpu个数+1 | 性能调优 | io线程池大小(固定大小) | 2.0.5以上版本 | +| accepts | accepts | int | 可选 | 0 | 性能调优 | 服务提供方最大可接受连接数 | 2.0.5以上版本 | +| payload | payload | int | 可选 | 8388608(=8M) | 性能调优 | 请求及响应数据包大小限制,单位:字节 | 2.0.5以上版本 | +| codec | codec | string | 可选 | dubbo | 性能调优 | 协议编码方式 | 2.0.5以上版本 | +| serialization | serialization | string | 可选 | dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json | 性能调优 | 协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json等 | 2.0.5以上版本 | +| accesslog | accesslog | string/boolean | 可选 | | 服务治理 | 设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件 | 2.0.5以上版本 | +| path | <path> | string | 可选 | | 服务发现 | 提供者上下文路径,为服务path的前缀 | 2.0.5以上版本 | +| transporter | transporter | string | 可选 | dubbo协议缺省为netty | 性能调优 | 协议的服务端和客户端实现类型,比如:dubbo协议的mina,netty等,可以分拆为server和client配置 | 2.0.5以上版本 | +| server | server | string | 可选 | dubbo协议缺省为netty,http协议缺省为servlet | 性能调优 | 协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等 | 2.0.5以上版本 | +| client | client | string | 可选 | dubbo协议缺省为netty | 性能调优 | 协议的客户端实现类型,比如:dubbo协议的mina,netty等 | 2.0.5以上版本 | +| dispatcher | dispatcher | string | 可选 | dubbo协议缺省为all | 性能调优 | 协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等 | 2.1.0以上版本 | +| queues | queues | int | 可选 | 0 | 性能调优 | 线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。 | 2.0.5以上版本 | +| charset | charset | string | 可选 | UTF-8 | 性能调优 | 序列化编码 | 2.0.5以上版本 | +| buffer | buffer | int | 可选 | 8192 | 性能调优 | 网络读写缓冲区大小 | 2.0.5以上版本 | +| heartbeat | heartbeat | int | 可选 | 0 | 性能调优 | 心跳间隔,对于长连接,当物理层断开时,比如拔网线,TCP的FIN消息来不及发送,对方收不到断开事件,此时需要心跳来帮助检查连接是否已断开 | 2.0.10以上版本 | +| telnet | telnet | string | 可选 | | 服务治理 | 所支持的telnet命令,多个命令用逗号分隔 | 2.0.5以上版本 | +| register | register | boolean | 可选 | true | 服务治理 | 该协议的服务是否注册到注册中心 | 2.0.8以上版本 | +| contextpath | contextpath | String | 可选 | 缺省为空串 | 服务治理 | | 2.0.6以上版本 | + + +# dubbo:provider + + +服务提供者缺省值配置。对应的配置类: `org.apache.dubbo.config.ProviderConfig`。同时该标签为 `` 和 `` 标签的缺省值设置。 + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| id | | string | 可选 | dubbo | 配置关联 | 协议BeanId,可以在<dubbo:service proivder="">中引用此ID | 1.0.16以上版本 | +| protocol | <protocol> | string | 可选 | dubbo | 性能调优 | 协议名称 | 1.0.16以上版本 | +| host | <host> | string | 可选 | 自动查找本机IP | 服务发现 | 服务主机名,多网卡选择或指定VIP及域名时使用,为空则自动查找本机IP,建议不要配置,让Dubbo自动获取本机IP | 1.0.16以上版本 | +| threads | threads | int | 可选 | 200 | 性能调优 | 服务线程池大小(固定大小) | 1.0.16以上版本 | +| payload | payload | int | 可选 | 8388608(=8M) | 性能调优 | 请求及响应数据包大小限制,单位:字节 | 2.0.0以上版本 | +| path | <path> | string | 可选 | | 服务发现 | 提供者上下文路径,为服务path的前缀 | 2.0.0以上版本 | +| server | server | string | 可选 | dubbo协议缺省为netty,http协议缺省为servlet | 性能调优 | 协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等 | 2.0.0以上版本 | +| client | client | string | 可选 | dubbo协议缺省为netty | 性能调优 | 协议的客户端实现类型,比如:dubbo协议的mina,netty等 | 2.0.0以上版本 | +| codec | codec | string | 可选 | dubbo | 性能调优 | 协议编码方式 | 2.0.0以上版本 | +| serialization | serialization | string | 可选 | dubbo协议缺省为hessian2,rmi协议缺省为java,http协议缺省为json | 性能调优 | 协议序列化方式,当协议支持多种序列化方式时使用,比如:dubbo协议的dubbo,hessian2,java,compactedjava,以及http协议的json,xml等 | 2.0.5以上版本 | +| default | | boolean | 可选 | false | 配置关联 | 是否为缺省协议,用于多协议 | 1.0.16以上版本 | +| filter | service.filter | string | 可选 | | 性能调优 | 服务提供方远程调用过程拦截器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| listener | exporter.listener | string | 可选 | | 性能调优 | 服务提供方导出服务监听器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| threadpool | threadpool | string | 可选 | fixed | 性能调优 | 线程池类型,可选:fixed/cached/limit(2.5.3以上)/eager(2.6.x以上) | 2.0.5以上版本 | +| accepts | accepts | int | 可选 | 0 | 性能调优 | 服务提供者最大可接受连接数 | 2.0.5以上版本 | +| version | version | string | 可选 | 0.0.0 | 服务发现 | 服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级 | 2.0.5以上版本 | +| group | group | string | 可选 | | 服务发现 | 服务分组,当一个接口有多个实现,可以用分组区分 | 2.0.5以上版本 | +| delay | delay | int | 可选 | 0 | 性能调优 | 延迟注册服务时间(毫秒)- ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务 | 2.0.5以上版本 | +| timeout | default.timeout | int | 可选 | 1000 | 性能调优 | 远程服务调用超时时间(毫秒) | 2.0.5以上版本 | +| retries | default.retries | int | 可选 | 2 | 性能调优 | 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0 | 2.0.5以上版本 | +| connections | default.connections | int | 可选 | 0 | 性能调优 | 对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数 | 2.0.5以上版本 | +| loadbalance | default.loadbalance | string | 可选 | random | 性能调优 | 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用 | 2.0.5以上版本 | +| async | default.async | boolean | 可选 | false | 性能调优 | 是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程 | 2.0.5以上版本 | +| stub | stub | boolean | 可选 | false | 服务治理 | 设为true,表示使用缺省代理类名,即:接口名 + Local后缀。 | 2.0.5以上版本 | +| mock | mock | boolean | 可选 | false | 服务治理 | 设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀。 | 2.0.5以上版本 | +| token | token | boolean | 可选 | false | 服务治理 | 令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌 | 2.0.5以上版本 | +| registry | registry | string | 可选 | 缺省向所有registry注册 | 配置关联 | 向指定注册中心注册,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A | 2.0.5以上版本 | +| dynamic | dynamic | boolean | 可选 | true | 服务治理 | 服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。 | 2.0.5以上版本 | +| accesslog | accesslog | string/boolean | 可选 | false | 服务治理 | 设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件 | 2.0.5以上版本 | +| owner | owner | string | 可选 | | 服务治理 | 服务负责人,用于服务治理,请填写负责人公司邮箱前缀 | 2.0.5以上版本 | +| document | document | string | 可选 | | 服务治理 | 服务文档URL | 2.0.5以上版本 | +| weight | weight | int | 可选 | | 性能调优 | 服务权重 | 2.0.5以上版本 | +| executes | executes | int | 可选 | 0 | 性能调优 | 服务提供者每服务每方法最大可并行执行请求数 | 2.0.5以上版本 | +| actives | default.actives | int | 可选 | 0 | 性能调优 | 每服务消费者每服务每方法最大并发调用数 | 2.0.5以上版本 | +| proxy | proxy | string | 可选 | javassist | 性能调优 | 生成动态代理方式,可选:jdk/javassist | 2.0.5以上版本 | +| cluster | default.cluster | string | 可选 | failover | 性能调优 | 集群方式,可选:failover/failfast/failsafe/failback/forking | 2.0.5以上版本 | +| deprecated | deprecated | boolean | 可选 | false | 服务治理 | 服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志 | 2.0.5以上版本 | +| queues | queues | int | 可选 | 0 | 性能调优 | 线程池队列大小,当线程池满时,排队等待执行的队列大小,建议不要设置,当线程池满时应立即失败,重试其它服务提供机器,而不是排队,除非有特殊需求。 | 2.0.5以上版本 | +| charset | charset | string | 可选 | UTF-8 | 性能调优 | 序列化编码 | 2.0.5以上版本 | +| buffer | buffer | int | 可选 | 8192 | 性能调优 | 网络读写缓冲区大小 | 2.0.5以上版本 | +| iothreads | iothreads | int | 可选 | CPU + 1 | 性能调优 | IO线程池,接收网络读写中断,以及序列化和反序列化,不处理业务,业务线程池参见threads配置,此线程池和CPU相关,不建议配置。 | 2.0.5以上版本 | +| telnet | telnet | string | 可选 | | 服务治理 | 所支持的telnet命令,多个命令用逗号分隔 | 2.0.5以上版本 | +| <dubbo:service> | contextpath | contextpath | String | 可选 | 缺省为空串 | 服务治理 | | 2.0.6以上版本 | +| layer | layer | string | 可选 | | 服务治理 | 服务提供者所在的分层。如:biz、dao、intl:web、china:acton。 | 2.0.7以上版本 | + + +# dubbo:reference + + +服务消费者引用服务配置。对应的配置类: `org.apache.dubbo.config.ReferenceConfig` + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| id | | string | 必填 | | 配置关联 | 服务引用BeanId | 1.0.0以上版本 | +| interface | | class | 必填 | | 服务发现 | 服务接口名 | 1.0.0以上版本 | +| version | version | string | 可选 | | 服务发现 | 服务版本,与服务提供者的版本一致 | 1.0.0以上版本 | +| group | group | string | 可选 | | 服务发现 | 服务分组,当一个接口有多个实现,可以用分组区分,必需和服务提供方一致 | 1.0.7以上版本 | +| timeout | timeout | long | 可选 | 缺省使用<dubbo:consumer>的timeout | 性能调优 | 服务方法调用超时时间(毫秒) | 1.0.5以上版本 | +| retries | retries | int | 可选 | 缺省使用<dubbo:consumer>的retries | 性能调优 | 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0 | 2.0.0以上版本 | +| connections | connections | int | 可选 | 缺省使用<dubbo:consumer>的connections | 性能调优 | 对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数 | 2.0.0以上版本 | +| loadbalance | loadbalance | string | 可选 | 缺省使用<dubbo:consumer>的loadbalance | 性能调优 | 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用 | 2.0.0以上版本 | +| async | async | boolean | 可选 | 缺省使用<dubbo:consumer>的async | 性能调优 | 是否异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程 | 2.0.0以上版本 | +| generic | generic | boolean | 可选 | 缺省使用<dubbo:consumer>的generic | 服务治理 | 是否缺省泛化接口,如果为泛化接口,将返回GenericService | 2.0.0以上版本 | +| check | check | boolean | 可选 | 缺省使用<dubbo:consumer>的check | 服务治理 | 启动时检查提供者是否存在,true报错,false忽略 | 2.0.0以上版本 | +| url | url | string | 可选 | | 服务治理 | 点对点直连服务提供者地址,将绕过注册中心 | 1.0.6以上版本 | +| stub | stub | class/boolean | 可选 | | 服务治理 | 服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService) | 2.0.0以上版本 | +| mock | mock | class/boolean | 可选 | | 服务治理 | 服务接口调用失败Mock实现类名,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。 | Dubbo1.0.13及其以上版本支持 | +| cache | cache | string/boolean | 可选 | | 服务治理 | 以调用参数为key,缓存返回结果,可选:lru, threadlocal, jcache等 | Dubbo2.1.0及其以上版本支持 | +| validation | validation | boolean | 可选 | | 服务治理 | 是否启用JSR303标准注解验证,如果启用,将对方法参数上的注解进行校验 | Dubbo2.1.0及其以上版本支持 | +| proxy | proxy | boolean | 可选 | javassist | 性能调优 | 选择动态代理实现策略,可选:javassist, jdk | 2.0.2以上版本 | +| client | client | string | 可选 | | 性能调优 | 客户端传输类型设置,如Dubbo协议的netty或mina。 | Dubbo2.0.0以上版本支持 | +| registry | | string | 可选 | 缺省将从所有注册中心获服务列表后合并结果 | 配置关联 | 从指定注册中心注册获取服务列表,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔 | 2.0.0以上版本 | +| owner | owner | string | 可选 | | 服务治理 | 调用服务负责人,用于服务治理,请填写负责人公司邮箱前缀 | 2.0.5以上版本 | +| actives | actives | int | 可选 | 0 | 性能调优 | 每服务消费者每服务每方法最大并发调用数 | 2.0.5以上版本 | +| cluster | cluster | string | 可选 | failover | 性能调优 | 集群方式,可选:failover/failfast/failsafe/failback/forking | 2.0.5以上版本 | +| filter | reference.filter | string | 可选 | default | 性能调优 | 服务消费方远程调用过程拦截器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| listener | invoker.listener | string | 可选 | default | 性能调优 | 服务消费方引用服务监听器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| layer | layer | string | 可选 | | 服务治理 | 服务调用者所在的分层。如:biz、dao、intl:web、china:acton。 | 2.0.7以上版本 | +| init | init | boolean | 可选 | false | 性能调优 | 是否在afterPropertiesSet()时饥饿初始化引用,否则等到有人注入或引用该实例时再初始化。 | 2.0.10以上版本 | +| protocol | protocol | string | 可选 | | 服务治理 | 只调用指定协议的服务提供方,其它协议忽略。 | 2.2.0以上版本 | + + +# dubbo:registry + + +注册中心配置。对应的配置类: `org.apache.dubbo.config.RegistryConfig`。同时如果有多个不同的注册中心,可以声明多个 `` 标签,并在 `` 或 `` 的 `registry` 属性指定使用的注册中心。 + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| id | | string | 可选 | | 配置关联 | 注册中心引用BeanId,可以在<dubbo:service registry="">或<dubbo:reference registry="">中引用此ID | 1.0.16以上版本 | +| address | <host:port> | string | 必填 | | 服务发现 | 注册中心服务器地址,如果地址没有端口缺省为9090,同一集群内的多个地址用逗号分隔,如:ip:port,ip:port,不同集群的注册中心,请配置多个<dubbo:registry>标签 | 1.0.16以上版本 | +| protocol | <protocol> | string | 可选 | dubbo | 服务发现 | 注册中心地址协议,支持`dubbo`, `multicast`, `zookeeper`, `redis`, `consul(2.7.1)`, `sofa(2.7.2)`, `etcd(2.7.2)`, `nacos(2.7.2)`等协议 | 2.0.0以上版本 | +| port | <port> | int | 可选 | 9090 | 服务发现 | 注册中心缺省端口,当address没有带端口时使用此端口做为缺省值 | 2.0.0以上版本 | +| username | <username> | string | 可选 | | 服务治理 | 登录注册中心用户名,如果注册中心不需要验证可不填 | 2.0.0以上版本 | +| password | <password> | string | 可选 | | 服务治理 | 登录注册中心密码,如果注册中心不需要验证可不填 | 2.0.0以上版本 | +| transport | registry.transporter | string | 可选 | netty | 性能调优 | 网络传输方式,可选mina,netty | 2.0.0以上版本 | +| timeout | registry.timeout | int | 可选 | 5000 | 性能调优 | 注册中心请求超时时间(毫秒) | 2.0.0以上版本 | +| session | registry.session | int | 可选 | 60000 | 性能调优 | 注册中心会话超时时间(毫秒),用于检测提供者非正常断线后的脏数据,比如用心跳检测的实现,此时间就是心跳间隔,不同注册中心实现不一样。 | 2.1.0以上版本 | +| file | registry.file | string | 可选 | | 服务治理 | 使用文件缓存注册中心地址列表及服务提供者列表,应用重启时将基于此文件恢复,注意:两个注册中心不能使用同一文件存储 | 2.0.0以上版本 | +| wait | registry.wait | int | 可选 | 0 | 性能调优 | 停止时等待通知完成时间(毫秒) | 2.0.0以上版本 | +| check | check | boolean | 可选 | true | 服务治理 | 注册中心不存在时,是否报错 | 2.0.0以上版本 | +| register | register | boolean | 可选 | true | 服务治理 | 是否向此注册中心注册服务,如果设为false,将只订阅,不注册 | 2.0.5以上版本 | +| subscribe | subscribe | boolean | 可选 | true | 服务治理 | 是否向此注册中心订阅服务,如果设为false,将只注册,不订阅 | 2.0.5以上版本 | +| dynamic | dynamic | boolean | 可选 | true | 服务治理 | 服务是否动态注册,如果设为false,注册后将显示为disable状态,需人工启用,并且服务提供者停止时,也不会自动取消注册,需人工禁用。 | 2.0.5以上版本 | +| group | group | string | 可选 | dubbo | 服务治理 | 服务注册分组,跨组的服务不会相互影响,也无法相互调用,适用于环境隔离。 | 2.0.5以上版本 | +| simplified | simplified | boolean | 可选 | false | 服务治理 | 注册到注册中心的URL是否采用精简模式的(与低版本兼容) | 2.7.0以上版本 | +| extra-keys | extraKeys | string | 可选 | | 服务治理 | 在simplified=true时,extraKeys允许你在默认参数外将额外的key放到URL中,格式:"interface,key1,key2"。 | 2.7.0以上版本 | + +# dubbo:service + +服务提供者暴露服务配置。对应的配置类:`org.apache.dubbo.config.ServiceConfig` + +| 属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 | +| --- | --- | ---- | --- | --- | --- | --- | --- | +| interface | | class | 必填 | | 服务发现 | 服务接口名 | 1.0.0以上版本 | +| ref | | object | 必填 | | 服务发现 | 服务对象实现引用 | 1.0.0以上版本 | +| version | version | string | 可选 | 0.0.0 | 服务发现 | 服务版本,建议使用两位数字版本,如:1.0,通常在接口不兼容时版本号才需要升级 | 1.0.0以上版本 | +| group | group | string | 可选 | | 服务发现 | 服务分组,当一个接口有多个实现,可以用分组区分 | 1.0.7以上版本 | +| path | <path> | string | 可选 | 缺省为接口名 | 服务发现 | 服务路径 (注意:1.0不支持自定义路径,总是使用接口名,如果有1.0调2.0,配置服务路径可能不兼容) | 1.0.12以上版本 | +| delay | delay | int | 可选 | 0 | 性能调优 | 延迟注册服务时间(毫秒) ,设为-1时,表示延迟到Spring容器初始化完成时暴露服务 | 1.0.14以上版本 | +| timeout | timeout | int | 可选 | 1000 | 性能调优 | 远程服务调用超时时间(毫秒) | 2.0.0以上版本 | +| retries | retries | int | 可选 | 2 | 性能调优 | 远程服务调用重试次数,不包括第一次调用,不需要重试请设为0 | 2.0.0以上版本 | +| connections | connections | int | 可选 | 100 | 性能调优 | 对每个提供者的最大连接数,rmi、http、hessian等短连接协议表示限制连接数,dubbo等长连接协表示建立的长连接个数 | 2.0.0以上版本 | +| loadbalance | loadbalance | string | 可选 | random | 性能调优 | 负载均衡策略,可选值:random,roundrobin,leastactive,分别表示:随机,轮询,最少活跃调用 | 2.0.0以上版本 | +| async | async | boolean | 可选 | false | 性能调优 | 是否缺省异步执行,不可靠异步,只是忽略返回值,不阻塞执行线程 | 2.0.0以上版本 | +| local | local | class/boolean | 可选 | false | 服务治理 | 设为true,表示使用缺省代理类名,即:接口名 + Local后缀,已废弃,请使用stub| 2.0.0以上版本 | +| stub | stub | class/boolean | 可选 | false | 服务治理 | 设为true,表示使用缺省代理类名,即:接口名 + Stub后缀,服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceStub(XxxService xxxService) | 2.0.0以上版本 | +| mock | mock | class/boolean | 可选 | false | 服务治理 | 设为true,表示使用缺省Mock类名,即:接口名 + Mock后缀,服务接口调用失败Mock实现类,该Mock类必须有一个无参构造函数,与Local的区别在于,Local总是被执行,而Mock只在出现非业务异常(比如超时,网络异常等)时执行,Local在远程调用之前执行,Mock在远程调用后执行。 | 2.0.0以上版本 | +| token | token | string/boolean | 可选 | false | 服务治理 | 令牌验证,为空表示不开启,如果为true,表示随机生成动态令牌,否则使用静态令牌,令牌的作用是防止消费者绕过注册中心直接访问,保证注册中心的授权功能有效,如果使用点对点调用,需关闭令牌功能 | 2.0.0以上版本 | +| registry | | string | 可选 | 缺省向所有registry注册 | 配置关联 | 向指定注册中心注册,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔,如果不想将该服务注册到任何registry,可将值设为N/A | 2.0.0以上版本 | +| provider | | string | 可选 | 缺省使用第一个provider配置 | 配置关联 | 指定provider,值为<dubbo:provider>的id属性 | 2.0.0以上版本 | +| deprecated | deprecated | boolean | 可选 | false | 服务治理 | 服务是否过时,如果设为true,消费方引用时将打印服务过时警告error日志 | 2.0.5以上版本 | +| dynamic | dynamic | boolean | 可选 | true | 服务治理 | 服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。 | 2.0.5以上版本 | +| accesslog | accesslog | string/boolean | 可选 | false | 服务治理 | 设为true,将向logger中输出访问日志,也可填写访问日志文件路径,直接把访问日志输出到指定文件 | 2.0.5以上版本 | +| owner | owner | string | 可选 | | 服务治理 | 服务负责人,用于服务治理,请填写负责人公司邮箱前缀 | 2.0.5以上版本 | +| document | document | string | 可选 | | 服务治理 | 服务文档URL | 2.0.5以上版本 | +| weight | weight | int | 可选 | | 性能调优 | 服务权重 | 2.0.5以上版本 | +| executes | executes | int | 可选 | 0 | 性能调优 | 服务提供者每服务每方法最大可并行执行请求数 | 2.0.5以上版本 | | actives | actives | int | 可选 | 0 | 性能调优 | 每服务消费者每服务每方法最大并发调用数 | 2.0.5以上版本 | +| proxy | proxy | string | 可选 | javassist | 性能调优 | 生成动态代理方式,可选:jdk/javassist | 2.0.5以上版本 | +| cluster | cluster | string | 可选 | failover | 性能调优 | 集群方式,可选:failover/failfast/failsafe/failback/forking | 2.0.5以上版本 | +| filter | service.filter | string | 可选 | default | 性能调优 | 服务提供方远程调用过程拦截器名称,多个名称用逗号分隔 | 2.0.5以上版本 | +| listener | exporter.listener | string | 可选 | default | 性能调优 | 服务提供方导出服务监听器名称,多个名称用逗号分隔 | | +| protocol | | string | 可选 | | 配置关联 | 使用指定的协议暴露服务,在多协议时使用,值为<dubbo:protocol>的id属性,多个协议ID用逗号分隔 | 2.0.5以上版本 | +| layer | layer | string | 可选 | | 服务治理 | 服务提供者所在的分层。如:biz、dao、intl:web、china:acton。 | 2.0.7以上版本 | +| register | register | boolean | 可选 | true | 服务治理 | 该协议的服务是否注册到注册中心 | 2.0.8以上版本 | diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/xml/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config/xml/guide.md new file mode 100644 index 000000000000..ad60f98fbbeb --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/xml/guide.md @@ -0,0 +1,98 @@ +--- +type: docs +title: "配置指南" +linkTitle: "配置指南" +weight: 1 +description: "以 XML 配置的方式来配置你的 Dubbo 应用" +--- + +有关 XML 的详细配置项,请参见:[XML配置参考手册](../../../references/xml)。如果不想使用 Spring 配置,而希望通过 API 的方式进行调用,请参见:[API配置](../api)。想知道如何使用配置,请参见:[快速启动](../../../quick-start)。 + +请在此查看文档描述的[完整示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-basic) + +## provider.xml 示例 + +```xml + + + + + + + +``` + +## consumer.xml示例 + +```xml + + + + + +``` + +所有标签都支持自定义参数,用于不同扩展点实现的特殊配置,如: + +```xml + + + +``` + +或: [^1] + +```xml + + + +``` + +## 配置之间的关系 + +![dubbo-config](/imgs/user/dubbo-config.jpg) + +标签 | 用途 | 解释 +------------- | ------------- | ------------- +`` | 服务配置 | 用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心 +`` [^2] | 引用配置 | 用于创建一个远程服务代理,一个引用可以指向多个注册中心 +`` | 协议配置 | 用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受 +`` | 应用配置 | 用于配置当前应用信息,不管该应用是提供者还是消费者 +`` | 模块配置 | 用于配置当前模块信息,可选 +`` | 注册中心配置 | 用于配置连接注册中心相关信息 +`` | 监控中心配置 | 用于配置连接监控中心相关信息,可选 +`` | 提供方配置 | 当 ProtocolConfig 和 ServiceConfig 某属性没有配置时,采用此缺省值,可选 +`` | 消费方配置 | 当 ReferenceConfig 某属性没有配置时,采用此缺省值,可选 +`` | 方法配置 | 用于 ServiceConfig 和 ReferenceConfig 指定方法级的配置信息 +`` | 参数配置 | 用于指定方法参数配置 + + +## 不同粒度配置的覆盖关系 + +以 timeout 为例,下图显示了配置的查找顺序,其它 retries, loadbalance, actives 等类似: + +* 方法级优先,接口级次之,全局配置再次之。 +* 如果级别一样,则消费方优先,提供方次之。 + +其中,服务提供方配置,通过 URL 经由注册中心传递给消费方。 + +![dubbo-config-override](/imgs/user/dubbo-config-override.jpg) + +(建议由服务提供方设置超时,因为一个方法需要执行多长时间,服务提供方更清楚,如果一个消费方同时引用多个服务,就不需要关心每个服务的超时设置)。 + +理论上 ReferenceConfig 中除了`interface`这一项,其他所有配置项都可以缺省不配置,框架会自动使用ConsumerConfig,ServiceConfig, ProviderConfig等提供的缺省配置。 + +[^1]: `2.1.0` 开始支持,注意声明:`xmlns:p="http://www.springframework.org/schema/p"` +[^2]: 引用缺省是延迟初始化的,只有引用被注入到其它 Bean,或被 `getBean()` 获取,才会初始化。如果需要饥饿加载,即没有人引用也立即生成动态代理,可以配置:`` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/_index.md new file mode 100644 index 000000000000..7a20d81c2f95 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "YAML 配置手册" +linkTitle: "YAML 配置手册" +weight: 4 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/description.md b/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/description.md new file mode 100644 index 000000000000..b82b3289ac85 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/description.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "配置项描述" +linkTitle: "配置项描述" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/guide.md new file mode 100644 index 000000000000..60114cfb88b1 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/config/yaml/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "配置指南" +linkTitle: "配置指南" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/graalvm/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/graalvm/_index.md new file mode 100644 index 000000000000..1412a54a3200 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/graalvm/_index.md @@ -0,0 +1,214 @@ +--- +type: docs +title: "Dubbo 集成 Graalvm参考手册" +linkTitle: "Dubbo 集成 Graalvm参考手册" +weight: 9 +description: "" +--- + +dubbo3.0支持native-image文档 + +## 概述 + +本文档将介绍将dubbo3.0项目接入GraalVM,进行native-image编译为二进制的流程。 + +关于GraalVm的更多信息可以阅读 https://www.graalvm.org/docs/getting-started/container-images/ 此文档。 + +## 使用样例 + +在编译我们的dubbo项目之前,需要确保我们正基于graalVm的环境。 + +1. 安装GraalVM + +进入https://www.graalvm.org/ 官网根据自己的系统选取最新版本安装: + +![](/imgs/blog/dubbo3.0-graalvm-support/graalvmgw.jpg) + +安装完成后,修改配置JAVA_HOME的路径,生效后查看本地jdk可以看到如下: + +![](/imgs/blog/dubbo3.0-graalvm-support/graalvm_env.jpg) +这里我们使用的基于jdk1.8版本的GraalVM。 + +- 安装native-image,只需执行gu install native-image即可。 + +1. 拉取dubbo代码,切换到[apache:3.0](https://github.com/apache/dubbo)分支。 +2. 手动执行生成SPI代码。 + +由于目前编译native-image不支持代码动态生成编译,所以有关代码动态生成的部分需要我们手动先生成,这里提供了工具函数: + +![](/imgs/blog/dubbo3.0-graalvm-support/code_generator.jpg) +执行CodeGenerator即可在dubbo-native模块下生成SPI代码。 + +1. 在根目录下执行install + +``` +MacdeMacBook-pro-3:incubator-dubbo mac$ pwd + +/Users/mac/Documents/Mi/project/incubator-dubbo + +MacdeMacBook-pro-3:incubator-dubbo mac$ mvn clean package install -Dmaven.test.skip=true +``` + +1. 编译demo项目 + +这里我们提供了可直接进行编译的示例项目,dubbo-demo/dubbo-demo-native。上面步骤install完成后,先到dubbo-demo-native的provider下,执行native-image编译: + +``` + mvn clean package -P native -Dmaven.test.skip=true +``` + +这里由于我们在maven中引入了native-image插件,所以直接-P native即可执行该插件。 + +![](/imgs/blog/dubbo3.0-graalvm-support/native_image_build.jpg) +编译成功后可以在target下看到已经生成的二进制文件,本地启动一个zookeeper,直接执行该二进制,可见启动成功如下: + +![](/imgs/blog/dubbo3.0-graalvm-support/run_provider.jpg) +consumer端同样执行编译,在consumer的target下也会生成二进制文件:demo-native-consumer,执行该二进制可以看到调用结果如下: + +![](/imgs/blog/dubbo3.0-graalvm-support/run_consumer.jpg) +### 具体步骤 + +实际上在这个demo下我们做了一些工作来确保项目可以编译执行,主要有以下几个步骤 + +- 引入dubbo-native依赖 + +``` + + + org.apache.dubbo + + dubbo-native + + ${project.version} + + +``` + +该模块下有我们生成的SPI代码。 + +- 引入native-image插件 + +``` + + + org.graalvm.nativeimage + + native-image-maven-plugin + + 21.0.0.2 + + + + + + + + native-image + + + + package + + + + + + + + false + + demo-native-provider + + org.apache.dubbo.demo.graalvm.provider.Application + + + + --no-fallback + + --initialize-at-build-time=org.slf4j.MDC + + --initialize-at-build-time=org.slf4j.LoggerFactory + + --initialize-at-build-time=org.slf4j.impl.StaticLoggerBinder + + --initialize-at-build-time=org.apache.log4j.helpers.Loader + + --initialize-at-build-time=org.apache.log4j.Logger + + --initialize-at-build-time=org.apache.log4j.helpers.LogLog + + --initialize-at-build-time=org.apache.log4j.LogManager + + --initialize-at-build-time=org.apache.log4j.spi.LoggingEvent + + --initialize-at-build-time=org.slf4j.impl.Log4jLoggerFactory + + --initialize-at-build-time=org.slf4j.impl.Log4jLoggerAdapter + + --initialize-at-build-time=org.eclipse.collections.api.factory.Sets + + --initialize-at-run-time=io.netty.channel.epoll.Epoll + + --initialize-at-run-time=io.netty.channel.epoll.Native + + --initialize-at-run-time=io.netty.channel.epoll.EpollEventLoop + + --initialize-at-run-time=io.netty.channel.epoll.EpollEventArray + + --initialize-at-run-time=io.netty.channel.DefaultFileRegion + + --initialize-at-run-time=io.netty.channel.kqueue.KQueueEventArray + + --initialize-at-run-time=io.netty.channel.kqueue.KQueueEventLoop + + --initialize-at-run-time=io.netty.channel.kqueue.Native + + --initialize-at-run-time=io.netty.channel.unix.Errors + + --initialize-at-run-time=io.netty.channel.unix.IovArray + + --initialize-at-run-time=io.netty.channel.unix.Limits + + --initialize-at-run-time=io.netty.util.internal.logging.Log4JLogger + + --initialize-at-run-time=io.netty.channel.unix.Socket + + --initialize-at-run-time=io.netty.channel.ChannelHandlerMask + + + + --report-unsupported-elements-at-runtime + + --allow-incomplete-classpath + + --enable-url-protocols=http + + -H:+ReportExceptionStackTraces + + + + + + +``` + +其中定义了生成的镜像名以及一些构建镜像的参数。 + +- 挂载native-image-agent + +由于我们需要将一些反射、JNI等类先指定出来,我们需要先使用该agent以正常方式运行一遍生成这些类的json形式的信息。 + +在启动参数中添加: + +``` +-agentlib:native-image-agent=config-output-dir=/tmp/config/,config-write-period-secs=300,config-write-initial-delay-secs=5 +``` + +以正常方式启动,在项目的resources下建立文件夹META-INF.native-image,把在本地目录中生成的文件粘进去: + +![](/imgs/blog/dubbo3.0-graalvm-support/resources.jpg) +(可能会有缺漏没有生成的类信息,需要根据编译或运行时的报错信息手动添加。) + + + +**完成以上几步后就可以进行项目的编译了。** diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/_index.md new file mode 100644 index 000000000000..f3fad3427b04 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "元数据中心参考手册" +linkTitle: "元数据中心参考手册" +weight: 6 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/_index.md new file mode 100644 index 000000000000..7fed8cf789af --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Nacos" +linkTitle: "Nacos" +weight: 3 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/guide.md new file mode 100644 index 000000000000..13dc994973dd --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/nacos/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/overview/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/overview/_index.md new file mode 100644 index 000000000000..3dece31ac6e3 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/overview/_index.md @@ -0,0 +1,619 @@ +--- +type: docs +title: "元数据中心概述" +linkTitle: "元数据中心概述" +weight: 1 +--- + + +## 背景 + +dubbo provider中的服务配置项有接近[30个配置项](https://dubbo.apache.org/zh/docs/v2.7/user/references/xml/dubbo-provider/)。 排除注册中心服务治理需要之外,很大一部分配置项是provider自己使用,不需要透传给消费者。这部分数据不需要进入注册中心,而只需要以key-value形式持久化存储。 +dubbo consumer中的配置项也有[20+个配置项](https://dubbo.apache.org/zh/docs/v2.7/user/references/xml/dubbo-consumer/)。在注册中心之中,服务消费者列表中只需要关注application,version,group,ip,dubbo版本等少量配置,其他配置也可以以key-value形式持久化存储。 +这些数据是以服务为维度注册进入注册中心,导致了数据量的膨胀,进而引发注册中心(如zookeeper)的网络开销增大,性能降低。 +除了上述配置项的存储之外,dubbo服务元数据信息也需要被存储下来。元数据信息包括服务接口,及接口的方法信息。这些信息将被用于服务mock,服务测试。 + +以上的元数据都是基于接口级别。在3.0版本中,引入了应用元数据的概念,应用元数据描述的是整个应用的信息概览。并且引入了服务自省映射,用于应用级别的服务发现。 + + +## 目标 + +需要将注册中心原来的数据信息和元数据信息保存到独立的key-value的存储中,这个key-value可以是DB,redis或者其他持久化存储。核心代码中支持了zookeeper,redis, nacos(推荐)的默认支持。 +>因为是基于key-value存储,key不会改变,最新的value会将原来的value进行覆盖 + +Provider存储内容的格式,参见:org.apache.dubbo.metadata.definition.model.FullServiceDefinition。是该类型gson化之后的存储。 +Consumer存储内容,为Map格式。从Consumer端注册到注册中心的URL中的获取参数信息。即通过URL.getParameterMap()获取到的Map,进行gson化之后进行存储。 + +详细的内容,可以参考下面的sample输出。 + + + +## 配置 + +默认的元数据存储,额外支持以下几个特性: +* 失败重试 +* 每天定时重刷 + +#### 失败重试 +失败重试可以通过retrytimes (重试次数,默认100),retryperiod(重试周期,默认3000ms)进行设置。 + +#### 定时刷新 +默认开启,可以通过设置cycleReport=false进行关闭。 + +#### 完整的配置项: + +```properties +dubbo.metadata-report.address=zookeeper://127.0.0.1:2181 +dubbo.metadata-report.username=xxx ##非必须 +dubbo.metadata-report.password=xxx ##非必须 +dubbo.metadata-report.retry-times=30 ##非必须,default值100 +dubbo.metadata-report.retry-period=5000 ##非必须,default值3000 +dubbo.metadata-report.cycle-report=false ##非必须,default值true +dubbo.metadata-report.sync.report=false ##非必须,default值为false +``` +> 如果元数据地址(dubbo.metadata-report.address)也不进行配置,会判断注册中心的协议是否支持元数据中心,如果支持,会使用注册中心的地址来用作元数据中心。 + + +接下来看几个sample的配置。无论哪种配置方式,都需要引入maven依赖: + +zookeeper: +```xml + + org.apache.dubbo + dubbo-metadata-report-zookeeper + +``` + +redis: +```xml + + org.apache.dubbo + dubbo-metadata-report-redis + +``` + +nacos: +```xml + + org.apache.dubbo + dubbo-metadata-report-nacos + +``` + + +> **完整的sample,查看[sample-2.7](https://github.com/dubbo/dubbo-samples/tree/master)** + +### 方式一:在配置中心配置 + +参考sample:dubbo-samples-metadata-report/dubbo-samples-metadata-report-configcenter 工程。 + +##### 配置中心配置 + +配置中心的配置,可以参考configcenter的文档。配置的内容如下: + +```properties +dubbo.registry.address=zookeeper://127.0.0.1:2181 +### 注意驼峰式风格 +dubbo.metadata-report.address=zookeeper://127.0.0.1:2181 ###元数据存储的地址 +``` + +在sample中,使用了Zookeeper作为配置中心。启动本地zookeeper服务之后,直接运行:org.apache.dubbo.samples.metadatareport.configcenter.ZKTools 就可以完成写入。 +如果配置中心使用了nacos,apollo,这些产品本身支持ops配置。 + +##### 应用配置 + +```properties +###dubbo.properties +dubbo.config-center.address=zookeeper://127.0.0.1:2181 +... +``` + +完成上述两步之后,注册中心地址、元数据地址将从配置中心进行获取。现在可以依次运行Provider类和Consumer类,会在console中得到对应的输出或者直接通过zookeeper的cli查看。 + +##### Provider配置 + +provider端存储的元数据内容如下: + +```json +{ + "parameters": { + "side": "provider", + "methods": "sayHello", + "dubbo": "2.0.2", + "threads": "100", + "interface": "org.apache.dubbo.samples.metadatareport.configcenter.api.AnnotationService", + "threadpool": "fixed", + "version": "1.1.1", + "generic": "false", + "revision": "1.1.1", + "valid": "true", + "application": "metadatareport-configcenter-provider", + "default.timeout": "5000", + "group": "d-test", + "anyhost": "true" + }, + "canonicalName": "org.apache.dubbo.samples.metadatareport.configcenter.api.AnnotationService", + "codeSource": "file:/Users/cvictory/workspace/work-mw/dubbo-samples/dubbo-samples-metadata-report/dubbo-samples-metadata-report-configcenter/target/classes/", + "methods": [{ + "name": "sayHello", + "parameterTypes": ["java.lang.String"], + "returnType": "java.lang.String" + }], + "types": [{ + "type": "java.lang.String", + "properties": { + "value": { + "type": "char[]" + }, + "hash": { + "type": "int" + } + } + }, { + "type": "int" + }, { + "type": "char" + }] +} + +``` + +provider存储的内容包括了provider服务往注册中心填写的全部参数,以及服务的方法信息(方法名,入参出参的格式)。 + +##### Consumer配置: + +```json +{ + "valid": "true", + "side": "consumer", + "application": "metadatareport-configcenter-consumer", + "methods": "sayHello", + "default.timeout": "6666", + "dubbo": "2.0.2", + "interface": "org.apache.dubbo.samples.metadatareport.configcenter.api.AnnotationService", + "version": "1.1.1", + "revision": "1.1.1", + "group": "d-test" +} +``` + +consumer端存储了consumer往注册中心填写的全部参数。 + + + +上面的例子,主要是将元数据地址放在配置中心,在元数据区存储下来的provider端服务信息和consumer端服务信息的展示。 +接下来的两个例子,主要讲解在工程中配置:xml方式,annotation方式。 + +### 方式二:配置在项目中-properties方式引入配置 + +参考sample:dubbo-samples-metadata-report/dubbo-samples-metadata-report-local-properties工程。 + +##### dubbo.properties + +```properties +dubbo.metadata-report.address=zookeeper://127.0.0.1:2181 +``` + +配置完成这个之后,其余的不用特别关注。也可以直接查看对应的provider和consumer端的服务信息。 + +##### provider存储的某个服务的内容: + +```json +{ + "parameters": { + "valid": "true", + "async": "true", + "side": "provider", + "application": "metadatareport-local-xml-provider", + "methods": "sayHello", + "dubbo": "2.0.2", + "interface": "org.apache.dubbo.samples.metadatareport.local.xml.api.DemoService", + "generic": "false", + "anyhost": "true" + }, + "canonicalName": "org.apache.dubbo.samples.metadatareport.local.xml.api.DemoService", + "codeSource": "file:/Users/cvictory/workspace/work-mw/dubbo-samples/dubbo-samples-metadata-report/dubbo-samples-metadata-report-local-xml/target/classes/", + "methods": [{ + "name": "sayHello", + "parameterTypes": ["java.lang.String"], + "returnType": "java.lang.String" + }], + "types": [{ + "type": "int" + }, { + "type": "char" + }, { + "type": "java.lang.String", + "properties": { + "value": { + "type": "char[]" + }, + "hash": { + "type": "int" + } + } + }] +} + +``` + +##### consumer端存储的内容: + +```json +{ + "valid": "true", + "side": "consumer", + "application": "metadatareport-local-xml-consumer", + "methods": "sayHello", + "dubbo": "2.0.2", + "interface": "org.apache.dubbo.samples.metadatareport.local.xml.api.DemoService" +} + +``` + + + +### 方式三:配置在项目中-annotation方式引入配置 + +参考sample:dubbo-samples-metadata-report/dubbo-samples-metadata-report-local-annotaion工程。 + +##### @Bean 引入bean + +```java +@Bean +public MetadataReportConfig metadataReportConfig() { + MetadataReportConfig metadataReportConfig = new MetadataReportConfig(); + metadataReportConfig.setAddress("zookeeper://127.0.0.1:2181"); + return metadataReportConfig; +} + +``` +引入Bean之后,其余的地方也不需要特别配置。直接查看对应的服务信息: + +##### provider存储的某个服务的内容: + +```json +{ + "parameters": { + "side": "provider", + "methods": "sayHello", + "dubbo": "2.0.2", + "interface": "org.apache.dubbo.samples.metadatareport.local.annotation.api.AnnotationService", + "version": "1.1.8", + "generic": "false", + "revision": "1.1.8", + "valid": "true", + "application": "metadatareport-local-annotaion-provider", + "default.timeout": "1000", + "group": "d-test", + "anyhost": "true" + }, + "canonicalName": "org.apache.dubbo.samples.metadatareport.local.annotation.api.AnnotationService", + "codeSource": "file:/Users/cvictory/workspace/work-mw/dubbo-samples/dubbo-samples-metadata-report/dubbo-samples-metadata-report-local-annotaion/target/classes/", + "methods": [{ + "name": "sayHello", + "parameterTypes": ["java.lang.String"], + "returnType": "java.lang.String" + }], + "types": [{ + "type": "int" + }, { + "type": "java.lang.String", + "properties": { + "value": { + "type": "char[]" + }, + "hash": { + "type": "int" + } + } + }, { + "type": "char" + }] +} +``` + +##### consumer端存储的内容: + +```json +{ + "valid": "true", + "side": "consumer", + "application": "metadatareport-local-annotaion-consumer", + "methods": "sayHello", + "dubbo": "2.0.2", + "interface": "org.apache.dubbo.samples.metadatareport.local.annotation.api.AnnotationService", + "version": "1.1.8", + "revision": "1.1.8", + "group": "d-test" +} +``` + +## 扩展 +### SPI定义 + +参考:org.apache.dubbo.metadata.store.MetadataReportFactory , org.apache.dubbo.metadata.store.MetadataReport + +```java +@SPI("redis") +public interface MetadataReportFactory { + @Adaptive({"protocol"}) + MetadataReport getMetadataReport(URL url); +} +``` + + + +### 自定义元数据的存储 + +下面以Redis存储为例进行说明。 + +新建一个project,需要支持以下修改: + +#### 扩展AbstractMetadataReport + +```java +public class RedisMetadataReport extends AbstractMetadataReport { + private final static Logger logger = LoggerFactory.getLogger(RedisMetadataReport.class); + final JedisPool pool; + + public RedisMetadataReport(URL url) { + super(url); + pool = new JedisPool(new JedisPoolConfig(), url.getHost(), url.getPort()); + } + @Override + protected void doStoreProviderMetadata(ProviderMetadataIdentifier providerMetadataIdentifier, String serviceDefinitions) { + this.storeMetadata(providerMetadataIdentifier, serviceDefinitions); + } + @Override + protected void doStoreConsumerMetadata(ConsumerMetadataIdentifier consumerMetadataIdentifier, String value) { + this.storeMetadata(consumerMetadataIdentifier, value); + } + private void storeMetadata(MetadataIdentifier metadataIdentifier, String v) { + try (Jedis jedis = pool.getResource()) { + jedis.set(metadataIdentifier.getIdentifierKey() + META_DATA_SOTRE_TAG, v); + } catch (Throwable e) { + logger.error("Failed to put " + metadataIdentifier + " to redis " + v + ", cause: " + e.getMessage(), e); + throw new RpcException("Failed to put " + metadataIdentifier + " to redis " + v + ", cause: " + e.getMessage(), e); + } + } +} +``` + +#### 扩展 AbstractMetadataReportFactory + +```java +public class RedisMetadataReportFactory extends AbstractMetadataReportFactory { + @Override + public MetadataReport createMetadataReport(URL url) { + return new RedisMetadataReport(url); + } +} +``` + +#### 增加 MetadataReportFactory + +> META-INF/dubbo/internal/org.apache.dubbo.metadata.store.MetadataReportFactory + +```properties +redis=org.apache.dubbo.metadata.store.redis.RedisMetadataReportFactory +``` + +只要将上面的修改和project打包成jar包,然后配置元数据中心的url:redis://10.20.153.10:6379。 + +至此,一个自定义的元数据存储就可以运行了。 + + +### 数据存储 + +#### 接口级别元数据 + +##### Zookeeper + +```xml + +``` + +Zookeeper 基于树形结构进行数据存储,它的元数据信息位于以下节点: +```text +Provider: /dubbo/metadata/{interface name}/{version}/{group}/provider/{application name} +Consumer: /dubbo/metadata/{interface name}/{version}/{group}/consumer/{application name} +``` + +当 version 或者 group 不存在时,version 路径和 group 路径会取消,路径如下: +```text +Provider: /dubbo/metadata/{interface name}/provider/{application name} +Consumer: /dubbo/metadata/{interface name}/consumer/{application name} +``` + +通过 zkCli get 操作查看数据. + +Provider node: +```shell script +[zk: localhost:2181(CONNECTED) 8] get /dubbo/metadata/org.apache.dubbo.demo.DemoService/provider/demo-provider +{"parameters":{"side":"provider","interface":"org.apache.dubbo.demo.DemoService","metadata-type":"remote","application":"demo-provider","dubbo":"2.0.2","release":"","anyhost":"true","delay":"5000","methods":"sayHello,sayHelloAsync","deprecated":"false","dynamic":"true","timeout":"3000","generic":"false"},"canonicalName":"org.apache.dubbo.demo.DemoService","codeSource":"file:/Users/apple/IdeaProjects/dubbo/dubbo-demo/dubbo-demo-interface/target/classes/","methods":[{"name":"sayHelloAsync","parameterTypes":["java.lang.String"],"returnType":"java.util.concurrent.CompletableFuture"},{"name":"sayHello","parameterTypes":["java.lang.String"],"returnType":"java.lang.String"}],"types":[{"type":"java.util.concurrent.CompletableFuture","properties":{"result":"java.lang.Object","stack":"java.util.concurrent.CompletableFuture.Completion"}},{"type":"java.lang.Object"},{"type":"java.lang.String"},{"type":"java.util.concurrent.CompletableFuture.Completion","properties":{"next":"java.util.concurrent.CompletableFuture.Completion","status":"int"}},{"type":"int"}]} +cZxid = 0x25a9b1 +ctime = Mon Jun 28 21:35:17 CST 2021 +mZxid = 0x25a9b1 +mtime = Mon Jun 28 21:35:17 CST 2021 +pZxid = 0x25a9b1 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 1061 +numChildren = 0 +``` + +Consumer node: +```shell script +[zk: localhost:2181(CONNECTED) 10] get /dubbo/metadata/org.apache.dubbo.demo.DemoService/consumer/demo-consumer +{"side":"consumer","interface":"org.apache.dubbo.demo.DemoService","metadata-type":"remote","application":"demo-consumer","dubbo":"2.0.2","release":"","sticky":"false","check":"false","methods":"sayHello,sayHelloAsync"} +cZxid = 0x25aa24 +ctime = Mon Jun 28 21:57:43 CST 2021 +mZxid = 0x25aa24 +mtime = Mon Jun 28 21:57:43 CST 2021 +pZxid = 0x25aa24 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 219 +numChildren = 0 +``` + + +##### Redis + +```xml + +``` + +在Redis中,使用string数据结构来进行存储元数据信息: +```text +Provider: {service name}:{version}:{group}:provider:{application name} +Consumer: {service name}:{version}:{group}:consumer:{application name} +``` + +当 version 或者 group 不存在时,`:` 依然保留: + +```text +Provider: {service name}:::provider:{application name} +Consumer: {service name}:::consumer:{application name} +``` + +通过 Redis client get key 查看数据. + +Provider key: +```shell script +127.0.0.1:6379> get org.apache.dubbo.demo.DemoService:::provider:demo-provider +"{\"parameters\":{\"side\":\"provider\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"metadata-type\":\"remote\",\"application\":\"demo-provider\",\"dubbo\":\"2.0.2\",\"release\":\"\",\"anyhost\":\"true\",\"delay\":\"5000\",\"methods\":\"sayHello,sayHelloAsync\",\"deprecated\":\"false\",\"dynamic\":\"true\",\"timeout\":\"3000\",\"generic\":\"false\"},\"canonicalName\":\"org.apache.dubbo.demo.DemoService\",\"codeSource\":\"file:/Users/apple/IdeaProjects/dubbo/dubbo-demo/dubbo-demo-interface/target/classes/\",\"methods\":[{\"name\":\"sayHello\",\"parameterTypes\":[\"java.lang.String\"],\"returnType\":\"java.lang.String\"},{\"name\":\"sayHelloAsync\",\"parameterTypes\":[\"java.lang.String\"],\"returnType\":\"java.util.concurrent.CompletableFuture\"}],\"types\":[{\"type\":\"java.util.concurrent.CompletableFuture\",\"properties\":{\"result\":\"java.lang.Object\",\"stack\":\"java.util.concurrent.CompletableFuture.Completion\"}},{\"type\":\"java.lang.Object\"},{\"type\":\"java.lang.String\"},{\"type\":\"java.util.concurrent.CompletableFuture.Completion\",\"properties\":{\"next\":\"java.util.concurrent.CompletableFuture.Completion\",\"status\":\"int\"}},{\"type\":\"int\"}]}" +``` + +Consumer key: +```shell script +127.0.0.1:6379> get org.apache.dubbo.demo.DemoService:::consumer:demo-consumer +"{\"side\":\"consumer\",\"interface\":\"org.apache.dubbo.demo.DemoService\",\"metadata-type\":\"remote\",\"application\":\"demo-consumer\",\"dubbo\":\"2.0.2\",\"release\":\"\",\"sticky\":\"false\",\"check\":\"false\",\"methods\":\"sayHello,sayHelloAsync\"}" +``` + +##### Nacos +```xml + +``` + +在 Nacos 中,本身就存在配置中心这个概念,正好用于元数据存储。在配置中心的场景下,存在命名空间- namespace 的概念,在 namespace 之下,还存在 group 概念。即通过 namespace 和 group 以及 dataId 去定位一个配置项,在不指定 namespace 的情况下,默认使用 `public` 作为默认的命名空间。 + +```text +Provider: namespace: 'public', dataId: '{service name}:{version}:{group}:provider:{application name}', group: 'dubbo' +Consumer: namespace: 'public', dataId: '{service name}:{version}:{group}:consumer:{application name}', group: 'dubbo' +``` + +当 version 或者 group 不存在时,`:` 依然保留: + +```text +Provider: namespace: 'public', dataId: '{service name}:::provider:{application name}', group: 'dubbo' +Consumer: namespace: 'public', dataId: '{service name}:::consumer:{application name}', group: 'dubbo' +``` + +可以通过 Nacos 自带的 web console 界面进行查看. + +Provider data: +![nacos-metadata-report-provider-metadata.png](/imgs/user/nacos-metadata-report-provider-metadata.png) + +Consumer data: +![nacos-metadata-report-consumer-metadata.png](/imgs/user/nacos-metadata-report-consumer-metadata.png) + + +#### 应用级别元数据 +应用级别元数据只有当一个应用定义服务之后,才会进行暴露。会根据当前应用的自身信息,以及接口信息,去计算出该应用的 revision 修订值,用于保存应用级别元数据, + +##### Zookeeper +Zookeeper 的应用级别元数据位于 /dubbo/metadata/{application name}/{revision} + +```shell script +[zk: localhost:2181(CONNECTED) 33] get /dubbo/metadata/demo-provider/da3be833baa2088c5f6776fb7ab1a436 +{"app":"demo-provider","revision":"da3be833baa2088c5f6776fb7ab1a436","services":{"org.apache.dubbo.demo.DemoService:dubbo":{"name":"org.apache.dubbo.demo.DemoService","protocol":"dubbo","path":"org.apache.dubbo.demo.DemoService","params":{"side":"provider","release":"","methods":"sayHello,sayHelloAsync","deprecated":"false","dubbo":"2.0.2","pid":"38298","interface":"org.apache.dubbo.demo.DemoService","service-name-mapping":"true","timeout":"3000","generic":"false","metadata-type":"remote","delay":"5000","application":"demo-provider","dynamic":"true","REGISTRY_CLUSTER":"registry1","anyhost":"true","timestamp":"1626887121829"}},"org.apache.dubbo.demo.RestDemoService:1.0.0:rest":{"name":"org.apache.dubbo.demo.RestDemoService","version":"1.0.0","protocol":"rest","path":"org.apache.dubbo.demo.RestDemoService","params":{"side":"provider","release":"","methods":"getRemoteApplicationName,sayHello,hello,error","deprecated":"false","dubbo":"2.0.2","pid":"38298","interface":"org.apache.dubbo.demo.RestDemoService","service-name-mapping":"true","version":"1.0.0","timeout":"5000","generic":"false","revision":"1.0.0","metadata-type":"remote","delay":"5000","application":"demo-provider","dynamic":"true","REGISTRY_CLUSTER":"registry1","anyhost":"true","timestamp":"1626887120943"}}}} +cZxid = 0x25b336 +ctime = Thu Jul 22 01:05:55 CST 2021 +mZxid = 0x25b336 +mtime = Thu Jul 22 01:05:55 CST 2021 +pZxid = 0x25b336 +cversion = 0 +dataVersion = 0 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 1286 +numChildren = 0 +``` + +#### Redis +Redis 元数据中心目前还不支持应用级别元数据,但已提上日程,会在近期进行实现。 + +##### Nacos +Nacos 应用级别的元数据位于 namespace: 'public', dataId: '{application name}', group: '{revision}' + +![nacos-metadata-report-application-metadata.png](/imgs/user/nacos-metadata-report-application-metadata.png) + + +#### 服务自省映射 - Service Name Mapping +在Dubbo 3.0 中,默认使用了服务自省机制去实现服务发现,关于服务自省可以查看[服务自省](https://mercyblitz.github.io/2020/05/11/Apache-Dubbo-%E6%9C%8D%E5%8A%A1%E8%87%AA%E7%9C%81%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/) + +简而言之,服务自省机制需要能够通过 interface name 去找到对应的 application name,这个关系可以是一对多的,即一个 service name 可能会对应多个不同的 application name。在 3.0 中,元数据中心提供此项映射的能力。 + + +##### Zookeeper +在上面提到,service name 和 application name 可能是一对多的,在 zookeeper 中,使用单个 key-value 进行保存,多个 application name 通过英文逗号`,`隔开。由于是单个 key-value 去保存数据,在多客户端的情况下可能会存在并发覆盖的问题。因此,我们使用 zookeeper 中的版本机制 version 去解决该问题。在 zookeeper 中,每一次对数据进行修改,dataVersion 都会进行增加,我们可以利用 version 这个机制去解决多个客户端同时更新映射的并发问题。不同客户端在更新之前,先去查一次 version,当作本地凭证。在更新时,把凭证 version 传到服务端比对 version, 如果不一致说明在次期间被其他客户端修改过,重新获取凭证再进行重试(CAS)。目前如果重试6次都失败的话,放弃本次更新映射行为。 + +Curator api. +```java +CuratorFramework client = ... +client.setData().withVersion(ticket).forPath(path, dataBytes); +``` + +映射信息位于: +```text +/dubbo/mapping/{service name} +``` + +通过 zkCli get 操作查看数据. + +```shell script +[zk: localhost:2181(CONNECTED) 26] get /dubbo/mapping/org.apache.dubbo.demo.DemoService +demo-provider,two-demo-provider,dubbo-demo-annotation-provider +cZxid = 0x25a80f +ctime = Thu Jun 10 01:36:40 CST 2021 +mZxid = 0x25a918 +mtime = Fri Jun 11 18:46:40 CST 2021 +pZxid = 0x25a80f +cversion = 0 +dataVersion = 2 +aclVersion = 0 +ephemeralOwner = 0x0 +dataLength = 62 +numChildren = 0 +``` + + +##### Redis +Redis 元数据中心目前还不支持服务自省映射,但已提上日程,会在近期进行实现。 + + +##### Nacos +在上面提到,service name 和 application name 可能是一对多的,在 nacos 中,使用单个 key-value 进行保存,多个 application name 通过英文逗号`,`隔开。由于是单个 key-value 去保存数据,在多客户端的情况下可能会存在并发覆盖的问题。因此,我们使用 nacos 中 publishConfigCas 的能力去解决该问题。在 nacos 中,使用 publishConfigCas 会让用户传递一个参数 casMd5,该值的含义是之前配置内容的 md5 值。不同客户端在更新之前,先去查一次 nacos 的 content 的值,计算出 md5 值,当作本地凭证。在更新时,把凭证 md5 传到服务端比对 md5 值, 如果不一致说明在次期间被其他客户端修改过,重新获取凭证再进行重试(CAS)。目前如果重试6次都失败的话,放弃本次更新映射行为。 + +Nacos api: +```java +ConfigService configService = ... +configService.publishConfigCas(key, group, content, ticket); +``` + +映射信息位于 namespace: 'public', dataId: '{service name}', group: 'mapping'. + +![nacos-metadata-report-service-name-mapping.png](/imgs/user/nacos-metadata-report-service-name-mapping.png) + + + + + + + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/_index.md new file mode 100644 index 000000000000..34200f813988 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Redis" +linkTitle: "Redis" +weight: 4 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/guide.md new file mode 100644 index 000000000000..37430b92141b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/guide.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/redis/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/_index.md new file mode 100644 index 000000000000..2864a6f0b145 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Zookeeper" +linkTitle: "Zookeeper" +weight: 2 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/guide.md new file mode 100644 index 000000000000..13dc994973dd --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/metadata-center/zookeeper/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/performance/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/performance/_index.md new file mode 100755 index 000000000000..e3536c13c3e3 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/performance/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "性能参考手册" +linkTitle: "性能参考手册" +weight: 8 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/performance/benchmarking.md b/content/zh/docs3-building/java-sdk/reference-manual/performance/benchmarking.md new file mode 100644 index 000000000000..f5e36dd6a4f3 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/performance/benchmarking.md @@ -0,0 +1,106 @@ +--- +type: docs +title: "基准测试" +linkTitle: "基准测试" +weight: 1 +description: "" +--- + + +## 1 Benchmark 结论 + +对比 2.x 版本,Dubbo3 版本 + +- 服务发现资源利用率显著提升。 + - 对比接口级服务发现,单机常驻内存下降 50%,地址变更期 GC 消耗下降一个数量级 (百次 -> 十次) + - 对比应用级服务发现,单机常驻内存下降 75%,GC 次数趋零 +- Dubbo 协议性能持平,Triple 协议在网关、Stream吞吐量方面更具优势。 + - Dubbo协议 (3.0 vs 2.x),3.0 实现较 2.x 总体 qps rt 持平,略有提升 + - Triple协议 vs Dubbo协议,直连调用场景 Triple 性能并无优势,其优势在网关、Stream调用场景。 + + + +以下是详细压测过程与数据 + +## 2 应用级服务发现(地址推送链路) + +此部分压测数据是由工商银行 Dubbo 团队基于内部生产数据给出,压测过程模拟了“生产环境地址+zookeeper”的服务发现架构。 + +### 2.1 环境 + +| | 描述 | +| ------------ | ------------------------------------------------------------ | +| **压测数据** | 提供者
500运行实例✖️8interface✖️5protocol,即每个提供者向注册中心注册40个URL,总计20000个URL,每个URL字符长度约1k。

注册中心
2个独立zookeeper注册中心,服务提供者消费者采用并行配置。

消费者
配置1c2g,xmx=768,开启GC,从2个注册中心订阅,每5秒调用一次服务。运行20小时。 | +| **压测环境** | Java version "1.8.0"
Java(TM) SE Runtime Enviroment (build pxa6480sr3fp12-20160919_01(SR3 FP12))
IBM J9 VM (Build 2.8, JRE 1.8.0 Linux amd64-64 Compressed References 20160915_318796, JIT enabled, AOT enabled) | + + +### 2.2 数据分析 + +![//imgs/v3/performance/registry-mem.svg](/imgs/v3/performance/registry-mem.svg) + +
图一 服务发现模型内存占用变化

+ +- Dubbo3 接口级服务发现模型,常驻内存较 2.x 版本下降约 50% +- Dubbo3 应用级服务发现模型,常驻内存较 2.x 版本下降约 75% + + +![//imgs/v3/performance/registry-gc.svg](/imgs/v3/performance/registry-gc.svg) + +
图二 服务发现模型 GC 变化

+ +- Dubbo3 接口级服务发现模型,YGC 次数 2.x 版本大幅下降,从数百次下降到十几次 +- Dubbo3 应用级服务发现模型,FGC 次数 2.x 版本大幅下降,从数百次下降到零次 + + + +## 3 RPC 协议(远程调用链路) + +- Dubbo3 的 _Dubbo协议 _实现与 Dubbo2 版本在性能上基本持平。 +- 由于 Triple协议 本身是基于 HTTP/2 构建,因此在单条链路上的 RPC 调用并未比基于 TCP 的 Dubbo2 有提升,反而在某些调用场景出现一定下降。但 _Triple协议 _更大的优势在于网关穿透性、通用性,以及 Stream 通信模型带来的总体吞吐量提升。 +- Triple 预期在网关代理场景下一定会有更好的性能表现,鉴于当前压测环境,本轮 benchmark 暂未提供。 + + + +### 3.1 环境 + + +| | 描述 | +| ------------ | ------------------------------------------------------------ | +| **机器** | 4C8G Linux JDK 1.8(Provider)4C8G Linux JDK 1.8 (Consumer) | +| **压测用例** | RPC 方法类型包括:无参无返回值、普通pojo返回值、pojo列表返回值

2.7 版本 Dubbo 协议(Hessian2 序列化)
3.0 版本 Dubbo 协议(Hessian2 序列化)
3.0 版本 Dubbo 协议(Protobuf 序列化)
3.0 版本 Triple 协议(Protobuf 序列化)
3.0 版本 Triple 协议(Protobuf 套 Hessian2 序列化) | +| **压测方法** | 单链接场景下,消费端起 32 并发线程(当前机器配置 qps rt 较均衡的并发数),持续压后采集压测数据
压测数据通过 https://github.com/apache/dubbo-benchmark 得出 | + +
+ +### 3.2 数据分析 + +| | **Dubbo + Hessian2
2.7** | **Dubbo + Hessian2
3.0** | **Dubbo + Protobuf
3.0** | **Triple + Protobuf
3.0** | **Triple + Protobuf(Hessian)
3.0** | +| ------------------ | ----------------------------- | ----------------------------- | ----------------------------- | ------------------------------ | --------------------------------------- | +| **无参方法** | 30333 ops/s
2.5ms P99 | 30414 ops/s
2.4ms P99 | 24123 ops/s
3.2ms P99 | 7016 ops/s
8.7ms P99 | 6635 ops/s
9.1ms P99 | +| **pojo返回值** | 8984 ops/s
6.1 ms P99 | 12279 ops/s
5.7 ms P99 | 21479 ops/s
3.0 ms P99 | 6255 ops/s
8.9 ms P99 | 6491 ops/s
10 ms P99 | +| **pojo列表返回值** | 1916 ops/s
34 ms P99 | 2037 ops/s
34 ms P99 | 12722 ops/s
7.7 ms P99 | 6920 ops/s
9.6 ms P99 | 2833 ops/s
27 ms P99 | + +#### 3.2.1 Dubbo 协议不同版本实现对比 + +![//imgs/v3/performance/rpc-dubbo.svg](/imgs/v3/performance/rpc-dubbo.svg) + +
图三 Dubbo协议在不同版本的实现对比
+ +- 就 Dubbo RPC + Hessian 的默认组合来说,Dubbo3 与 Dubbo2 在性能上在不同调用场景下基本持平 + +#### 3.2.2 Dubbo协议 vs Triple协议 + +![//imgs/v3/performance/rpc-triple.svg](/imgs/v3/performance/rpc-triple.svg) + +
图四 Triple vs Dubbo
+ +- 单纯看 Consumer <-> Provider 的点对点调用,可以看出 Triple 协议本身并不占优势,同样使用 Protobuf 序列化方式,Dubbo RPC 协议总体性能还是要优于 Triple。

+- Triple 实现在 3.0 版本中将会得到持续优化,但不能完全改变在某些场景下“基于 HTTP/2 的 RPC 协议”对比“基于 TCP 的 RPC 协议”处于劣势的局面 + +#### 3.2.3 补充网关场景 + +TBD

+ +#### 3.3.4 模拟 Stream 通信场景的吞吐量提升 + +TBD diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/_index.md new file mode 100755 index 000000000000..259d37856d28 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "RPC 协议参考手册" +linkTitle: "RPC 协议参考手册" +weight: 4 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/_index.md new file mode 100755 index 000000000000..81d56f7be3d9 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Dubbo协议" +linkTitle: "Dubbo协议" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/guide.md new file mode 100644 index 000000000000..9139650626c4 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/overview.md new file mode 100644 index 000000000000..d44d924d06f6 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/dubbo/overview.md @@ -0,0 +1,121 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- + + +Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。 + +反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。 + +![dubbo-protocol.jpg](/imgs/user/dubbo-protocol.jpg) + +* Transporter: mina, netty, grizzy +* Serialization: dubbo, hessian2, java, json +* Dispatcher: all, direct, message, execution, connection +* ThreadPool: fixed, cached + +## 特性 + +缺省协议,使用基于 netty `3.2.5.Final` 和 hessian2 `3.2.1-fixed-2(Alibaba embed version)` 的 tbremoting 交互。 + +* 连接个数:单连接 +* 连接方式:长连接 +* 传输协议:TCP +* 传输方式:NIO 异步传输 +* 序列化:Hessian 二进制序列化 +* 适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串。 +* 适用场景:常规远程服务方法调用 + +## 约束 + +* 参数及返回值需实现 `Serializable` 接口 +* 参数及返回值不能自定义实现 `List`, `Map`, `Number`, `Date`, `Calendar` 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。 +* Hessian 序列化,只传成员属性值和值的类型,不传方法或静态变量,兼容情况 [^1][^2]: + +| 数据通讯 | 情况 | 结果 | +| ------------- | ------------- | ------------- | +| A->B | 类A多一种 属性(或者说类B少一种 属性)| 不抛异常,A多的那 个属性的值,B没有, 其他正常 | +| A->B | 枚举A多一种 枚举(或者说B少一种 枚举),A使用多 出来的枚举进行传输 | 抛异常 | +| A->B | 枚举A多一种 枚举(或者说B少一种 枚举),A不使用 多出来的枚举进行传输 | 不抛异常,B正常接 收数据 | +| A->B | A和B的属性 名相同,但类型不相同 | 抛异常 | +| A->B | serialId 不相同 | 正常传输 | + +接口增加方法,对客户端无影响,如果该方法不是客户端需要的,客户端不需要重新部署。输入参数和结果集中增加属性,对客户端无影响,如果客户端并不需要新属性,不用重新部署。 + +输入参数和结果集属性名变化,对客户端序列化无影响,但是如果客户端不重新部署,不管输入还是输出,属性名变化的属性值是获取不到的。 + +总结:服务器端和客户端对领域对象并不需要完全一致,而是按照最大匹配原则。 + +## 配置 + +配置协议: + +```xml + +``` + +设置默认协议: + +```xml + +``` + +设置某个服务的协议: + +```xml + +``` + +多端口: + +```xml + + +``` + +配置协议选项: + +```xml + +``` + +多连接配置: + +Dubbo 协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接。 + +```xml + + +``` + +* `` 或 `` 表示该服务使用 JVM 共享长连接。**缺省** +* `` 或 `` 表示该服务使用独立长连接。 +* `` 或`` 表示该服务使用独立两条长连接。 + +为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护。 + +```xml + +``` + +## 常见问题 + +#### 为什么要消费者比提供者个数多? + +因 dubbo 协议采用单一长连接,假设网络为千兆网卡 [^3],根据测试经验数据每条连接最多只能压满 7MByte(不同的环境可能不一样,供参考),理论上 1 个服务提供者需要 20 个服务消费者才能压满网卡。 + +#### 为什么不能传大包? + +因 dubbo 协议采用单一长连接,如果每次请求的数据包大小为 500KByte,假设网络为千兆网卡 [^3],每条连接最大 7MByte(不同的环境可能不一样,供参考),单个服务提供者的 TPS(每秒处理事务数)最大为:128MByte / 500KByte = 262。单个消费者调用单个服务提供者的 TPS(每秒处理事务数)最大为:7MByte / 500KByte = 14。如果能接受,可以考虑使用,否则网络将成为瓶颈。 + +#### 为什么采用异步单一长连接? + +因为服务的现状大都是服务提供者少,通常只有几台机器,而服务的消费者多,可能整个网站都在访问该服务,比如 Morgan 的提供者只有 6 台提供者,却有上百台消费者,每天有 1.5 亿次调用,如果采用常规的 hessian 服务,服务提供者很容易就被压跨,通过单一连接,保证单一消费者不会压死提供者,长连接,减少连接握手验证等,并使用异步 IO,复用线程池,防止 C10K 问题。 + +[^1]: 由吴亚军提供 +[^2]: 总结:会抛异常的情况:枚举值一边多一种,一边少一种,正好使用了差别的那种,或者属性名相同,类型不同 +[^3]: 1024Mbit=128MByte + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/_index.md new file mode 100755 index 000000000000..e4d6450bba8d --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "gRPC协议" +linkTitle: "gRPC协议" +weight: 5 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/guide.md new file mode 100644 index 000000000000..9139650626c4 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/overview.md new file mode 100644 index 000000000000..d0513b1ffee4 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/grpc/overview.md @@ -0,0 +1,23 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- +Dubbo 自 2.7.5 版本开始支持 gRPC 协议,对于计划使用 HTTP/2 通信,或者想利用 gRPC 带来的 Stream、反压、Reactive 编程等能力的开发者来说, +都可以考虑启用 gRPC 协议。 + +## 支持 gRPC 的好处 +* 为期望使用 gRPC 协议的用户带来服务治理能力,方便接入 Dubbo 体系 +* 用户可以使用 Dubbo 风格的,基于接口的编程风格来定义和使用远程服务 + +## 如何在 Dubbo 中使用 gRPC +大概需要以下步骤: +1. 使用 IDL 定义服务 +2. 配置 compiler 插件,本地预编译 +3. 配置暴露/引用 Dubbo 服务 + +具体可参见以下[示例](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-grpc) + +除了原生 StreamObserver 接口类型之外,Dubbo 还支持 [RxJava](https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-grpc/dubbo-samples-rxjava)、[Reactor](https://github.com/apache/dubbo-samples/tree/master/java/dubbo-samples-grpc/dubbo-samples-reactor) 编程风格的 API + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/_index.md new file mode 100755 index 000000000000..18343fa8acaf --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Hessian协议" +linkTitle: "Hessian协议" +weight: 10 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/guide.md new file mode 100644 index 000000000000..298e971e394b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/guide.md @@ -0,0 +1,74 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +Hessian [^1] 协议用于集成 Hessian 的服务,Hessian 底层采用 Http 通讯,采用 Servlet 暴露服务,Dubbo 缺省内嵌 Jetty 作为服务器实现。 + +Dubbo 的 Hessian 协议可以和原生 Hessian 服务互操作,即: + +* 提供者用 Dubbo 的 Hessian 协议暴露服务,消费者直接用标准 Hessian 接口调用 +* 或者提供方用标准 Hessian 暴露服务,消费方用 Dubbo 的 Hessian 协议调用。 + +## 特性 + +* 连接个数:多连接 +* 连接方式:短连接 +* 传输协议:HTTP +* 传输方式:同步传输 +* 序列化:Hessian二进制序列化 +* 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。 +* 适用场景:页面传输,文件传输,或与原生hessian服务互操作 + +## 依赖 + +```xml + + com.caucho + hessian + 4.0.7 + +``` + +## 约束 + +* 参数及返回值需实现 `Serializable` 接口 +* 参数及返回值不能自定义实现 `List`, `Map`, `Number`, `Date`, `Calendar` 等接口,只能用 JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。 + +## 配置 + +定义 hessian 协议: + +```xml + +``` + +设置默认协议: + +```xml + +``` + +设置 service 协议: + +```xml + +``` + +多端口: + +```xml + + +``` + +直连: + +```xml + +``` + +[^1]: [Hessian](http://hessian.caucho.com) 是 Caucho 开源的一个 RPC 框架,其通讯效率高于 WebService 和 Java 自带的序列化。 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/hessian/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/_index.md new file mode 100755 index 000000000000..8eb15efafa13 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "HTTP协议" +linkTitle: "HTTP协议" +weight: 6 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/guide.md new file mode 100644 index 000000000000..9e7bdec34198 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/guide.md @@ -0,0 +1,65 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +基于 HTTP 表单的远程调用协议,采用 Spring 的 HttpInvoker 实现 + +{{% alert title="提示" color="primary" %}} +`2.3.0` 以上版本支持 +{{% /alert %}} + +## 特性 + +* 连接个数:多连接 +* 连接方式:短连接 +* 传输协议:HTTP +* 传输方式:同步传输 +* 序列化:表单序列化 +* 适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件。 +* 适用场景:需同时给应用程序和浏览器 JS 使用的服务。 + +## 约束 +* 参数及返回值需符合 Bean 规范 + +## 配置 + +配置协议: + +```xml + +``` + +配置 Jetty Server (默认): + +```xml + +``` + +配置 Servlet Bridge Server (推荐使用): + +```xml + +``` + +配置 DispatcherServlet: + +```xml + + dubbo + org.apache.dubbo.remoting.http.servlet.DispatcherServlet + 1 + + + dubbo + /* + +``` + +注意,如果使用 servlet 派发请求: + +* 协议的端口 `` 必须与 servlet 容器的端口相同, +* 协议的上下文路径 `` 必须与 servlet 应用的上下文路径相同。 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/http/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/_index.md new file mode 100755 index 000000000000..b248e53ad669 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Memcached协议" +linkTitle: "Memcached协议" +weight: 12 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/guide.md new file mode 100644 index 000000000000..9139650626c4 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/memcached/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/overview/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/overview/_index.md new file mode 100644 index 000000000000..94ca217e236c --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/overview/_index.md @@ -0,0 +1,200 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- + +Dubbo3 提供了 Triple(Dubbo3)、Dubbo2 协议,这是 Dubbo 框架的原生协议。除此之外,Dubbo3 也对众多第三方协议进行了集成,并将它们纳入 Dubbo 的编程与服务治理体系, +包括 gRPC、Thrift、JsonRPC、Hessian2、REST 等。以下重点介绍 Triple 与 Dubbo2 协议。 + +## Triple 协议 + +Triple 协议是 Dubbo3 推出的主力协议。Triple 意为第三代,通过 Dubbo1.0/ Dubbo2.0 两代协议的演进,以及云原生带来的技术标准化浪潮,Dubbo3 新协议 Triple 应运而生。 + +### RPC 协议的选择 + +协议是 RPC 的核心,它规范了数据在网络中的传输内容和格式。除必须的请求、响应数据外,通常还会包含额外控制数据,如单次请求的序列化方式、超时时间、压缩方式和鉴权信息等。 + +协议的内容包含三部分 +- 数据交换格式: 定义 RPC 的请求和响应对象在网络传输中的字节流内容,也叫作序列化方式 +- 协议结构: 定义包含字段列表和各字段语义以及不同字段的排列方式 +- 协议通过定义规则、格式和语义来约定数据如何在网络间传输。一次成功的 RPC 需要通信的两端都能够按照协议约定进行网络字节流的读写和对象转换。如果两端对使用的协议不能达成一致,就会出现鸡同鸭讲,无法满足远程通信的需求。 + +![协议选择](/imgs/v3/concepts/triple-protocol.png) + +RPC 协议的设计需要考虑以下内容: +- 通用性: 统一的二进制格式,跨语言、跨平台、多传输层协议支持 +- 扩展性: 协议增加字段、升级、支持用户扩展和附加业务元数据 +- 性能:As fast as it can be +- 穿透性:能够被各种终端设备识别和转发:网关、代理服务器等 + 通用性和高性能通常无法同时达到,需要协议设计者进行一定的取舍。 + +#### HTTP/1.1 + +比于直接构建于 TCP 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。 + +选择构建在 HTTP 之上,有两个最大的优势: + +- HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。 +- 通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。 + +但也存在比较明显的问题: + +- 典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求。会产生 HOL。 +- Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式,但性能相当差 +- 无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式 + +#### gRPC +上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点,相比于 Dubbo 构建于 TCP 传输层之上,Google 选择将 gRPC 直接定义在 HTTP/2 协议之上。 +gRPC 的优势由HTTP2 和 Protobuf 继承而来。 + +- 基于 HTTP2 的协议足够简单,用户学习成本低,天然有 server push/ 多路复用 / 流量控制能力 +- 基于 Protobuf 的多语言跨平台二进制兼容能力,提供强大的统一跨语言能力 +- 基于协议本身的生态比较丰富,k8s/etcd 等组件的天然支持协议,云原生的事实协议标准 + +但是也存在部分问题 + +- 对服务治理的支持比较基础,更偏向于基础的 RPC 功能,协议层缺少必要的统一定义,对于用户而言直接用起来并不容易。 +- 强绑定 protobuf 的序列化方式,需要较高的学习成本和改造成本,对于现有的偏单语言的用户而言,迁移成本不可忽视 + +#### 最终的选择 Triple +最终我们选择了兼容 gRPC ,以 HTTP2 作为传输层构建新的协议,也就是 Triple。 + +容器化应用程序和微服务的兴起促进了针对负载内容优化技术的发展。 客户端中使用的传统通信协议( RESTFUL或其他基于 HTTP 的自定义协议)难以满足应用在性能、可维护性、扩展性、安全性等方便的需求。一个跨语言、模块化的协议会逐渐成为新的应用开发协议标准。自从 2017 年 gRPC 协议成为 CNCF 的项目后,包括 k8s、etcd 等越来越多的基础设施和业务都开始使用 gRPC 的生态,作为云原生的微服务化框架, Dubbo 的新协议也完美兼容了 gRPC。并且,对于 gRPC 协议中一些不完善的部分, Triple 也将进行增强和补充。 + +那么,Triple 协议是否解决了上面我们提到的一系列问题呢? + +- 性能上: Triple 协议采取了 metadata 和 payload 分离的策略,这样就可以避免中间设备,如网关进行 payload 的解析和反序列化,从而降低响应时间。 +- 路由支持上,由于 metadata 支持用户添加自定义 header ,用户可以根据 header 更方便的划分集群或者进行路由,这样发布的时候切流灰度或容灾都有了更高的灵活性。 +- 安全性上,支持双向TLS认证(mTLS)等加密传输能力。 +- 易用性上,Triple 除了支持原生 gRPC 所推荐的 Protobuf 序列化外,使用通用的方式支持了 Hessian / JSON 等其他序列化,能让用户更方便的升级到 Triple 协议。对原有的 Dubbo 服务而言,修改或增加 Triple 协议 只需要在声明服务的代码块添加一行协议配置即可,改造成本几乎为 0。 + +### Triple 协议 + +![Triple 协议通信方式](/imgs/v3/concepts/triple.png) + +- 现状 + +1、完整兼容grpc、客户端/服务端可以与原生grpc客户端打通 + +2、目前已经经过大规模生产实践验证,达到生产级别 + +- 特点与优势 + +1、具备跨语言互通的能力,传统的多语言多 SDK 模式和 Mesh 化跨语言模式都需要一种更通用易扩展的数据传输格式。 + +2、提供更完善的请求模型,除了 Request/Response 模型,还应该支持 Streaming 和 Bidirectional。 + +3、易扩展、穿透性高,包括但不限于 Tracing / Monitoring 等支持,也应该能被各层设备识别,网关设施等可以识别数据报文,对 Service Mesh 部署友好,降低用户理解难度。 + +4、多种序列化方式支持、平滑升级 + +5、支持 Java 用户无感知升级,不需要定义繁琐的 IDL 文件,仅需要简单的修改协议名便可以轻松升级到 Triple 协议 + +#### Triple 协议内容介绍 + +基于 grpc 协议进行进一步扩展 + +- Service-Version → "tri-service-version" {Dubbo service version} +- Service-Group → "tri-service-group" {Dubbo service group} +- Tracing-ID → "tri-trace-traceid" {tracing id} +- Tracing-RPC-ID → "tri-trace-rpcid" {_span id _} +- Cluster-Info → "tri-unit-info" {cluster infomation} + +其中 Service-Version 跟 Service-Group 分别标识了 Dubbo 服务的 version 跟 group 信息,因为grpc的 path 申明了 service name 跟 method name,相比于 Dubbo 协议,缺少了version 跟 group 信息;Tracing-ID、Tracing-RPC-ID 用于全链路追踪能力,分别表示 tracing id 跟 span id 信息;Cluster-Info 表示集群信息,可以使用其构建一些如集群划分等路由相关的灵活的服务治理能力。 + +#### Triple Streaming + +Triple协议相比传统的unary方式,多了目前提供的Streaming RPC的能力 + +- Streaming 用于什么场景呢? + +在一些大文件传输、直播等应用场景中, consumer或provider需要跟对端进行大量数据的传输,由于这些情况下的数据量是非常大的,因此是没有办法可以在一个RPC的数据包中进行传输,因此对于这些数据包我们需要对数据包进行分片之后,通过多次RPC调用进行传输,如果我们对这些已经拆分了的RPC数据包进行并行传输,那么到对端后相关的数据包是无序的,需要对接收到的数据进行排序拼接,相关的逻辑会非常复杂。但如果我们对拆分了的RPC数据包进行串行传输,那么对应的网络传输RTT与数据处理的时延会是非常大的。 + +为了解决以上的问题,并且为了大量数据的传输以流水线方式在consumer与provider之间传输,因此Streaming RPC的模型应运而生。 + +通过Triple协议的Streaming RPC方式,会在consumer跟provider之间建立多条用户态的长连接,Stream。同一个TCP连接之上能同时存在多个Stream,其中每条Stream都有StreamId进行标识,对于一条Stream上的数据包会以顺序方式读写。 + +### 总结 + +在API领域,最重要的趋势是标准化技术的崛起。Triple 协议是 Dubbo3 推出的主力协议。它采用分层设计,其数据交换格式基于Protobuf (Protocol Buffers) 协议开发,具备优秀的序列化/反序列化效率,当然还支持多种序列化方式,也支持众多开发语言。在传输层协议,Triple 选择了 HTTP/2,相较于 HTTP/1.1,其传输效率有了很大提升。此外HTTP/2作为一个成熟的开放标准,具备丰富的安全、流控等能力,同时拥有良好的互操作性。Triple 不仅可以用于Server端服务调用,也可以支持浏览器、移动App和IoT设备与后端服务的交互,同时 Triple协议无缝支持 Dubbo3 的全部服务治理能力。 + +在Cloud Native的潮流下,跨平台、跨厂商、跨环境的系统间互操作性的需求必然会催生基于开放标准的RPC技术,而gRPC顺应了历史趋势,得到了越来越广泛地应用。在微服务领域,Triple协议的提出与落地,是 Dubbo3 迈向云原生微服务的一大步。 + +## Dubbo2 + +### Protocol SPEC + +![/dev-guide/images/dubbo_protocol_header.jpg](/imgs/dev/dubbo_protocol_header.png) + + +- Magic - Magic High & Magic Low (16 bits) + + Identifies dubbo protocol with value: 0xdabb + +- Req/Res (1 bit) + + Identifies this is a request or response. Request - 1; Response - 0. + +- 2 Way (1 bit) + + Only useful when Req/Res is 1 (Request), expect for a return value from server or not. Set to 1 if need a return value from server. + +- Event (1 bit) + + Identifies an event message or not, for example, heartbeat event. Set to 1 if this is an event. + +- Serialization ID (5 bit) + + Identifies serialization type: the value for fastjson is 6. + +- Status (8 bits) + + Only useful when Req/Res is 0 (Response), identifies the status of response + + - 20 - OK + - 30 - CLIENT_TIMEOUT + - 31 - SERVER_TIMEOUT + - 40 - BAD_REQUEST + - 50 - BAD_RESPONSE + - 60 - SERVICE_NOT_FOUND + - 70 - SERVICE_ERROR + - 80 - SERVER_ERROR + - 90 - CLIENT_ERROR + - 100 - SERVER_THREADPOOL_EXHAUSTED_ERROR + +- Request ID (64 bits) + + Identifies an unique request. Numeric (long). + +- Data Length (32) + + Length of the content (the variable part) after serialization, counted by bytes. Numeric (integer). + +- Variable Part + + Each part is a byte[] after serialization with specific serialization type, identifies by Serialization ID. + +Every part is a byte[] after serialization with specific serialization type, identifies by Serialization ID + +1. If the content is a Request (Req/Res = 1), each part consists of the content, in turn is: + - Dubbo version + - Service name + - Service version + - Method name + - Method parameter types + - Method arguments + - Attachments + +1. If the content is a Response (Req/Res = 0), each part consists of the content, in turn is: + - Return value type, identifies what kind of value returns from server side: RESPONSE_NULL_VALUE - 2, RESPONSE_VALUE - 1, RESPONSE_WITH_EXCEPTION - 0. + - Return value, the real value returns from server. + + +**注意:** 对于(Variable Part)变长部分,当前版本的dubbo框架使用json序列化时,在每部分内容间额外增加了换行符作为分隔,请选手在Variable Part的每个part后额外增加换行符, 如: +``` +Dubbo version bytes (换行符) +Service name bytes (换行符) +... +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/_index.md new file mode 100755 index 000000000000..78fd3ab2cc26 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Redis协议" +linkTitle: "Redis协议" +weight: 9 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/guide.md new file mode 100644 index 000000000000..4430aea86cfd --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/guide.md @@ -0,0 +1,53 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +基于 Redis [^1] 实现的 RPC 协议。 + +{{% alert title="提示" color="primary" %}} +`2.3.0` 以上版本支持 +{{% /alert %}} + +## 注册 redis 服务的地址 + +```java +RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); +Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181")); +registry.register(URL.valueOf("redis://10.20.153.11/com.foo.BarService?category=providers&dynamic=false&application=foo&group=member&loadbalance=consistenthash")); +``` + +## 在客户端引用 + +在客户端使用 [^2]: + +```xml + +``` + +或者,点对点直连: + +```xml + +``` + +也可以使用自定义接口: + +```xml + +``` + +方法名建议和 redis 的标准方法名相同,即:get(key), set(key, value), delete(key)。 + +如果方法名和 redis 的标准方法名不相同,则需要配置映射关系 [^3]: + +```xml + +``` + +[^1]: [Redis](http://redis.io) 是一个高效的 KV 存储服务器 +[^2]: 不需要感知 Redis 的地址 +[^3]: 其中 "p:xxx" 为 spring 的标准 p 标签 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/redis/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/_index.md new file mode 100755 index 000000000000..d48e3e9fbf50 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Rest协议" +linkTitle: "Rest协议" +weight: 4 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/guide.md new file mode 100644 index 000000000000..9139650626c4 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/guide.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/overview.md new file mode 100644 index 000000000000..35a818058cfe --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rest/overview.md @@ -0,0 +1,1243 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- + + +{{% pageinfo %}} +作者:沈理 + +文档版权:[Apache 2.0许可证 署名-禁止演绎](http://www.apache.org/licenses/LICENSE-2.0) + +本文篇幅较长,因为REST本身涉及面较多。另外,本文参照 Spring 等的文档风格,不仅仅局限于框架用法的阐述,同时也努力呈现框架的设计理念和优良应用的架构思想。 +对于想粗略了解 dubbo 和 REST 的人,只需浏览 概述 至 标准Java REST API:JAX-RS简介 几节即可。 +{{% /pageinfo %}} + + + +## 目录 + +* 概述 +* REST的优点 +* 应用场景 +* 快速入门 +* 标准Java REST API:JAX-RS简介 +* REST服务提供端详解 + * HTTP POST/GET的实现 + * Annotation放在接口类还是实现类 + * JSON、XML等多数据格式的支持 + * 中文字符支持 + * XML数据格式的额外要求 + * 定制序列化 + * 配置REST Server的实现 + * 获取上下文(Context)信息 + * 配置端口号和Context Path + * 配置线程数和IO线程数 + * 配置长连接 + * 配置最大的HTTP连接数 + * 配置每个消费端的超时时间和HTTP连接数 + * GZIP数据压缩 + * 用Annotation取代部分Spring XML配置 + * 添加自定义的Filter、Interceptor等 + * 添加自定义的Exception处理 + * 配置HTTP日志输出 + * 输入参数的校验 + * 是否应该透明发布REST服务 + * Dubbo的REST提供端在被调用时使用header +* REST服务消费端详解 + * 场景1:非dubbo的消费端调用dubbo的REST服务 + * 场景2:dubbo消费端调用dubbo的REST服务 + * 场景3:dubbo的消费端调用非dubbo的REST服务 + * Dubbo的消费端在调用REST服务时配置自定义header +* Dubbo中JAX-RS的限制 +* REST常见问题解答(REST FAQ) + * Dubbo REST的服务能和Dubbo注册中心、监控中心集成吗? + * Dubbo REST中如何实现负载均衡和容错(failover)? + * JAX-RS中重载的方法能够映射到同一URL地址吗? + * JAX-RS中作POST的方法能够接收多个参数吗? +* Dubbo当前体系可能的不足之处(与REST相关的) + * RpcContext的侵入性 + * Protocol配置的局限性 + * XML命名不符合spring规范 +* REST最佳实践 +* 性能基准测试 + * 测试环境 + * 测试脚本 + * 测试结果 +* 扩展讨论 + * REST与Thrift、Protobuf等的对比 + * REST与传统WebServices的对比 + * JAX-RS与Spring MVC的对比 +* 未来 + +## 概述 + +dubbo支持多种远程调用方式,例如dubbo RPC(二进制序列化 + tcp协议)、http invoker(二进制序列化 + http协议,至少在开源版本没发现对文本序列化的支持)、hessian(二进制序列化 + http协议)、WebServices (文本序列化 + http协议)等等,但缺乏对当今特别流行的REST风格远程调用(文本序列化 + http协议)的支持。 + +有鉴于此,我们基于标准的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的简写),为dubbo提供了接近透明的REST调用支持。由于完全兼容Java标准API,所以为dubbo开发的所有REST服务,未来脱离dubbo或者任何特定的REST底层实现一般也可以正常运行。 + +特别值得指出的是,我们并不需要完全严格遵守REST的原始定义和架构风格。即使著名的Twitter REST API也会根据情况做适度调整,而不是机械的遵守原始的REST风格。 + +> 附注:我们将这个功能称之为REST风格的远程调用,即RESTful Remoting(抽象的远程处理或者调用),而不是叫RESTful RPC(具体的远程“过程”调用),是因为REST和RPC本身可以被认为是两种不同的风格。在dubbo的REST实现中,可以说有两个面向,其一是提供或消费正常的REST服务,其二是将REST作为dubbo RPC体系中一种协议实现,而RESTful Remoting同时涵盖了这两个面向。 + +## REST的优点 + +以下摘自维基百科: + +* 可更高效利用缓存来提高响应速度 +* 通讯本身的无状态性可以让不同的服务器的处理一系列请求中的不同请求,提高服务器的扩展性 +* 浏览器即可作为客户端,简化软件需求 +* 相对于其他叠加在HTTP协议之上的机制,REST的软件依赖性更小 +* 不需要额外的资源发现机制 +* 在软件技术演进中的长期的兼容性更好 + +这里我还想特别补充REST的显著优点:基于简单的文本格式消息和通用的HTTP协议,使它具备极广的适用性,几乎所有语言和平台都对它提供支持,同时其学习和使用的门槛也较低。 + +## 应用场景 + +正是由于REST在适用性方面的优点,所以在dubbo中支持REST,可以为当今多数主流的远程调用场景都带来(显著)好处: + +1. 显著简化企业内部的异构系统之间的(跨语言)调用。此处主要针对这种场景:dubbo的系统做服务提供端,其他语言的系统(也包括某些不基于dubbo的java系统)做服务消费端,两者通过HTTP和文本消息进行通信。即使相比Thrift、ProtoBuf等二进制跨语言调用方案,REST也有自己独特的优势(详见后面讨论) + +2. 显著简化对外Open API(开放平台)的开发。既可以用dubbo来开发专门的Open API应用,也可以将原内部使用的dubbo service直接“透明”发布为对外的Open REST API(当然dubbo本身未来最好可以较透明的提供诸如权限控制、频次控制、计费等诸多功能) + +3. 显著简化手机(平板)APP或者PC桌面客户端开发。类似于2,既可以用dubbo来开发专门针对无线或者桌面的服务器端,也可以将原内部使用的dubbo service直接”透明“的暴露给手机APP或桌面程序。当然在有些项目中,手机或桌面程序也可以直接访问以上场景2中所述的Open API。 + +4. 显著简化浏览器AJAX应用的开发。类似于2,既可以用dubbo来开发专门的AJAX服务器端,也可以将原内部使用的dubbo service直接”透明“的暴露给浏览器中JavaScript。当然,很多AJAX应用更适合与web框架协同工作,所以直接访问dubbo service在很多web项目中未必是一种非常优雅的架构。 + +5. 为企业内部的dubbo系统之间(即服务提供端和消费端都是基于dubbo的系统)提供一种基于文本的、易读的远程调用方式。 + +6. 一定程度简化dubbo系统对其它异构系统的调用。可以用类似dubbo的简便方式“透明”的调用非dubbo系统提供的REST服务(不管服务提供端是在企业内部还是外部) + +需要指出的是,我认为1~3是dubbo的REST调用最有价值的三种应用场景,并且我们为dubbo添加REST调用,其最主要到目的也是面向服务的提供端,即开发REST服务来提供给非dubbo的(异构)消费端。 + +归纳起来,所有应用场景如下图所示: +![rest](/imgs/user/rest.jpg) + +借用Java过去最流行的宣传语,为dubbo添加REST调用后,可以实现服务的”一次编写,到处访问“,理论上可以面向全世界开放,从而真正实现比较理想化的面向服务架构(SOA)。 + +当然,传统的WebServices(WSDL/SOAP)也基本同样能满足以上场景(除了场景4)的要求(甚至还能满足那些需要企业级特性的场景),但由于其复杂性等问题,现在已经越来越少被实际采用了。 + +## 快速入门 + +在dubbo中开发一个REST风格的服务会比较简单,下面以一个注册用户的简单服务为例说明。 + +这个服务要实现的功能是提供如下URL(注:这个URL不是完全符合REST的风格,但是更简单实用): + +``` +http://localhost:8080/users/register +``` + +而任何客户端都可以将包含用户信息的JSON字符串POST到以上URL来完成用户注册。 + +首先,开发服务的接口: + +```java +public interface UserService { + void registerUser(User user); +} +``` + +然后,开发服务的实现: + +```java +@Path("users") +public class UserServiceImpl implements UserService { + + @POST + @Path("register") + @Consumes({MediaType.APPLICATION_JSON}) + public void registerUser(User user) { + // save the user... + } +} +``` +上面的服务实现代码非常简单,但是由于REST服务是要被发布到特定HTTP URL,供任意语言客户端甚至浏览器来访问,所以这里要额外添加了几个JAX-RS的标准annotation来做相关的配置: + +@Path("users"):指定访问UserService的URL相对路径是/users,即http://localhost:8080/users + +@Path("register"):指定访问registerUser()方法的URL相对路径是/register,再结合上一个@Path为UserService指定的路径,则调用UserService.register()的完整路径为http://localhost:8080/users/register + +@POST:指定访问registerUser()用HTTP POST方法 + +@Consumes({MediaType.APPLICATION_JSON}):指定registerUser()接收JSON格式的数据。REST框架会自动将JSON数据反序列化为User对象 + +最后,在spring配置文件中添加此服务,即完成所有服务开发工作: + + ```xml + + + + + + + + +``` + +## 标准Java REST API:JAX-RS简介 + +JAX-RS是标准的Java REST API,得到了业界的广泛支持和应用,其著名的开源实现就有很多,包括Oracle的Jersey,RedHat的RestEasy,Apache的CXF和Wink,以及restlet等等。另外,所有支持JavaEE 6.0以上规范的商用JavaEE应用服务器都对JAX-RS提供了支持。因此,JAX-RS是一种已经非常成熟的解决方案,并且采用它没有任何所谓vendor lock-in的问题。 + +JAX-RS在网上的资料非常丰富,例如下面的入门教程: + +* Oracle官方的tutorial:http://docs.oracle.com/javaee/7/tutorial/doc/jaxrs.htm +* IBM developerWorks中国站文章:http://www.ibm.com/developerworks/cn/java/j-lo-jaxrs/ + +更多的资料请自行google或者百度一下。就学习JAX-RS来说,一般主要掌握其各种annotation的用法即可。 + +> 注意:dubbo是基于JAX-RS 2.0版本的,有时候需要注意一下资料或REST实现所涉及的版本。 + +## REST服务提供端详解 + +下面我们扩充“快速入门”中的UserService,进一步展示在dubbo中REST服务提供端的开发要点。 + +### HTTP POST/GET的实现 + +REST服务中虽然建议使用HTTP协议中四种标准方法POST、DELETE、PUT、GET来分别实现常见的“增删改查”,但实际中,我们一般情况直接用POST来实现“增改”,GET来实现“删查”即可(DELETE和PUT甚至会被一些防火墙阻挡)。 + +前面已经简单演示了POST的实现,在此,我们为UserService添加一个获取注册用户资料的功能,来演示GET的实现。 + +这个功能就是要实现客户端通过访问如下不同URL来获取不同ID的用户资料: + +``` +http://localhost:8080/users/1001 +http://localhost:8080/users/1002 +http://localhost:8080/users/1003 +``` + +当然,也可以通过其他形式的URL来访问不同ID的用户资料,例如: + +``` +http://localhost:8080/users/load?id=1001 +``` + +JAX-RS本身可以支持所有这些形式。但是上面那种在URL路径中包含查询参数的形式(http://localhost:8080/users/1001) 更符合REST的一般习惯,所以更推荐大家来使用。下面我们就为UserService添加一个getUser()方法来实现这种形式的URL访问: + +```java +@GET +@Path("{id : \\d+}") +@Produces({MediaType.APPLICATION_JSON}) +public User getUser(@PathParam("id") Long id) { + // ... +} +``` + +@GET:指定用HTTP GET方法访问 + +@Path("{id : \\d+}"):根据上面的功能需求,访问getUser()的URL应当是“http://localhost:8080/users/ + 任意数字",并且这个数字要被做为参数传入getUser()方法。 这里的annotation配置中,@Path中间的{id: xxx}指定URL相对路径中包含了名为id参数,而它的值也将被自动传递给下面用@PathParam("id")修饰的方法参数id。{id:后面紧跟的\\d+是一个正则表达式,指定了id参数必须是数字。 + +@Produces({MediaType.APPLICATION_JSON}):指定getUser()输出JSON格式的数据。框架会自动将User对象序列化为JSON数据。 + +### Annotation放在接口类还是实现类 + +在Dubbo中开发REST服务主要都是通过JAX-RS的annotation来完成配置的,在上面的示例中,我们都是将annotation放在服务的实现类中。但其实,我们完全也可以将annotation放到服务的接口上,这两种方式是完全等价的,例如: + +```java +@Path("users") +public interface UserService { + + @GET + @Path("{id : \\d+}") + @Produces({MediaType.APPLICATION_JSON}) + User getUser(@PathParam("id") Long id); +} +``` + +在一般应用中,我们建议将annotation放到服务实现类,这样annotation和java实现代码位置更接近,更便于开发和维护。另外更重要的是,我们一般倾向于避免对接口的污染,保持接口的纯净性和广泛适用性。 + +但是,如后文所述,如果我们要用dubbo直接开发的消费端来访问此服务,则annotation必须放到接口上。 + +如果接口和实现类都同时添加了annotation,则实现类的annotation配置会生效,接口上的annotation被直接忽略。 + +### JSON、XML等多数据格式的支持 + +在dubbo中开发的REST服务可以同时支持传输多种格式的数据,以给客户端提供最大的灵活性。其中我们目前对最常用的JSON和XML格式特别添加了额外的功能。 + +比如,我们要让上例中的getUser()方法支持分别返回JSON和XML格式的数据,只需要在annotation中同时包含两种格式即可: + +```java +@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +User getUser(@PathParam("id") Long id); +``` + +或者也可以直接用字符串(还支持通配符)表示MediaType: + +```java +@Produces({"application/json", "text/xml"}) +User getUser(@PathParam("id") Long id); +``` + +如果所有方法都支持同样类型的输入输出数据格式,则我们无需在每个方法上做配置,只需要在服务类上添加annotation即可: + +```java +@Path("users") +@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +@Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML}) +public class UserServiceImpl implements UserService { + // ... +} + +``` + +在一个REST服务同时对多种数据格式支持的情况下,根据JAX-RS标准,一般是通过HTTP中的MIME header(content-type和accept)来指定当前想用的是哪种格式的数据。 + +但是在dubbo中,我们还自动支持目前业界普遍使用的方式,即用一个URL后缀(.json和.xml)来指定想用的数据格式。例如,在添加上述annotation后,直接访问http://localhost:8888/users/1001.json则表示用json格式,直接访问http://localhost:8888/users/1002.xml则表示用xml格式,比用HTTP Header更简单直观。Twitter、微博等的REST API都是采用这种方式。 + +如果你既不加HTTP header,也不加后缀,则dubbo的REST会优先启用在以上annotation定义中排位最靠前的那种数据格式。 + +> 注意:这里要支持XML格式数据,在annotation中既可以用MediaType.TEXT_XML,也可以用MediaType.APPLICATION_XML,但是TEXT_XML是更常用的,并且如果要利用上述的URL后缀方式来指定数据格式,只能配置为TEXT_XML才能生效。 + +### 中文字符支持 + +为了在dubbo REST中正常输出中文字符,和通常的Java web应用一样,我们需要将HTTP响应的contentType设置为UTF-8编码。 + +基于JAX-RS的标准用法,我们只需要做如下annotation配置即可: + +```java +@Produces({"application/json; charset=UTF-8", "text/xml; charset=UTF-8"}) +User getUser(@PathParam("id") Long id); +``` + +为了方便用户,我们在dubbo REST中直接添加了一个支持类,来定义以上的常量,可以直接使用,减少出错的可能性。 + +```java +@Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8}) +User getUser(@PathParam("id") Long id); +``` + +### XML数据格式的额外要求 + +由于JAX-RS的实现一般都用标准的JAXB(Java API for XML Binding)来序列化和反序列化XML格式数据,所以我们需要为每一个要用XML传输的对象添加一个类级别的JAXB annotation,否则序列化将报错。例如为getUser()中返回的User添加如下: + +```java +@XmlRootElement +public class User implements Serializable { + // ... +} +``` + +此外,如果service方法中的返回值是Java的 primitive类型(如int,long,float,double等),最好为它们添加一层wrapper对象,因为JAXB不能直接序列化primitive类型。 + +例如,我们想让前述的registerUser()方法返回服务器端为用户生成的ID号: + +```java +long registerUser(User user); +``` + +由于primitive类型不被JAXB序列化支持,所以添加一个wrapper对象: + +```java +@XmlRootElement +public class RegistrationResult implements Serializable { + + private Long id; + + public RegistrationResult() { + } + + public RegistrationResult(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} +``` + +并修改service方法: + +```java +RegistrationResult registerUser(User user); +``` + +这样不但能够解决XML序列化的问题,而且使得返回的数据都符合XML和JSON的规范。例如,在JSON中,返回的将是如下形式: + +```javascript +{"id": 1001} +``` + +如果不加wrapper,JSON返回值将直接是 + +``` +1001 +``` + +而在XML中,加wrapper后返回值将是: + +```xml + + 1002 + +``` + +这种wrapper对象其实利用所谓Data Transfer Object(DTO)模式,采用DTO还能对传输数据做更多有用的定制。 + +### 定制序列化 + +如上所述,REST的底层实现会在service的对象和JSON/XML数据格式之间自动做序列化/反序列化。但有些场景下,如果觉得这种自动转换不满足要求,可以对其做定制。 + +Dubbo中的REST实现是用JAXB做XML序列化,用Jackson做JSON序列化,所以在对象上添加JAXB或Jackson的annotation即可以定制映射。 + +例如,定制对象属性映射到XML元素的名字: + +```java +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class User implements Serializable { + + @XmlElement(name="username") + private String name; +} +``` + +定制对象属性映射到JSON字段的名字: + +```java +public class User implements Serializable { + + @JsonProperty("username") + private String name; +} +``` + +更多资料请参考JAXB和Jackson的官方文档,或自行google。 + +### 配置REST Server的实现 + +目前在dubbo中,我们支持5种嵌入式rest server的实现,并同时支持采用外部应用服务器来做rest server的实现。rest server的实现是通过如下server这个XML属性来选择的: + +```xml + +``` + +以上配置选用了嵌入式的jetty来做rest server,同时,如果不配置server属性,rest协议默认也是选用jetty。jetty是非常成熟的java servlet容器,并和dubbo已经有较好的集成(目前5种嵌入式server中只有jetty和后面所述的tomcat、tjws,与dubbo监控系统等完成了无缝的集成),所以,如果你的dubbo系统是单独启动的进程,你可以直接默认采用jetty即可。 + + +```xml + +``` + +以上配置选用了嵌入式的tomcat来做rest server。在嵌入式tomcat上,REST的性能比jetty上要好得多(参见后面的基准测试),建议在需要高性能的场景下采用tomcat。 + +```xml + +``` + +以上配置选用嵌入式的netty来做rest server。(TODO more contents to add) + +```xml + (tjws is now deprecated) + +``` + +以上配置选用嵌入式的tjws或Sun HTTP server来做rest server。这两个server实现非常轻量级,非常方便在集成测试中快速启动使用,当然也可以在负荷不高的生产环境中使用。 注:tjws目前已经被deprecated掉了,因为它不能很好的和servlet 3.1 API工作。 + +如果你的dubbo系统不是单独启动的进程,而是部署到了Java应用服务器中,则建议你采用以下配置: + +```xml + +``` + +通过将server设置为servlet,dubbo将采用外部应用服务器的servlet容器来做rest server。同时,还要在dubbo系统的web.xml中添加如下配置: + +```xml + + + contextConfigLocation + /WEB-INF/classes/META-INF/spring/dubbo-demo-provider.xml + + + + org.apache.dubbo.remoting.http.servlet.BootstrapListener + + + + org.springframework.web.context.ContextLoaderListener + + + + dispatcher + org.apache.dubbo.remoting.http.servlet.DispatcherServlet + 1 + + + + dispatcher + /* + + +``` + +即必须将dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能与外部servlet容器的集成。 + +> 注意:如果你是用spring的ContextLoaderListener来加载spring,则必须保证BootstrapListener配置在ContextLoaderListener之前,否则dubbo初始化会出错。 + +其实,这种场景下你依然可以坚持用嵌入式server,但外部应用服务器的servlet容器往往比嵌入式server更加强大(特别是如果你是部署到更健壮更可伸缩的WebLogic,WebSphere等),另外有时也便于在应用服务器做统一管理、监控等等。 + +### 获取上下文(Context)信息 + +在远程调用中,值得获取的上下文信息可能有很多种,这里特别以获取客户端IP为例。 + +在dubbo的REST中,我们有两种方式获取客户端IP。 + +第一种方式,用JAX-RS标准的@Context annotation: + +```java +public User getUser(@PathParam("id") Long id, @Context HttpServletRequest request) { + System.out.println("Client address is " + request.getRemoteAddr()); +} +``` + +用Context修饰getUser()的一个方法参数后,就可以将当前的HttpServletRequest注入进来,然后直接调用servlet api获取IP。 + +> 注意:这种方式只能在设置server="tjws"或者server="tomcat"或者server="jetty"或者server="servlet"的时候才能工作,因为只有这几种REST server的实现才提供了servlet容器。另外,标准的JAX-RS还支持用@Context修饰service类的一个实例字段来获取HttpServletRequest,但在dubbo中我们没有对此作出支持。 + +第二种方式,用dubbo中常用的RpcContext: + +```java +public User getUser(@PathParam("id") Long id) { + System.out.println("Client address is " + RpcContext.getContext().getRemoteAddressString()); +} +``` + +> 注意:这种方式只能在设置server="jetty"或者server="tomcat"或者server="servlet"或者server="tjws"的时候才能工作。另外,目前dubbo的RpcContext是一种比较有侵入性的用法,未来我们很可能会做出重构。 + +如果你想保持你的项目对JAX-RS的兼容性,未来脱离dubbo也可以运行,请选择第一种方式。如果你想要更优雅的服务接口定义,请选用第二种方式。 + +此外,在最新的dubbo rest中,还支持通过RpcContext来获取HttpServletRequest和HttpServletResponse,以提供更大的灵活性来方便用户实现某些复杂功能,比如在dubbo标准的filter中访问HTTP Header。用法示例如下: + +```java +if (RpcContext.getContext().getRequest() != null && RpcContext.getContext().getRequest() instanceof HttpServletRequest) { + System.out.println("Client address is " + ((HttpServletRequest) RpcContext.getContext().getRequest()).getRemoteAddr()); +} + +if (RpcContext.getContext().getResponse() != null && RpcContext.getContext().getResponse() instanceof HttpServletResponse) { + System.out.println("Response object from RpcContext: " + RpcContext.getContext().getResponse()); +} +``` + +> 注意:为了保持协议的中立性,RpcContext.getRequest()和RpcContext.getResponse()返回的仅仅是一个Object类,而且可能为null。所以,你必须自己做null和类型的检查。 + +> 注意:只有在设置server="jetty"或者server="tomcat"或者server="servlet"的时候,你才能通过以上方法正确的得到HttpServletRequest和HttpServletResponse,因为只有这几种server实现了servlet容器。 + +为了简化编程,在此你也可以用泛型的方式来直接获取特定类型的request/response: + +```java +if (RpcContext.getContext().getRequest(HttpServletRequest.class) != null) { + System.out.println("Client address is " + RpcContext.getContext().getRequest(HttpServletRequest.class).getRemoteAddr()); +} + +if (RpcContext.getContext().getResponse(HttpServletResponse.class) != null) { + System.out.println("Response object from RpcContext: " + RpcContext.getContext().getResponse(HttpServletResponse.class)); +} +``` + +如果request/response不符合指定的类型,这里也会返回null。 + +### 配置端口号和Context Path + +dubbo中的rest协议默认将采用80端口,如果想修改端口,直接配置: + +```xml + +``` + +另外,如前所述,我们可以用@Path来配置单个rest服务的URL相对路径。但其实,我们还可以设置一个所有rest服务都适用的基础相对路径,即java web应用中常说的context path。 + +只需要添加如下contextpath属性即可: + +```xml + +``` + +以前面代码为例: + +```java +@Path("users") +public class UserServiceImpl implements UserService { + + @POST + @Path("register") + @Consumes({MediaType.APPLICATION_JSON}) + public void registerUser(User user) { + // save the user... + } +} +``` + +现在registerUser()的完整访问路径为: + +``` +http://localhost:8888/services/users/register +``` + +注意:如果你是选用外部应用服务器做rest server,即配置: + +```xml + +``` + +则必须保证这里设置的port、contextpath,与外部应用服务器的端口、DispatcherServlet的上下文路径(即webapp path加上servlet url pattern)保持一致。例如,对于部署为tomcat ROOT路径的应用,这里的contextpath必须与web.xml中DispacherServlet的``完全一致: + +```xml + + dispatcher + /services/* + +``` + +### 配置线程数和IO线程数 + +可以为rest服务配置线程池大小: + +```xml + +``` + +> 注意:目前线程池的设置只有当server="netty"或者server="jetty"或者server="tomcat"的时候才能生效。另外,如果server="servlet",由于这时候启用的是外部应用服务器做rest server,不受dubbo控制,所以这里的线程池设置也无效。 + +如果是选用netty server,还可以配置Netty的IO worker线程数: + +```xml + +``` + +### 配置长连接 + +Dubbo中的rest服务默认都是采用http长连接来访问,如果想切换为短连接,直接配置: + +```xml + +``` + +> 注意:这个配置目前只对server="netty"和server="tomcat"才能生效。 + +### 配置最大的HTTP连接数 + +可以配置服务器提供端所能同时接收的最大HTTP连接数,防止REST server被过多连接撑爆,以作为一种最基本的自我保护机制: + +```xml + +``` + +当然,由于这个配置针对消费端生效的,所以也可以在消费端配置: + +```xml + +``` + +但是,通常我们建议配置在服务提供端提供此类配置。按照dubbo官方文档的说法:“Provider上尽量多配置Consumer端的属性,让Provider实现者一开始就思考Provider服务特点、服务质量的问题。” + +> 注意:如果dubbo的REST服务是发布给非dubbo的客户端使用,则这里``上的配置完全无效,因为这种客户端不受dubbo控制。 + +### GZIP数据压缩 + +Dubbo的REST支持用GZIP压缩请求和响应的数据,以减少网络传输时间和带宽占用,但这种方式会也增加CPU开销。 + +TODO more contents to add + +### 用Annotation取代部分Spring XML配置 + +以上所有的讨论都是基于dubbo在spring中的xml配置。但是,dubbo/spring本身也支持用annotation来作配置,所以我们也可以按dubbo官方文档中的步骤,把相关annotation加到REST服务的实现中,取代一些xml配置,例如: + +```java +@Service(protocol = "rest") +@Path("users") +public class UserServiceImpl implements UserService { + + @Autowired + private UserRepository userRepository; + + @POST + @Path("register") + @Consumes({MediaType.APPLICATION_JSON}) + public void registerUser(User user) { + // save the user + userRepository.save(user); + } +} +``` + +annotation的配置更简单更精确,经常也更便于维护(当然现代IDE都可以在xml中支持比如类名重构,所以就这里的特定用例而言,xml的维护性也很好)。而xml对代码的侵入性更小一些,尤其有利于动态修改配置,特别是比如你要针对单个服务配置连接超时时间、每客户端最大连接数、集群策略、权重等等。另外,特别对复杂应用或者模块来说,xml提供了一个中心点来涵盖的所有组件和配置,更一目了然,一般更便于项目长时期的维护。 + +当然,选择哪种配置方式没有绝对的优劣,和个人的偏好也不无关系。 + +### 添加自定义的Filter、Interceptor等 + +Dubbo的REST也支持JAX-RS标准的Filter和Interceptor,以方便对REST的请求与响应过程做定制化的拦截处理。 + +其中,Filter主要用于访问和设置HTTP请求和响应的参数、URI等等。例如,设置HTTP响应的cache header: + +```java +public class CacheControlFilter implements ContainerResponseFilter { + + public void filter(ContainerRequestContext req, ContainerResponseContext res) { + if (req.getMethod().equals("GET")) { + res.getHeaders().add("Cache-Control", "someValue"); + } + } +} +``` + +Interceptor主要用于访问和修改输入与输出字节流,例如,手动添加GZIP压缩: + +```java +public class GZIPWriterInterceptor implements WriterInterceptor { + + @Override + public void aroundWriteTo(WriterInterceptorContext context) + throws IOException, WebApplicationException { + OutputStream outputStream = context.getOutputStream(); + context.setOutputStream(new GZIPOutputStream(outputStream)); + context.proceed(); + } +} +``` + +在标准JAX-RS应用中,我们一般是为Filter和Interceptor添加@Provider annotation,然后JAX-RS runtime会自动发现并启用它们。而在dubbo中,我们是通过添加XML配置的方式来注册Filter和Interceptor: + +```xml + +``` + +在此,我们可以将Filter、Interceptor和DynamicFeature这三种类型的对象都添加到`extension`属性上,多个之间用逗号分隔。(DynamicFeature是另一个接口,可以方便我们更动态的启用Filter和Interceptor,感兴趣请自行google。) + +当然,dubbo自身也支持Filter的概念,但我们这里讨论的Filter和Interceptor更加接近协议实现的底层,相比dubbo的filter,可以做更底层的定制化。 + +> 注:这里的XML属性叫extension,而不是叫interceptor或者filter,是因为除了Interceptor和Filter,未来我们还会添加更多的扩展类型。 + +如果REST的消费端也是dubbo系统(参见下文的讨论),则也可以用类似方式为消费端配置Interceptor和Filter。但注意,JAX-RS中消费端的Filter和提供端的Filter是两种不同的接口。例如前面例子中服务端是ContainerResponseFilter接口,而消费端对应的是ClientResponseFilter: + +```java +public class LoggingFilter implements ClientResponseFilter { + + public void filter(ClientRequestContext reqCtx, ClientResponseContext resCtx) throws IOException { + System.out.println("status: " + resCtx.getStatus()); + System.out.println("date: " + resCtx.getDate()); + System.out.println("last-modified: " + resCtx.getLastModified()); + System.out.println("location: " + resCtx.getLocation()); + System.out.println("headers:"); + for (Entry> header : resCtx.getHeaders().entrySet()) { + System.out.print("\t" + header.getKey() + " :"); + for (String value : header.getValue()) { + System.out.print(value + ", "); + } + System.out.print("\n"); + } + System.out.println("media-type: " + resCtx.getMediaType().getType()); + } +} +``` + +### 添加自定义的Exception处理 + +Dubbo的REST也支持JAX-RS标准的ExceptionMapper,可以用来定制特定exception发生后应该返回的HTTP响应。 + +```java +public class CustomExceptionMapper implements ExceptionMapper { + + public Response toResponse(NotFoundException e) { + return Response.status(Response.Status.NOT_FOUND).entity("Oops! the requested resource is not found!").type("text/plain").build(); + } +} +``` + +和Interceptor、Filter类似,将其添加到XML配置文件中即可启用: + +```xml + +``` + +### 配置HTTP日志输出 + +Dubbo rest支持输出所有HTTP请求/响应中的header字段和body消息体。 + +在XML配置中添加如下自带的REST filter: + +```xml + +``` + +**然后在logging配置中至少为org.apache.dubbo.rpc.protocol.rest.support打开INFO级别日志输出**,例如,在log4j.xml中配置: + +```xml + + + + +``` + +当然,你也可以直接在ROOT logger打开INFO级别日志输出: + +```xml + + + + +``` + +然后在日志中会有类似如下的内容输出: + +``` +The HTTP headers are: +accept: application/json;charset=UTF-8 +accept-encoding: gzip, deflate +connection: Keep-Alive +content-length: 22 +content-type: application/json +host: 192.168.1.100:8888 +user-agent: Apache-HttpClient/4.2.1 (java 1.5) +``` + +``` +The contents of request body is: +{"id":1,"name":"dang"} +``` + +打开HTTP日志输出后,除了正常日志输出的性能开销外,也会在比如HTTP请求解析时产生额外的开销,因为需要建立额外的内存缓冲区来为日志的输出做数据准备。 + +### 输入参数的校验 + +dubbo的rest支持采用Java标准的bean validation annotation(JSR 303)来做输入校验http://beanvalidation.org/ + +为了和其他dubbo远程调用协议保持一致,在rest中作校验的annotation必须放在服务的接口上,例如: + +```java +public interface UserService { + + User getUser(@Min(value=1L, message="User ID must be greater than 1") Long id); +} + +``` + +当然,在很多其他的bean validation的应用场景都是将annotation放到实现类而不是接口上。把annotation放在接口上至少有一个好处是,dubbo的客户端可以共享这个接口的信息,dubbo甚至不需要做远程调用,在本地就可以完成输入校验。 + +然后按照dubbo的标准方式在XML配置中打开验证: + +```xml + +``` + +在dubbo的其他很多远程调用协议中,如果输入验证出错,是直接将`RpcException`抛向客户端,而在rest中由于客户端经常是非dubbo,甚至非java的系统,所以不便直接抛出Java异常。因此,目前我们将校验错误以XML的格式返回: + +```xml + + + getUserArgument0 + User ID must be greater than 1 + 0 + + +``` + +稍后也会支持其他数据格式的返回值。至于如何对验证错误消息作国际化处理,直接参考bean validation的相关文档即可。 + +如果你认为默认的校验错误返回格式不符合你的要求,可以如上面章节所述,添加自定义的ExceptionMapper来自由的定制错误返回格式。需要注意的是,这个ExceptionMapper必须用泛型声明来捕获dubbo的RpcException,才能成功覆盖dubbo rest默认的异常处理策略。为了简化操作,其实这里最简单的方式是直接继承dubbo rest的RpcExceptionMapper,并覆盖其中处理校验异常的方法即可: + +```java +public class MyValidationExceptionMapper extends RpcExceptionMapper { + + protected Response handleConstraintViolationException(ConstraintViolationException cve) { + ViolationReport report = new ViolationReport(); + for (ConstraintViolation cv : cve.getConstraintViolations()) { + report.addConstraintViolation(new RestConstraintViolation( + cv.getPropertyPath().toString(), + cv.getMessage(), + cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString())); + } + // 采用json输出代替xml输出 + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(report).type(ContentType.APPLICATION_JSON_UTF_8).build(); + } +} +``` + +然后将这个ExceptionMapper添加到XML配置中即可: + +```xml + +``` + +### 是否应该透明发布REST服务 + +Dubbo的REST调用和dubbo中其它某些RPC不同的是,需要在服务代码中添加JAX-RS的annotation(以及JAXB、Jackson的annotation),如果你觉得这些annotation一定程度“污染”了你的服务代码,你可以考虑编写额外的Facade和DTO类,在Facade和DTO上添加annotation,而Facade将调用转发给真正的服务实现类。当然事实上,直接在服务代码中添加annotation基本没有任何负面作用,而且这本身是Java EE的标准用法,另外JAX-RS和JAXB的annotation是属于java标准,比我们经常使用的spring、dubbo等等annotation更没有vendor lock-in的问题,所以一般没有必要因此而引入额外对象。 + +另外,如果你想用前述的@Context annotation,通过方法参数注入HttpServletRequest(如`public User getUser(@PathParam("id") Long id, @Context HttpServletRequest request)`),这时候由于改变了服务的方法签名,并且HttpServletRequest是REST特有的参数,**所以如果你的服务要支持多种RPC机制的话**,则引入额外的Facade类是比较适当的。 + +当然,在没有添加REST调用之前,你的服务代码可能本身已经就充当了Facade和DTO的角色(至于为什么有些场景需要这些角色,有兴趣可参考[微观SOA:服务设计原则及其实践方式](http://www.infoq.com/cn/articles/micro-soa-1))。这种情况下,在添加REST之后,如果你再额外添加与REST相关的Facade和DTO,就相当于对原有代码对再一次包装,即形成如下调用链: + +`RestFacade/RestDTO -> Facade/DTO -> Service` + +这种体系比较繁琐,数据转换之类的工作量也不小,所以一般应尽量避免如此。 + +### dubbo的提供端在调用REST服务时使用header + +Dubbo通过RpcContextFilter将header取出分解之后设置到RpcContext的attachments,所以在提供端可以直接从RpcContext的attachments中获取到消费端设置的header信息: + +``` + RpcContext.getContext().getAttachment(key1) + RpcContext.getContext().getAttachment(key2) +``` + +## REST服务消费端详解 + +这里我们用三种场景来分别讨论: + +1. 非dubbo的消费端调用dubbo的REST服务(non-dubbo --> dubbo) +2. dubbo消费端调用dubbo的REST服务 (dubbo --> dubbo) +3. dubbo的消费端调用非dubbo的REST服务 (dubbo --> non-dubbo) + +### 场景1:非dubbo的消费端调用dubbo的REST服务 + +这种场景的客户端与dubbo本身无关,直接选用相应语言和框架中合适的方式即可。 + +如果是还是java的客户端(但没用dubbo),可以考虑直接使用标准的JAX-RS Client API或者特定REST实现的Client API来调用REST服务。下面是用JAX-RS Client API来访问上述的UserService的registerUser(): + +```java +User user = new User(); +user.setName("Larry"); + +Client client = ClientBuilder.newClient(); +WebTarget target = client.target("http://localhost:8080/services/users/register.json"); +Response response = target.request().post(Entity.entity(user, MediaType.APPLICATION_JSON_TYPE)); + +try { + if (response.getStatus() != 200) { + throw new RuntimeException("Failed with HTTP error code : " + response.getStatus()); + } + System.out.println("The generated id is " + response.readEntity(RegistrationResult.class).getId()); +} finally { + response.close(); + client.close(); // 在真正开发中不要每次关闭client,比如HTTP长连接是由client持有的 +} +``` + +上面代码片段中的User和RegistrationResult类都是消费端自己编写的,JAX-RS Client API会自动对它们做序列化/反序列化。 + +当然,在java中也可以直接用自己熟悉的比如HttpClient,FastJson,XStream等等各种不同技术来实现REST客户端,在此不再详述。 + +### 场景2:dubbo消费端调用dubbo的REST服务 + +这种场景下,和使用其他dubbo的远程调用方式一样,直接在服务提供端和服务消费端共享Java服务接口,并添加spring xml配置(当然也可以用spring/dubbo的annotation配置),即可透明的调用远程REST服务: + +```xml + +``` + +如前所述,这种场景下必须把JAX-RS的annotation添加到服务接口上,这样在dubbo在消费端才能共享相应的REST配置信息,并据之做远程调用: + +```java +@Path("users") +public interface UserService { + + @GET + @Path("{id : \\d+}") + @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) + User getUser(@PathParam("id") Long id); +} +``` + +如果服务接口的annotation中配置了多种数据格式,这里由于两端都是dubbo系统,REST的大量细节被屏蔽了,所以不存在用前述URL后缀之类选择数据格式的可能。目前在这种情况下,排名最靠前的数据格式将直接被使用。 + +因此,我们建议你在定义annotation的时候最好把最合适的数据格式放到前面,比如以上我们是把json放在xml前面,因为json的传输性能优于xml。 + +### 场景3:dubbo的消费端调用非dubbo的REST服务 + +这种场景下,可以直接用场景1中描述的Java的方式来调用REST服务。但其实也可以采用场景2中描述的方式,即更透明的调用REST服务,即使这个服务并不是dubbo提供的。 + +如果用场景2的方式,由于这里REST服务并非dubbo提供,一般也就没有前述的共享的Java服务接口,所以在此我们需要根据外部REST服务的情况,自己来编写Java接口以及相应参数类,并添加JAX-RS、JAXB、Jackson等的annotation,dubbo的REST底层实现会据此去自动生成请求消息,自动解析响应消息等等,从而透明的做远程调用。或者这种方式也可以理解为,我们尝试用JAX-RS的方式去仿造实现一遍外部的REST服务提供端,然后把写成服务接口放到客户端来直接使用,dubbo的REST底层实现就能像调用dubbo的REST服务一样调用其他REST服务。 + +例如,我们要调用如下的外部服务 + +``` +http://api.foo.com/services/users/1001 +http://api.foo.com/services/users/1002 +``` + +获取不同ID的用户资料,返回格式是JSON + +```javascript +{ + "id": 1001, + "name": "Larry" +} +``` + +我们可根据这些信息,编写服务接口和参数类即可: + +```java +@Path("users") +public interface UserService { + + @GET + @Path("{id : \\d+}") + @Produces({MediaType.APPLICATION_JSON}) + User getUser(@PathParam("id") Long id); +} +``` + +```java +public class User implements Serializable { + + private Long id; + + private String name; + + // … +} +``` + +对于spring中的配置,因为这里的REST服务不是dubbo提供的,所以无法使用dubbo的注册中心,直接配置外部REST服务的url地址即可(如多个地址用逗号分隔): + +```xml + +``` + +> 注意:这里协议必须用rest://而不是http://之类。如果外部的REST服务有context path,则在url中也必须添加上(除非你在每个服务接口的@Path annotation中都带上context path),例如上面的/services/。同时这里的services后面必须带上/,这样才能使dubbo正常工作。 + +另外,这里依然可以配置客户端可启动的最大连接数和超时时间: + +```xml + +``` + +### dubbo的消费端在调用REST服务时配置自定义header + +Dubbo进行rest调用的时候,采用的是将RpcContext的attachment转换为header的方式,所以,dubbo消费端可以按以下方式进行自定义header的设置: + +``` + RpcContext.getContext().setAttachment("key1", "value1"); + RpcContext.getContext().setAttachment("key2", "value2"); +``` + +即可设置如下格式的header: + +``` + key1=value1 + key2=value2 +``` + + +## Dubbo中JAX-RS的限制 + +Dubbo中的REST开发是完全兼容标准JAX-RS的,但其支持的功能目前是完整JAX-RS的一个子集,部分因为它要受限于dubbo和spring的特定体系。 + +在dubbo中使用的JAX-RS的局限包括但不限于: + +1. 服务实现只能是singleton的,不能支持per-request scope和per-lookup scope +2. 不支持用@Context annotation对服务的实例字段注入 ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse等等,但可以支持对服务方法参数的注入。但对某些特定REST server实现,(祥见前面的叙述),也不支持对服务方法参数的注入。 + +## REST常见问题解答(REST FAQ) + +### Dubbo REST的服务能和Dubbo注册中心、监控中心集成吗? + +可以的,而且是自动集成的,也就是你在dubbo中开发的所有REST服务都会自动注册到注册中心和监控中心,可以通过它们做管理。 + +但是,只有当REST的消费端也是基于dubbo的时候,注册中心中的许多服务治理操作才能完全起作用。而如果消费端是非dubbo的,自然不受注册中心管理,所以其中很多操作是不会对消费端起作用的。 + +### Dubbo REST中如何实现负载均衡和容错(failover)? + +如果dubbo REST的消费端也是dubbo的,则Dubbo REST和其他dubbo远程调用协议基本完全一样,由dubbo框架透明的在消费端做load balance、failover等等。 + +如果dubbo REST的消费端是非dubbo的,甚至是非java的,则最好配置服务提供端的软负载均衡机制,目前可考虑用LVS、HAProxy、 Nginx等等对HTTP请求做负载均衡。 + +### JAX-RS中重载的方法能够映射到同一URL地址吗? + +http://stackoverflow.com/questions/17196766/can-resteasy-choose-method-based-on-query-params + +### JAX-RS中作POST的方法能够接收多个参数吗? + +http://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects + +## Dubbo当前体系的不足之处(与REST相关的) + +我认为dubbo当前体系中显然也有不少不足之处,这里列出几个与REST有关的、并影响用户使用的问题(不包括内部实现的问题),供参考评论,为下一步重构作准备。 + +### RpcContext的侵入性 + +在前文,前面我们已经提到过RpcContext用法的侵入性,由于它是用单例的方式来访问上下文信息,这完全不符合spring应用的一般风格,不利于应用扩展和单元测试。未来我们可能用依赖注入方式注入一个接口,再用它去访问ThreadLocal中的上下文信息。 + +### Protocol配置的局限性 + +dubbo支持多种远程调用方式,但所有调用方式都是用``来配置的,例如: + +```xml + +``` + +其实,上面很多属性实际上dubbo RPC远程调用方式特有的,很多dubbo中的其它远程调用方式根本就不支持例如server, client, codec, iothreads, accepts, payload等等(当然,有的是条件所限不支持,有的是根本没有必要支持)。这给用户的使用徒增很多困惑,用户也并不知道有些属性(比如做性能调优)添加了实际上是不起作用的。 + +另一方面,各种远程调用方式往往有大量自己独特的配置需要,特别是我们逐步为每种远程调用方式都添加更丰富、更高级的功能,这就不可避免的扩展``中的属性(例如目前我们在REST中已经添加了keepalive和extension两个属性),到最后会导致``臃肿不堪,用户的使用也更加困惑。 + +当然,dubbo中有一种扩展``的方式是用``,但这种方式显然很有局限性,而且用法复杂,缺乏schema校验。 + +所以,最好的方式是为每种远程调用方式设置自己的protocol元素,比如``,``等等,每种元素用XML schema规定自己的属性(当然属性在各种远程调用方式之间能通用是最好的)。 + +如此一来,例如前面提到过的extension配置也可以用更自由的方式,从而更清楚更可扩展(以下只是举例,当然也许有更好的方式): + +```xml + + someInterceptor + someFilter + someDynamicFeature + someEntityProvider + +``` + +### XML命名不符合spring规范 + +dubbo的XML配置中大量命名都不符合spring规范,比如: + +```xml + +``` + +上面threadpool应该改为thread-pool,iothreads应该改为io-threads,单词之间应该用"-"分隔。这虽然看起来是个小问题,但也涉及到了可读性,特别是可扩展性,因为有时候我们不可避免要用更多单词来描述XML元素和属性。 + +其实dubbo本身也是建议遵守spring到XML的命名规范。 + + +## REST最佳实践 + +TODO + +## 性能基准测试 + +### 测试环境 + +粗略如下: + +* 两台独立服务器 +* 4核Intel(R) Xeon(R) CPU E5-2603 0 @ 1.80GHz +* 8G内存 +* 服务器之间网络通过百兆交换机 +* CentOS 5 +* JDK 7 +* Tomcat 7 +* JVM参数-server -Xms1g -Xmx1g -XX:PermSize=64M -XX:+UseConcMarkSweepGC + +### 测试脚本 + +和dubbo自身的基准测试保持接近: + +10个并发客户端持续不断发出请求: + +* 传入嵌套复杂对象(但单个数据量很小),不做任何处理,原样返回 +* 传入50K字符串,不做任何处理,原样返回(TODO:结果尚未列出) + +进行5分钟性能测试。(引用dubbo自身测试的考虑:“主要考察序列化和网络IO的性能,因此服务端无任何业务逻辑。取10并发是考虑到http协议在高并发下对CPU的使用率较高可能会先打到瓶颈。”) + +### 测试结果 + +下面的结果主要对比的是REST和dubbo RPC两种远程调用方式,并对它们作不同的配置,例如: + +* “REST: Jetty + XML + GZIP”的意思是:测试REST,并采用jetty server,XML数据格式,启用GZIP压缩。 +* “Dubbo: hessian2”的意思是:测试dubbo RPC,并采用hessian2序列化方式。 + +针对复杂对象的结果如下(响应时间越小越好,TPS越大越好): + +| 远程调用方式 | 平均响应时间 | 平均TPS(每秒事务数) | +| ----------- | ------------- | ------------- | +| REST: Jetty + JSON | 7.806 | 1280 | +| REST: Jetty + JSON + GZIP | TODO | TODO | +| REST: Jetty + XML | TODO | TODO | +| REST: Jetty + XML + GZIP | TODO | TODO | +| REST: Tomcat + JSON | 2.082 | 4796 | +| REST: Netty + JSON | 2.182 | 4576 | +| Dubbo: FST | 1.211 | 8244 | +| Dubbo: kyro | 1.182 | 8444 | +| Dubbo: dubbo serialization | 1.43 | 6982 | +| Dubbo: hessian2 | 1.49 | 6701 | +| Dubbo: fastjson | 1.572 | 6352 | + +![rt](/imgs/user/rt.png) + +![tps](/imgs/user/tps.png) + + +仅就目前的结果,一点简单总结: + +* dubbo RPC(特别是基于高效java序列化方式如kryo,fst)比REST的响应时间和吞吐量都有较显著优势,内网的dubbo系统之间优先选择dubbo RPC。 +* 在REST的实现选择上,仅就性能而言,目前tomcat7和netty最优(当然目前使用的jetty和netty版本都较低)。tjws和sun http server在性能测试中表现极差,平均响应时间超过200ms,平均tps只有50左右(为了避免影响图片效果,没在上面列出)。 +* 在REST中JSON数据格式性能优于XML(数据暂未在以上列出)。 +* 在REST中启用GZIP对企业内网中的小数据量复杂对象帮助不大,性能反而有下降(数据暂未在以上列出)。 + +## 性能优化建议 + +如果将dubbo REST部署到外部Tomcat上,并配置server="servlet",即启用外部的tomcat来做为rest server的底层实现,则最好在tomcat上添加如下配置: + +```xml + +``` + +特别是maxKeepAliveRequests="-1",这个配置主要是保证tomcat一直启用http长连接,以提高REST调用性能。但是请注意,如果REST消费端不是持续的调用REST服务,则一直启用长连接未必是最好的做法。另外,一直启用长连接的方式一般不适合针对普通webapp,更适合这种类似rpc的场景。所以为了高性能,在tomcat中,dubbo REST应用和普通web应用最好不要混合部署,而应该用单独的实例。 + +TODO more contents to add + +## 扩展讨论 + +### REST与Thrift、Protobuf等的对比 + +TODO + +### REST与传统WebServices的对比 + +TODO + +### JAX-RS与Spring MVC的对比 + +初步看法,摘自http://www.infoq.com/cn/news/2014/10/dubbox-open-source?utm_source=infoq&utm_medium=popular_links_homepage#theCommentsSection + +> 谢谢,对于jax-rs和spring mvc,其实我对spring mvc的rest支持还没有太深入的看过,说点初步想法,请大家指正: +> +> spring mvc也支持annotation的配置,其实和jax-rs看起来是非常非常类似的。 +> +> 我个人认为spring mvc相对更适合于面向web应用的restful服务,比如被AJAX调用,也可能输出HTML之类的,应用中还有页面跳转流程之类,spring mvc既可以做好正常的web页面请求也可以同时处理rest请求。但总的来说这个restful服务是在展现层或者叫web层之类实现的 +> +> 而jax-rs相对更适合纯粹的服务化应用,也就是传统Java EE中所说的中间层服务,比如它可以把传统的EJB发布成restful服务。在spring应用中,也就把spring中充当service之类的bean直接发布成restful服务。总的来说这个restful服务是在业务、应用层或者facade层。而MVC层次和概念在这种做比如(后台)服务化的应用中通常是没有多大价值的。 +> +> 当然jax-rs的有些实现比如jersey,也试图提供mvc支持,以更好的适应上面所说的web应用,但应该是不如spring mvc。 +> +> 在dubbo应用中,我想很多人都比较喜欢直接将一个本地的spring service bean(或者叫manager之类的)完全透明的发布成远程服务,则这里用JAX-RS是更自然更直接的,不必额外的引入MVC概念。当然,先不讨论透明发布远程服务是不是最佳实践,要不要添加facade之类。 +> +> 当然,我知道在dubbo不支持rest的情况下,很多朋友采用的架构是spring mvc restful调用dubbo (spring) service来发布restful服务的。这种方式我觉得也非常好,只是如果不修改spring mvc并将其与dubbo深度集成,restful服务不能像dubbo中的其他远程调用协议比如webservices、dubbo rpc、hessian等等那样,享受诸多高级的服务治理的功能,比如:注册到dubbo的服务注册中心,通过dubbo监控中心监控其调用次数、TPS、响应时间之类,通过dubbo的统一的配置方式控制其比如线程池大小、最大连接数等等,通过dubbo统一方式做服务流量控制、权限控制、频次控制。另外spring mvc仅仅负责服务端,而在消费端,通常是用spring restTemplate,如果restTemplate不和dubbo集成,有可能像dubbo服务客户端那样自动或者人工干预做服务降级。如果服务端消费端都是dubbo系统,通过spring的rest交互,如果spring rest不深度整合dubbo,则不能用dubbo统一的路由分流等功能。 +> +> 当然,其实我个人认为这些东西不必要非此即彼的。我听说spring创始人rod johnson总是爱说一句话,the customer is always right,其实与其非要探讨哪种方式更好,不如同时支持两种方式就是了,所以原来在文档中也写过计划支持spring rest annoation,只是不知道具体可行性有多高。 + +## 未来 + +稍后可能要实现的功能: + +* spring mvc的rest annotation支持 +* 安全 +* OAuth +* 异步调用 +* 完善gzip +* 最大payload限制 + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/_index.md new file mode 100755 index 000000000000..68ef52230b40 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Rmi协议" +linkTitle: "Rmi协议" +weight: 8 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/guide.md new file mode 100644 index 000000000000..e7dc335e25ae --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/guide.md @@ -0,0 +1,86 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +RMI 协议采用 JDK 标准的 `java.rmi.*` 实现,采用阻塞式短连接和 JDK 标准序列化方式。 + +注意:如果正在使用 RMI 提供服务给外部访问 [^1],同时应用里依赖了老的 common-collections 包 [^2] 的情况下,存在反序列化安全风险 [^3]。 + +## 特性 + +* 连接个数:多连接 +* 连接方式:短连接 +* 传输协议:TCP +* 传输方式:同步传输 +* 序列化:Java 标准二进制序列化 +* 适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件。 +* 适用场景:常规远程服务方法调用,与原生RMI服务互操作 + +## 约束 + +* 参数及返回值需实现 `Serializable` 接口 +* dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置:`-Dsun.rmi.transport.tcp.responseTimeout=3000`,参见下面的 RMI 配置 + + +## RMI配置 + +```sh +java -Dsun.rmi.transport.tcp.responseTimeout=3000 +``` +更多 RMI 优化参数请查看 [JDK 文档](https://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html) + + +## 接口 + +如果服务接口继承了 `java.rmi.Remote` 接口,可以和原生 RMI 互操作,即: + +* 提供者用 Dubbo 的 RMI 协议暴露服务,消费者直接用标准 RMI 接口调用, +* 或者提供方用标准 RMI 暴露服务,消费方用 Dubbo 的 RMI 协议调用。 + +如果服务接口没有继承 `java.rmi.Remote` 接口: + +* 缺省 Dubbo 将自动生成一个 `com.xxx.XxxService$Remote` 的接口,并继承 `java.rmi.Remote` 接口,并以此接口暴露服务, +* 但如果设置了 ``,将不生成 `$Remote` 接口,而使用 Spring 的 `RmiInvocationHandler` 接口暴露服务,和 Spring 兼容。 + +## 配置 + +定义 RMI 协议: + +```xml + +``` + +设置默认协议: + +```xml + +``` + +设置某个服务的协议: + +```xml + +``` + +多端口: + +```xml + + + + +``` + +Spring 兼容性: + +```xml + +``` + +[^1]: 公司内网环境应该不会有攻击风险 +[^2]: dubbo 不会依赖这个包,请排查自己的应用有没有使用 +[^3]: 请检查应用:将 commons-collections3 请升级到 [3.2.2](https://commons.apache.org/proper/commons-collections/release_3_2_2.html);将 commons-collections4 请升级到 [4.1](https://commons.apache.org/proper/commons-collections/release_4_1.html)。新版本的 commons-collections 解决了该问题 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/rmi/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/_index.md new file mode 100755 index 000000000000..9a9fc5aa721c --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Thrift协议" +linkTitle: "Thrift协议" +weight: 7 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/guide.md new file mode 100644 index 000000000000..264112e7927d --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/guide.md @@ -0,0 +1,45 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + + +当前 dubbo 支持的 thrift 协议是对 thrift 原生协议 [^1] 的扩展,在原生协议的基础上添加了一些额外的头信息,比如 service name,magic number 等。 + +{{% alert title="提示" color="primary" %}} +`2.3.0` 以上版本支持 +{{% /alert %}} + +使用 dubbo thrift 协议同样需要使用 thrift 的 idl compiler 编译生成相应的 java 代码,后续版本中会在这方面做一些增强。 + +## 依赖 + +```xml + + org.apache.thrift + libthrift + 0.8.0 + +``` + +## 配置 + +所有服务共用一个端口 [^2]: + +```xml + +``` + +## 使用 + +可以参考 [dubbo 项目中的示例代码](https://github.com/apache/dubbo/tree/master/dubbo-rpc/dubbo-rpc-thrift/src/test/java/org/apache/dubbo/rpc/protocol/thrift) + +## 常见问题 + +* Thrift 不支持 null 值,即:不能在协议中传递 null 值 + +[^1]: [Thrift](http://thrift.apache.org) 是 Facebook 捐给 Apache 的一个 RPC 框架 +[^2]: 与原生Thrift不兼容 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/thrift/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/_index.md new file mode 100755 index 000000000000..d6933892ab73 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Triple协议" +linkTitle: "Triple协议" +weight: 3 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/guide.md new file mode 100644 index 000000000000..0754620bc1dc --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/guide.md @@ -0,0 +1,283 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + +Triple 协议是 Dubbo3 的主力协议,完整兼容 gRPC over HTTP/2,并在协议层面扩展了负载均衡和流量控制相关机制。本文档旨在指导用户正确的使用 Triple 协议。 + +在开始前,需要决定服务使用的序列化方式,如果为新服务,推荐使用 protobuf 作为默认序列化,在性能和跨语言上的效果都会更好。如果是原有服务想进行协议升级,Triple 协议也已经支持其他序列化方式,如 Hessian / JSON 等 + + + +### Protobuf + +1. 编写 IDL 文件 + ```protobuf + syntax = "proto3"; + + option java_multiple_files = true; + option java_package = "org.apache.dubbo.hello"; + option java_outer_classname = "HelloWorldProto"; + option objc_class_prefix = "HLW"; + + package helloworld; + + // The request message containing the user's name. + message HelloRequest { + string name = 1; + } + + // The response message containing the greetings + message HelloReply { + string message = 1; + } + ``` + +2. 添加编译 protobuf 的 extension 和 plugin (以 maven 为例) + ```xml + + + kr.motd.maven + os-maven-plugin + 1.6.1 + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier} + triple-java + build/generated/source/proto/main/java + + + + + compile + test-compile + + + + + + ``` + +3. 构建/ 编译生成 protobuf Message 类 + ```shell + $ mvn clean install + ``` + +### Unary 方式 + +4. 编写 Java 接口 + ```java + import org.apache.dubbo.hello.HelloReply; + import org.apache.dubbo.hello.HelloRequest; + + public interface IGreeter { + /** + *
+         *  Sends a greeting
+         * 
+ */ + HelloReply sayHello(HelloRequest request); + + } + ``` + +5. 创建 Provider + ```java + public static void main(String[] args) throws InterruptedException { + ServiceConfig service = new ServiceConfig<>(); + service.setInterface(IGreeter.class); + service.setRef(new IGreeter1Impl()); + // 这里需要显示声明使用的协议为triple + service.setProtocol(new ProtocolConfig(CommonConstants.TRIPLE, 50051)); + service.setApplication(new ApplicationConfig("demo-provider")); + service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); + service.export(); + System.out.println("dubbo service started"); + new CountDownLatch(1).await(); + } + + ``` + + +6. 创建 Consumer + + ```java + public static void main(String[] args) throws IOException { + ReferenceConfig ref = new ReferenceConfig<>(); + ref.setInterface(IGreeter.class); + ref.setCheck(false); + ref.setInterface(IGreeter.class); + ref.setCheck(false); + ref.setProtocol(CommonConstants.TRIPLE); + ref.setLazy(true); + ref.setTimeout(100000); + ref.setApplication(new ApplicationConfig("demo-consumer")); + ref.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); + final IGreeter iGreeter = ref.get(); + + System.out.println("dubbo ref started"); + try { + final HelloReply reply = iGreeter.sayHello(HelloRequest.newBuilder() + .setName("name") + .build()); + TimeUnit.SECONDS.sleep(1); + System.out.println("Reply:" + reply); + } catch (Throwable t) { + t.printStackTrace(); + } + System.in.read(); + } + ``` + +7. 运行 Provider 和 Consumer ,可以看到请求正常返回了 + > Reply:message: "name" + +### stream 方式 + +8. 编写 Java 接口 + ```java + import org.apache.dubbo.hello.HelloReply; + import org.apache.dubbo.hello.HelloRequest; + + public interface IGreeter { + /** + *
+     	*  Sends greeting by stream
+     	* 
+ */ + StreamObserver sayHello(StreamObserver replyObserver); + + } + ``` + +9. 编写实现类 + ```java + public class IStreamGreeterImpl implements IStreamGreeter { + + @Override + public StreamObserver sayHello(StreamObserver replyObserver) { + + return new StreamObserver() { + private List replyList = new ArrayList<>(); + + @Override + public void onNext(HelloRequest helloRequest) { + System.out.println("onNext receive request name:" + helloRequest.getName()); + replyList.add(HelloReply.newBuilder() + .setMessage("receive name:" + helloRequest.getName()) + .build()); + } + + @Override + public void onError(Throwable cause) { + System.out.println("onError"); + replyObserver.onError(cause); + } + + @Override + public void onCompleted() { + System.out.println("onComplete receive request size:" + replyList.size()); + for (HelloReply reply : replyList) { + replyObserver.onNext(reply); + } + replyObserver.onCompleted(); + } + }; + } + } + ``` + +10. 创建 Provider + + ```java + public class StreamProvider { + public static void main(String[] args) throws InterruptedException { + ServiceConfig service = new ServiceConfig<>(); + service.setInterface(IStreamGreeter.class); + service.setRef(new IStreamGreeterImpl()); + service.setProtocol(new ProtocolConfig(CommonConstants.TRIPLE, 50051)); + service.setApplication(new ApplicationConfig("stream-provider")); + service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); + service.export(); + System.out.println("dubbo service started"); + new CountDownLatch(1).await(); + } + } + ``` + +11. 创建 Consumer + + ```java + public class StreamConsumer { + public static void main(String[] args) throws InterruptedException, IOException { + ReferenceConfig ref = new ReferenceConfig<>(); + ref.setInterface(IStreamGreeter.class); + ref.setCheck(false); + ref.setProtocol(CommonConstants.TRIPLE); + ref.setLazy(true); + ref.setTimeout(100000); + ref.setApplication(new ApplicationConfig("stream-consumer")); + ref.setRegistry(new RegistryConfig("zookeeper://mse-6e9fda00-p.zk.mse.aliyuncs.com:2181")); + final IStreamGreeter iStreamGreeter = ref.get(); + + System.out.println("dubbo ref started"); + try { + + StreamObserver streamObserver = iStreamGreeter.sayHello(new StreamObserver() { + @Override + public void onNext(HelloReply reply) { + System.out.println("onNext"); + System.out.println(reply.getMessage()); + } + + @Override + public void onError(Throwable throwable) { + System.out.println("onError:" + throwable.getMessage()); + } + + @Override + public void onCompleted() { + System.out.println("onCompleted"); + } + }); + + streamObserver.onNext(HelloRequest.newBuilder() + .setName("tony") + .build()); + + streamObserver.onNext(HelloRequest.newBuilder() + .setName("nick") + .build()); + + streamObserver.onCompleted(); + } catch (Throwable t) { + t.printStackTrace(); + } + System.in.read(); + } + } + ``` + +12. 运行 Provider 和 Consumer ,可以看到请求正常返回了 + > onNext\ + > receive name:tony\ + > onNext\ + > receive name:nick\ + > onCompleted + +### 其他序列化方式 +省略上文中的 1-3 步,指定 Provider 和 Consumer 使用的协议即可完成协议升级。 + +### 示例程序 +本文的示例程序可以在 [triple-samples](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple) 找到 + + + \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/triple/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/_index.md new file mode 100755 index 000000000000..059ef4ba98c9 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "Webservice协议" +linkTitle: "Webservice协议" +weight: 11 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/guide.md new file mode 100644 index 000000000000..afb1ee2e3f07 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/guide.md @@ -0,0 +1,119 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +基于 WebService 的远程调用协议,基于 [Apache CXF](http://cxf.apache.org) [^1] 的 `frontend-simple` 和 `transports-http` 实现。 + +{{% alert title="提示" color="primary" %}} +`2.3.0` 以上版本支持 +{{% /alert %}} + +可以和原生 WebService 服务互操作,即: + +* 提供者用 Dubbo 的 WebService 协议暴露服务,消费者直接用标准 WebService 接口调用, +* 或者提供方用标准 WebService 暴露服务,消费方用 Dubbo 的 WebService 协议调用。 + +## 依赖 + +```xml + + org.apache.cxf + cxf-rt-frontend-simple + 2.6.1 + + + org.apache.cxf + cxf-rt-transports-http + 2.6.1 + +``` + +## 特性 + +* 连接个数:多连接 +* 连接方式:短连接 +* 传输协议:HTTP +* 传输方式:同步传输 +* 序列化:SOAP 文本序列化 +* 适用场景:系统集成,跨语言调用 + +## 约束 + +* 参数及返回值需实现 `Serializable` 接口 +* 参数尽量使用基本类型和 POJO + +## 配置 + +配置协议: + +```xml + +``` + +配置默认协议: + +```xml + +``` + +配置服务协议: + +```xml + +``` + +多端口: + +```xml + + +``` + +直连: + +```xml + +``` + +WSDL: + +``` +http://10.20.153.10:8080/com.foo.HelloWorld?wsdl +``` + +Jetty Server (默认): + +```xml + +``` + +Servlet Bridge Server (推荐): + +```xml + +``` + +配置 DispatcherServlet: + +```xml + + dubbo + org.apache.dubbo.remoting.http.servlet.DispatcherServlet + 1 + + + dubbo + /* + +``` + +注意,如果使用 servlet 派发请求: + +* 协议的端口 `` 必须与 servlet 容器的端口相同, +* 协议的上下文路径 `` 必须与 servlet 应用的上下文路径相同。 + +[^1]: CXF 是 Apache 开源的一个 RPC 框架,由 Xfire 和 Celtix 合并而来 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/overview.md new file mode 100644 index 000000000000..ed5e7234ba58 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/protocol/webservice/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "协议概述" +linkTitle: "协议概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/qos/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/qos/_index.md new file mode 100755 index 000000000000..dd8e0eba9a15 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/qos/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "QOS 操作手册" +linkTitle: "QOS 操作手册" +weight: 3 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/qos/command.md b/content/zh/docs3-building/java-sdk/reference-manual/qos/command.md new file mode 100644 index 000000000000..40eaa6cc6c95 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/qos/command.md @@ -0,0 +1,243 @@ +--- +type: docs +title: "QOS 命令手册" +linkTitle: "QOS 命令手册" +weight: 2 +description: "新版本 telnet 命令使用说明" +--- + +dubbo `2.5.8` 新版本增加了 QOS 模块,提供了新的 telnet 命令支持。 + +## 端口 +新版本的 telnet 端口 与 dubbo 协议的端口是不同的端口,默认为 `22222`,可通过配置文件`dubbo.properties` 修改: + +``` +dubbo.application.qos-port=33333 +``` + +或者通过设置 JVM 参数: + +``` +-Ddubbo.application.qos-port=33333 +``` + +## 安全 + +默认情况下,dubbo 接收任何主机发起的命令,可通过配置文件`dubbo.properties` 修改: + +``` +dubbo.application.qos-accept-foreign-ip=false +``` + +或者通过设置 JVM 参数: + +``` +-Ddubbo.application.qos-accept-foreign-ip=false +``` + +拒绝远端主机发出的命令,只允许服务本机执行 + + +## telnet 与 http 协议 + +telnet 模块现在同时支持 http 协议和 telnet 协议,方便各种情况的使用 + +示例如下: + +``` +➜ ~ telnet localhost 22222 +Trying ::1... +telnet: connect to address ::1: Connection refused +Trying 127.0.0.1... +Connected to localhost. +Escape character is '^]'. + ████████▄ ███ █▄ ▀█████████▄ ▀█████████▄ ▄██████▄ + ███ ▀███ ███ ███ ███ ███ ███ ███ ███ ███ + ███ ███ ███ ███ ███ ███ ███ ███ ███ ███ + ███ ███ ███ ███ ▄███▄▄▄██▀ ▄███▄▄▄██▀ ███ ███ + ███ ███ ███ ███ ▀▀███▀▀▀██▄ ▀▀███▀▀▀██▄ ███ ███ + ███ ███ ███ ███ ███ ██▄ ███ ██▄ ███ ███ + ███ ▄███ ███ ███ ███ ███ ███ ███ ███ ███ + ████████▀ ████████▀ ▄█████████▀ ▄█████████▀ ▀██████▀ + + +dubbo>ls +As Provider side: ++----------------------------------+---+ +| Provider Service Name |PUB| ++----------------------------------+---+ +|org.apache.dubbo.demo.DemoService| N | ++----------------------------------+---+ +As Consumer side: ++---------------------+---+ +|Consumer Service Name|NUM| ++---------------------+---+ + +dubbo> +``` + + +``` +➜ ~ curl "localhost:22222/ls?arg1=xxx&arg2=xxxx" +As Provider side: ++----------------------------------+---+ +| Provider Service Name |PUB| ++----------------------------------+---+ +|org.apache.dubbo.demo.DemoService| N | ++----------------------------------+---+ +As Consumer side: ++---------------------+---+ +|Consumer Service Name|NUM| ++---------------------+---+ +``` + +## 支持的命令 + +### ls 列出消费者和提供者 + +``` +dubbo>ls +As Provider side: ++----------------------------------+---+ +| Provider Service Name |PUB| ++----------------------------------+---+ +|org.apache.dubbo.demo.DemoService| Y | ++----------------------------------+---+ +As Consumer side: ++---------------------+---+ +|Consumer Service Name|NUM| ++---------------------+---+ +``` + +列出 dubbo 的所提供的服务和消费的服务,以及消费的服务地址数 + +### Online 上线服务命令 + +当使用延迟发布功能的时候(通过设置 org.apache.dubbo.config.AbstractServiceConfig#register 为 false),后续需要上线的时候,可通过 Online 命令 + +``` +//上线所有服务 +dubbo>online +OK + +//根据正则,上线部分服务 +dubbo>online com.* +OK +``` + +常见使用场景: + + - 当线上的 QPS 比较高的时候,当刚重启机器的时候,由于没有进行JIT 预热或相关资源没有预热,可能会导致大量超时,这个时候,可通过分批发布服务,逐渐加大流量 + - 当由于某台机器由于某种原因,需要下线服务,然后又需要重新上线服务 + + + +### Offline 下线服务命令 + +由于故障等原因,需要临时下线服务保持现场,可以使用 Offline 下线命令。 + + +``` +//下线所有服务 +dubbo>offline +OK + +//根据正则,下线部分服务 +dubbo>offline com.* +OK +``` + +### help 命令 + + + +``` +//列出所有命令 +dubbo>help + +//列出单个命令的具体使用情况 +dubbo>help online ++--------------+----------------------------------------------------------------------------------+ +| COMMAND NAME | online | ++--------------+----------------------------------------------------------------------------------+ +| EXAMPLE | online dubbo | +| | online xx.xx.xxx.service | ++--------------+----------------------------------------------------------------------------------+ + +dubbo> +``` + +## 相关参数说明 + +QoS提供了一些启动参数,来对启动进行配置,他们主要包括: + +| 参数 | 说明 | 默认值 | +| ------------------ | ----------------- | ------ | +| qos-enable | 是否启动QoS | true | +| qos-port | 启动QoS绑定的端口 | 22222 | +| qos-accept-foreign-ip | 是否允许远程访问 | false | + +> 注意,从2.6.4/2.7.0开始,qos-accept-foreign-ip默认配置改为false,如果qos-accept-foreign-ip设置为true,有可能带来安全风险,请仔细评估后再打开。 + +QoS参数可以通过如下方式进行配置 + +* 系统属性 +* dubbo.properties +* XML方式 +* Spring-boot自动装配方式 + +其中,上述方式的优先顺序为系统属性 > dubbo.properties > XML/Spring-boot自动装配方式。 + +### 使用系统属性方式进行配置 + +``` +-Ddubbo.application.qos-enable=true +-Ddubbo.application.qos-port=33333 +-Ddubbo.application.qos-accept-foreign-ip=false +``` + +### 使用dubbo.properties文件进行配置 + +在项目的`src/main/resources`目录下添加dubbo.properties文件,内容如下: +``` +dubbo.application.qos-enable=true +dubbo.application.qos-port=33333 +dubbo.application.qos-accept-foreign-ip=false +``` + +### 使用XML方法进行配置 + +如果要通过XML配置响应的QoS相关的参数,可以进行如下配置: + +```xml + + + + + + + + + + + + +``` + +### 使用spring-boot自动装配方式配置 + +如果是spring-boot的应用,可以在`application.properties`或者`application.yml`上配置: + +``` +dubbo.application.qos-enable=true +dubbo.application.qos-port=33333 +dubbo.application.qos-accept-foreign-ip=false +``` + + + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/qos/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/qos/overview.md new file mode 100644 index 000000000000..a8efb54f6966 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/qos/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "QOS 概述" +linkTitle: "QOS 概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/_index.md new file mode 100644 index 000000000000..1c0c3c19970a --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "注册中心参考手册" +linkTitle: "注册中心参考手册" +weight: 5 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/_index.md new file mode 100644 index 000000000000..dcf9708c305c --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "广播" +linkTitle: "广播" +weight: 5 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/guide.md new file mode 100644 index 000000000000..643a01127eac --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/guide.md @@ -0,0 +1,46 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。 + +![/user-guide/images/multicast.jpg](/imgs/user/multicast.jpg) + +0. 提供方启动时广播自己的地址 +1. 消费方启动时广播订阅请求 +2. 提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了 `unicast=false`,则广播给订阅者 +3. 消费方收到提供方地址时,连接该地址进行 RPC 调用。 + +组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255 + +## 配置 + +```xml + +``` + +或 + +```xml + +``` + +为了减少广播量,Dubbo 缺省使用单播发送提供者地址信息给消费者,如果一个机器上同时启了多个消费者进程,消费者需声明 `unicast=false`,否则只会有一个消费者能收到消息; 当服务者和消费者运行在同一台机器上,消费者同样需要声明`unicast=false`,否则消费者无法收到消息,导致No provider available for the service异常: + +```xml + + + +``` + +或 + +```xml + + + +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/multicast/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/_index.md new file mode 100644 index 000000000000..6ed4ddc4e412 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "多注册中心" +linkTitle: "多注册中心" +weight: 6 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/guide.md new file mode 100644 index 000000000000..37430b92141b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/guide.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/multiple-registry/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/_index.md new file mode 100644 index 000000000000..7fed8cf789af --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Nacos" +linkTitle: "Nacos" +weight: 3 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/guide.md new file mode 100644 index 000000000000..a27ab64cac9f --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/guide.md @@ -0,0 +1,135 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + +Nacos 是 Dubbo 生态系统中重要的注册中心实现,其中 [`dubbo-registry-nacos`](https://github.com/apache/incubator-dubbo/tree/master/dubbo-registry/dubbo-registry-nacos) 则是 Dubbo 融合 Nacos 注册中心的实现。 + +## 预备工作 + +当您将 [dubbo-registry-nacos](https://github.com/apache/incubator-dubbo/tree/master/dubbo-registry/dubbo-registry-nacos) 整合到您的 Dubbo 工程之前,请确保后台已经启动 Nacos 服务。如果您尚且不熟悉 Nacos 的基本使用的话,可先行参考 [Nacos 快速入门](https://nacos.io/en-us/docs/quick-start.html)。建议使用 Nacos `1.0.0` 及以上的版本。 + +## 快速上手 + +Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单,大致步骤可分为“增加 Maven 依赖”以及“配置注册中心“。 + + +### 增加 Maven 依赖 + +首先,您需要将 `dubbo-registry-nacos` 的 Maven 依赖添加到您的项目 `pom.xml` 文件中,并且强烈地推荐您使用 Dubbo `2.6.5`: + +```xml + + + ... + + + + com.alibaba + dubbo-registry-nacos + 0.0.2 + + + + + com.alibaba.nacos + nacos-client + [0.6.1,) + + + + + com.alibaba + dubbo + 2.6.5 + + + + + com.alibaba.spring + spring-context-support + 1.0.2 + + + ... + + +``` + +当项目中添加 `dubbo-registry-nacos` 后,您无需显式地编程实现服务发现和注册逻辑,实际实现由该三方包提供,接下来配置 Naocs 注册中心。 + +### 配置注册中心 + +假设您 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 外部化配置 + +> [参考](https://mercyblitz.github.io/2018/01/18/Dubbo-%E5%A4%96%E9%83%A8%E5%8C%96%E9%85%8D%E7%BD%AE/) + +Dubbo Spring 外部化配置是由 Dubbo `2.5.8` 引入的新特性,可通过 Spring `Environment` 属性自动地生成并绑定 Dubbo 配置 Bean,实现配置简化,并且降低微服务开发门槛。 + +假设您的 Nacos Server 同样运行在服务器 `10.20.153.10` 上,并使用默认 Nacos 服务端口 `8848`,您只需将 `dubbo.registry.address` 属性调整如下: + +```properties +## 其他属性保持不变 + +## Nacos registry address +dubbo.registry.address = nacos://10.20.153.10:8848 +... +``` + +随后,重启您的 Dubbo 应用,Dubbo 的服务提供和消费信息在 Nacos 控制台中可以显示: + +![dubbo-registry-nacos-1.png](/imgs/blog/dubbo-registry-nacos-1.png) + + + +如图所示,服务名前缀为 `providers:` 的信息为服务提供者的元信息,`consumers:` 则代表服务消费者的元信息。点击“**详情**”可查看服务状态详情: + +![image-dubbo-registry-nacos-2.png](/imgs/blog/dubbo-registry-nacos-2.png) + +如果您正在使用 Spring XML 配置文件装配 Dubbo 注册中心的话,请参考下一节。 + +### Spring XML 配置文件 + +与 [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/) 配置类似,只需要调整 `address` 属性配置即可: + +```xml + + + + + + + + + ... + +``` + + + +重启 Dubbo 应用后,您同样也能发现服务提供方和消费方的注册元信息呈现在 Nacos 控制台中: + +![dubbo-registry-nacos-3.png](/imgs/blog/dubbo-registry-nacos-3.png) + + +**附加信息**: 在nacos-server@`1.0.0`版本后,支持客户端通过上报一些包含特定的元数据的实例到服务端来控制实例的一些行为。 + +例如: +`preserved.heart.beat.timeout` : 该实例在不发送心跳后,从健康到不健康的时间。(单位:毫秒) +`preserved.ip.delete.timeout` : 该实例在不发送心跳后,被服务端下掉该实例的时间。(单位:毫秒) +`preserved.heart.beat.interval` : 该实例在客户端上报心跳的间隔时间。(单位:毫秒) +`preserved.instance.id.generator`: 该实例的id生成策略,值为`snowflake`时,从0开始增加。 +`preserved.register.source` : 保留键,目前未使用。 + +该功能将在Dubbo@`2.7.10`开始支持,通过在address中增加参数来进行配置. +例如: `nacos://10.20.153.10:8848?preserved.heart.beat.timeout=15000&preserved.ip.delete.timeout=30000&preserved.heart.beat.interval=10000` + + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/nacos/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/overview/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/overview/_index.md new file mode 100644 index 000000000000..15061f9901ac --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/overview/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "注册中心概述" +linkTitle: "注册中心概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/_index.md new file mode 100644 index 000000000000..34200f813988 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Redis" +linkTitle: "Redis" +weight: 4 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/guide.md new file mode 100644 index 000000000000..206f48c01459 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/guide.md @@ -0,0 +1,86 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +基于 Redis [^1] 实现的注册中心。 + +{{% alert title="提示" color="primary" %}} +从 `2.1.0` 版本开始支持。 + +Redis 过期数据通过心跳的方式检测脏数据,服务器时间必须同步,并且对服务器有一定压力,否则过期检测会不准确 +{{% /alert %}} + +![/user-guide/images/dubbo-redis-registry.jpg](/imgs/user/dubbo-redis-registry.jpg) + +使用 Redis 的 Key/Map 结构存储数据结构: + +* 主 Key 为服务名和类型 +* Map 中的 Key 为 URL 地址 +* Map 中的 Value 为过期时间,用于判断脏数据,脏数据由监控中心删除 [^3] + +使用 Redis 的 Publish/Subscribe 事件通知数据变更: + +* 通过事件的值区分事件类型:`register`, `unregister`, `subscribe`, `unsubscribe` +* 普通消费者直接订阅指定服务提供者的 Key,只会收到指定服务的 `register`, `unregister` 事件 +* 监控中心通过 `psubscribe` 功能订阅 `/dubbo/*`,会收到所有服务的所有变更事件 + +调用过程: + +0. 服务提供方启动时,向 `Key:/dubbo/com.foo.BarService/providers` 下,添加当前提供者的地址 +1. 并向 `Channel:/dubbo/com.foo.BarService/providers` 发送 `register` 事件 +2. 服务消费方启动时,从 `Channel:/dubbo/com.foo.BarService/providers` 订阅 `register` 和 `unregister` 事件 +3. 并向 `Key:/dubbo/com.foo.BarService/consumers` 下,添加当前消费者的地址 +4. 服务消费方收到 `register` 和 `unregister` 事件后,从 `Key:/dubbo/com.foo.BarService/providers` 下获取提供者地址列表 +5. 服务监控中心启动时,从 `Channel:/dubbo/*` 订阅 `register` 和 `unregister`,以及 `subscribe` 和`unsubsribe `事件 +6. 服务监控中心收到 `register` 和 `unregister` 事件后,从 `Key:/dubbo/com.foo.BarService/providers` 下获取提供者地址列表 +7. 服务监控中心收到 `subscribe` 和 `unsubsribe` 事件后,从 `Key:/dubbo/com.foo.BarService/consumers` 下获取消费者地址列表 + + +## 配置 + +```xml + +``` + +或 + +```xml + +``` + +或 + +```xml + +``` + +或 + +```xml + +``` + +## 选项 + +* 可通过 `` 设置 redis 中 key 的前缀,缺省为 `dubbo`。 +* 可通过 `` 设置 redis 集群策略,缺省为 `failover`: + * `failover`: 只写入和读取任意一台,失败时重试另一台,需要服务器端自行配置数据同步 + * `replicate`: 在客户端同时写入所有服务器,只读取单台,服务器端不需要同步,注册中心集群增大,性能压力也会更大 + + +## 可靠性声明 + +阿里内部并没有采用 Redis 做为注册中心,而是使用自己实现的基于数据库的注册中心,即:Redis 注册中心并没有在阿里内部长时间运行的可靠性保障,此 Redis 桥接实现只为开源版本提供,其可靠性依赖于 Redis 本身的可靠性。 + + +## 安装 + + +安装方式参见: [Redis安装手册]( ../../../admin/install/redis.md),只需搭一个原生的 Redis 服务器,并将 [Quick Start](../../preface/usage.md) 中 Provider 和 Consumer 里的 `conf/dubbo.properties` 中的 `dubbo.registry.address` 的值改为 `redis://127.0.0.1:6379` 即可使用。 + + +[^1]: [Redis](http://redis.io) 是一个高效的 KV 存储服务器 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/redis/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/_index.md new file mode 100644 index 000000000000..2864a6f0b145 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/_index.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "Zookeeper" +linkTitle: "Zookeeper" +weight: 2 +description: "" +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/guide.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/guide.md new file mode 100644 index 000000000000..5e4123f03a1f --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/guide.md @@ -0,0 +1,171 @@ +--- +type: docs +title: "使用说明" +linkTitle: "使用说明" +weight: 2 +--- + + +[Zookeeper](http://zookeeper.apache.org) 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用 [^1]。 + +![/user-guide/images/zookeeper.jpg](/imgs/user/zookeeper.jpg) + +流程说明: + +* 服务提供者启动时: 向 `/dubbo/com.foo.BarService/providers` 目录下写入自己的 URL 地址 +* 服务消费者启动时: 订阅 `/dubbo/com.foo.BarService/providers` 目录下的提供者 URL 地址。并向 `/dubbo/com.foo.BarService/consumers` 目录下写入自己的 URL 地址 +* 监控中心启动时: 订阅 `/dubbo/com.foo.BarService` 目录下的所有提供者和消费者 URL 地址。 + +支持以下功能: + +* 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息 +* 当注册中心重启时,能自动恢复注册数据,以及订阅请求 +* 当会话过期时,能自动恢复注册数据,以及订阅请求 +* 当设置 `` 时,记录失败注册和订阅请求,后台定时重试 +* 可通过 `` 设置 zookeeper 登录信息 +* 可通过 `` 设置 zookeeper 的根节点,不配置将使用默认的根节点。 +* 支持 `*` 号通配符 ``,可订阅服务的所有分组和所有版本的提供者 + +## 使用 + +在 provider 和 consumer 中增加 zookeeper 客户端 jar 包依赖: + +```xml + + org.apache.zookeeper + zookeeper + 3.8.0 + +``` + +或直接[下载](http://repo1.maven.org/maven2/org/apache/zookeeper/zookeeper)。 + +Dubbo 支持 zkclient 和 curator 两种 Zookeeper 客户端实现: + +**注意:在2.7.x的版本中已经移除了zkclient的实现,如果要使用zkclient客户端,需要自行拓展** + +### 使用 zkclient 客户端 + +从 `2.2.0` 版本开始缺省为 zkclient 实现,以提升 zookeeper 客户端的健壮性。[zkclient](https://github.com/sgroschupf/zkclient) 是 Datameer 开源的一个 Zookeeper 客户端实现。 + +缺省配置: + +```xml + +``` + +或: + +```sh +dubbo.registry.client=zkclient +``` + +或: + +```sh +zookeeper://10.20.153.10:2181?client=zkclient +``` + +需依赖或直接[下载](http://repo1.maven.org/maven2/com/github/sgroschupf/zkclient): + +```xml + + com.github.sgroschupf + zkclient + 0.11 + +``` + + +### 使用 curator 客户端 + +从 `2.3.0` 版本开始支持可选 curator 实现。[Curator](https://github.com/apache/curator) 是 Netflix 开源的一个 Zookeeper 客户端实现。 + +如果需要改为 curator 实现,请配置: + +```xml + +``` + +或: + +```sh +dubbo.registry.client=curator +``` + +或: + +```sh +zookeeper://10.20.153.10:2181?client=curator +``` + +需依赖或直接下载[curator-framework](https://repo1.maven.org/maven2/org/apache/curator/curator-framework/), [curator-recipes](https://repo1.maven.org/maven2/org/apache/curator/curator-recipes/): + +```xml + + 5.2.1 + + + + org.apache.curator + curator-framework + ${curator.version} + + + org.apache.curator + curator-recipes + ${curator.version} + + + org.apache.curator + curator-x-discovery + ${curator.version} + +``` + +Zookeeper 单机配置: + +```xml + +``` + +或: + +```xml + +``` + +Zookeeper 集群配置: + +```xml + + +``` + +或: + +```xml + +``` + +同一 Zookeeper,分成多组注册中心: + +```xml + + +``` + +## zookeeper 安装 + +安装方式参见: [Zookeeper安装手册](../../../../docsv2.7/admin/install/zookeeper),只需搭一个原生的 Zookeeper 服务器,并将 [Quick Start](../../../quick-start) 中 Provider 和 Consumer 里的 `conf/dubbo.properties` 中的 `dubbo.registry.address` 的值改为 `zookeeper://127.0.0.1:2181` 即可使用。 + + +## 可靠性声明 + +阿里内部并没有采用 Zookeeper 做为注册中心,而是使用自己实现的基于数据库的注册中心,即:Zookeeper 注册中心并没有在阿里内部长时间运行的可靠性保障,此 Zookeeper 桥接实现只为开源版本提供,其可靠性依赖于 Zookeeper 本身的可靠性。 + +## 兼容性声明 + +因 `2.0.8` 最初设计的 zookeeper 存储结构不能扩充不同类型的数据,`2.0.9` 版本做了调整,所以不兼容,需全部改用 `2.0.9` 版本才行,以后的版本会保持兼容 `2.0.9`。`2.2.0` 版本改为基于 zkclient 实现,需增加 zkclient 的依赖包,`2.3.0` 版本增加了基于 curator 的实现,作为可选实现策略。 + +[^1]: 建议使用 `2.3.3` 以上版本的 zookeeper 注册中心客户端 diff --git a/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/overview.md b/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/overview.md new file mode 100644 index 000000000000..c087fc3b6f43 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/registry/zookeeper/overview.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "概述" +linkTitle: "概述" +weight: 1 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/_index.md new file mode 100755 index 000000000000..4201d07472a0 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "SPI扩展使用手册" +linkTitle: "SPI扩展使用手册" +weight: 2 +--- + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/_index.md new file mode 100644 index 000000000000..285cc5f79cf9 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/_index.md @@ -0,0 +1,6 @@ +--- +type: docs +title: "Dubbo SPI扩展实现说明" +linkTitle: "Dubbo SPI扩展实现说明" +weight: 2 +--- diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/cache.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/cache.md new file mode 100644 index 000000000000..7e5f8ff9cdbe --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/cache.md @@ -0,0 +1,88 @@ +--- +type: docs +title: "缓存扩展" +linkTitle: "缓存扩展" +weight: 24 +--- + +## 扩展说明 + +用请求参数作为 key,缓存返回结果。 + +## 扩展接口 + +`org.apache.dubbo.cache.CacheFactory` + +## 扩展配置 + +```xml + + + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.cache.support.lru.LruCacheFactory` +* `org.apache.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory` +* `org.apache.dubbo.cache.support.jcache.JCacheFactory` + + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxCacheFactory.java (实现CacheFactory接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.cache.CacheFactory (纯文本文件,内容为:xxx=com.xxx.XxxCacheFactory) +``` + +XxxCacheFactory.java: + +```java +package com.xxx; + +import org.apache.dubbo.cache.CacheFactory; + +public class XxxCacheFactory implements CacheFactory { + public Cache getCache(URL url, String name) { + return new XxxCache(url, name); + } +} +``` + +XxxCache.java: + +```java +package com.xxx; + +import org.apache.dubbo.cache.Cache; + +public class XxxCache implements Cache { + public Cache(URL url, String name) { + // ... + } + public void put(Object key, Object value) { + // ... + } + public Object get(Object key) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.cache.CacheFactory: + +```properties +xxx=com.xxx.XxxCacheFactory +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/cluster.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/cluster.md new file mode 100644 index 000000000000..1d2cf8c04f67 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/cluster.md @@ -0,0 +1,83 @@ +--- +type: docs +title: "集群扩展" +linkTitle: "集群扩展" +weight: 5 +--- + +## 扩展说明 + +当有多个服务提供方时,将多个服务提供方组织成一个集群,并伪装成一个提供方。 + +## 扩展接口 + +`org.apache.dubbo.rpc.cluster.Cluster` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper` +* `org.apache.dubbo.rpc.cluster.support.FailoverCluster` +* `org.apache.dubbo.rpc.cluster.support.FailfastCluster` +* `org.apache.dubbo.rpc.cluster.support.FailsafeCluster` +* `org.apache.dubbo.rpc.cluster.support.FailbackCluster` +* `org.apache.dubbo.rpc.cluster.support.ForkingCluster` +* `org.apache.dubbo.rpc.cluster.support.AvailableCluster` +* `org.apache.dubbo.rpc.cluster.support.MergeableCluster` +* `org.apache.dubbo.rpc.cluster.support.BroadcastCluster` +* `org.apache.dubbo.rpc.cluster.support.registry.ZoneAwareCluster` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxCluster.java (实现Cluster接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.cluster.Cluster (纯文本文件,内容为:xxx=com.xxx.XxxCluster) +``` + +XxxCluster.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.cluster.Cluster; +import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; +import org.apache.dubbo.rpc.cluster.Directory; +import org.apache.dubbo.rpc.cluster.LoadBalance; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; + +public class XxxCluster implements Cluster { + public Invoker merge(Directory directory) throws RpcException { + return new AbstractClusterInvoker(directory) { + public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException { + // ... + } + }; + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.cluster.Cluster: + +```properties +xxx=com.xxx.XxxCluster +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/compiler.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/compiler.md new file mode 100644 index 000000000000..bdc9fda8ebab --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/compiler.md @@ -0,0 +1,60 @@ +--- +type: docs +title: "编译器扩展" +linkTitle: "编译器扩展" +weight: 13 +--- + +## 扩展说明 + +Java 代码编译器,用于动态生成字节码,加速调用。 + +## 扩展接口 + +`org.apache.dubbo.common.compiler.Compiler` + +## 扩展配置 + +自动加载 + +## 已知扩展 + +* `org.apache.dubbo.common.compiler.support.JdkCompiler` +* `org.apache.dubbo.common.compiler.support.JavassistCompiler` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxCompiler.java (实现Compiler接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.common.compiler.Compiler (纯文本文件,内容为:xxx=com.xxx.XxxCompiler) +``` + +XxxCompiler.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.compiler.Compiler; + +public class XxxCompiler implements Compiler { + public Object getExtension(Class type, String name) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.common.compiler.Compiler: + +```properties +xxx=com.xxx.XxxCompiler +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/config-center.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/config-center.md new file mode 100644 index 000000000000..90d192f6367b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/config-center.md @@ -0,0 +1,103 @@ +--- +type: docs +title: "配置中心扩展" +linkTitle: "配置中心扩展" +weight: 13 +--- + +## 设计目的 + +配置中心的核心功能是作为 Key-Value 存储,Dubbo 框架告知配置中心其关心的 key,配置中心返回该key对应的 value 值。 + +按照应用场景划分,配置中心在 Dubbo 框架中主要承担以下职责: + +- 作为外部化配置中心,即存储 dubbo.properties 配置文件,此时,key 值通常为文件名如 dubbo.properties,value 则为配置文件内容。 +- 存储单个配置项,如各种开关项、常量值等。 +- 存储服务治理规则,此时key通常按照 "服务名+规则类型" 的格式来组织,而 value 则为具体的治理规则。 + +为了进一步实现对 key-value 的分组管理,Dubbo 的配置中心还加入了 namespace、group 的概念,这些概念在很多专业的第三方配置中心中都有体现,通常情况下,namespace 用来隔离不同的租户,group 用来对同一租户的key集合做分组。 + +当前,Dubbo 配置中心实现了对 Zookeeper、Nacos、Etcd、Consul、Apollo 的对接,接下来我们具体看一下 Dubbo 抽象的配置中心是怎么映射到具体的第三方实现中的。 + +## 扩展接口 + +* `org.apache.dubbo.configcenter.DynamicConfigurationFactory` +* `org.apache.dubbo.configcenter.DynamicConfiguration` + +## 已知扩展 + +* `org.apache.dubbo.configcenter.support.zookeeper.ZookeeperDynamicConfigurationFactory` +* `org.apache.dubbo.configcenter.support.nacos.NacosDynamicConfigurationFactory` +* `org.apache.dubbo.configcenter.support.etcd.EtcdDynamicConfigurationFactory` +* `org.apache.dubbo.configcenter.consul.ConsulDynamicConfigurationFactory` +* `org.apache.dubbo.configcenter.support.apollo.ApolloDynamicConfigurationFactory` +* `org.apache.dubbo.common.config.configcenter.file.FileSystemDynamicConfigurationFactory` + +## 实现原理 + +### Zookeeper + +zookeeper提供了一个树状的存储模型,其实现原理如下: + +![image-20190127225608553](/imgs/dev/configcenter_zk_model.jpg) + +namespace, group, key 等分别对应不同层级的 ZNode 节点,而 value 则作为根 ZNode 节点的值存储。 + +1. 外部化配置中心 dubbo.properties + + ![image-20190127225608553](/imgs/dev/configcenter_zk_properties.jpg) + + 上图展示了两个不同作用域的 dubbo.properties 文件在 zookeeper 中的存储结构: + - 命名空间namespace都为:dubbo + - 分组 group:全局级别为 dubbo,所有应用共享;应用级别为应用名 demo-provider,只对该应用生效 + - key:dubbo.properties + +2. 单个配置项 + + ![image-20190127225608553](/imgs/dev/configcenter_zk_singleitem.jpg) + + 设置优雅停机事件为15000: + - 命名空间 namespace:dubbo + - 分组 group:dubbo + - key:dubbo.service.shutdown.wait + - value:15000 + +3. 服务治理规则 + + ![image-20190127225608553](/imgs/dev/configcenter_zk_rule.jpg) + + 上图展示了一条应用级别的条件路由规则: + + - 命名空间 namespace:dubbo + - 分组 group:dubbo + - key:governance-conditionrouter-consumer.condition-router,其中 governance-conditionrouter-consumer 为应用名,condition-router 代表条件路由 + + + > 注意: + > + > Dubbo同时支持应用、服务两种粒度的服务治理规则,对于这两种粒度,其key取值规则如下: + > * 应用粒度 {应用名 + 规则后缀}。如: `demo-application.configurators`、`demo-application.tag-router`等 + > * 服务粒度 {服务接口名:[服务版本]:[服务分组] + 规则后缀},其中服务版本、服务分组是可选的,如果它们有配置则在key中体现,没被配置则用":"占位。如 + > `org.apache.dubbo.demo.DemoService::.configurators`、`org.apache.dubbo.demo.DemoService:1.0.0:group1.configurators` + +### Etcd & Consul + +Etcd 和 Consul 本质上也是一种类似 zookeeper 的树状存储结构,实现请参考 zookeeper。 + +### Nacos + +Nacos 作为一个专业的第三方配置中心,拥有专门为配置中心设计的存储结构,包括内置的 namespace、group、dataid 等概念。并且这几个概念基本上与 Dubbo 框架抽象的配置中心是一一对应的。 + +与 Zookeeper 实现的对应关系如下: + +![image-20190127225608553](/imgs/dev/configcenter_nacos_model.jpg) + +参考上文关于 zookeeper 实现中描述的示例,这里的 dataid 可能为: +* 外部化配置中心:dubbo.properties +* 单个配置项:dubbo.service.shutdown.wait +* 服务治理规则:org.apache.dubbo.demo.DemoService:1.0.0:group1.configurators + +### Apollo + +Apollo 与 Nacos 类似,请参考动态配置中心使用文档中关于 Apollo 部分的描述。 + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/container.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/container.md new file mode 100644 index 000000000000..355227d81417 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/container.md @@ -0,0 +1,67 @@ +--- +type: docs +title: "容器扩展" +linkTitle: "容器扩展" +weight: 22 +--- + +## 扩展说明 + +服务容器扩展,用于自定义加载内容。 + +## 扩展接口 + +`org.apache.dubbo.container.Container` + +## 扩展配置 + +```sh +java org.apache.dubbo.container.Main spring jetty log4j +``` + +## 已知扩展 + +* `org.apache.dubbo.container.spring.SpringContainer` +* `org.apache.dubbo.container.spring.JettyContainer` +* `org.apache.dubbo.container.spring.Log4jContainer` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxContainer.java (实现Container接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.container.Container (纯文本文件,内容为:xxx=com.xxx.XxxContainer) +``` + +XxxContainer.java: + +```java +package com.xxx; + +org.apache.dubbo.container.Container; + + +public class XxxContainer implements Container { + public Status start() { + // ... + } + public Status stop() { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.container.Container: + +```properties +xxx=com.xxx.XxxContainer +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/dispatcher.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/dispatcher.md new file mode 100644 index 000000000000..9338fa963081 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/dispatcher.md @@ -0,0 +1,67 @@ +--- +type: docs +title: "消息派发扩展" +linkTitle: "消息派发扩展" +weight: 14 +--- + +## 扩展说明 + +通道信息派发器,用于指定线程池模型。 + +## 扩展接口 + +`org.apache.dubbo.remoting.Dispatcher` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.remoting.transport.dispatcher.all.AllDispatcher` +* `org.apache.dubbo.remoting.transport.dispatcher.direct.DirectDispatcher` +* `org.apache.dubbo.remoting.transport.dispatcher.message.MessageOnlyDispatcher` +* `org.apache.dubbo.remoting.transport.dispatcher.execution.ExecutionDispatcher` +* `org.apache.dubbo.remoting.transport.dispatcher.connection.ConnectionOrderedDispatcher` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxDispatcher.java (实现Dispatcher接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.remoting.Dispatcher (纯文本文件,内容为:xxx=com.xxx.XxxDispatcher) +``` + +XxxDispatcher.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.Dispatcher; + +public class XxxDispatcher implements Dispatcher { + public Group lookup(URL url) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.remoting.Dispatcher: + +```properties +xxx=com.xxx.XxxDispatcher +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/dubbo-spi.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/dubbo-spi.md new file mode 100644 index 000000000000..e374804898b1 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/dubbo-spi.md @@ -0,0 +1,693 @@ +--- +type: docs +title: "扩展点开发指南" +linkTitle: "扩展点开发指南" +weight: 0 +description: "本文介绍了 Dubbo SPI 的原理和实现细节" +--- + +## 1.简介 + +SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。 +Dubbo 中,SPI 主要有两种用法,一种是加载固定的扩展类,另一种是加载自适应扩展类。这两种方式会在下面详细的介绍。 +需要特别注意的是: 在 Dubbo 中,基于 SPI 扩展加载的类是单例的。 + +### 1.1 加载固定的扩展类 + +如果让你来设计加载固定扩展类,你会怎么做了? +一种常见思路是读取特定目录下的配置文件,然后解析出全类名,通过反射机制来实例化这个类,然后将这个类放在集合中存起来,如果有需要的时候,直接从集合中取。Dubbo 中的实现也是这么一个思路。 +不过在 Dubbo 中,实现的更加完善,它实现类了 IOC 和 AOP 的功能。IOC 就是说如果这个扩展类依赖其他属性,Dubbo 会自动的将这个属性进行注入。这个功能如何实现了?一个常见思路是获取这个扩展类的 setter + 方法,调用 setter 方法进行属性注入。AOP 指的是什么了?这个说的是 Dubbo 能够为扩展类注入其包装类。比如 DubboProtocol 是 Protocol 的扩展类,ProtocolListenerWrapper 是 DubboProtocol 的包装类。 + +### 1.2 加载自适应扩展类 + +先说明下自适应扩展类的使用场景。比如我们有需求,在调用某一个方法时,基于参数选择调用到不同的实现类。和工厂方法有些类似,基于不同的参数,构造出不同的实例对象。 +在 Dubbo 中实现的思路和这个差不多,不过 Dubbo 的实现更加灵活,它的实现和策略模式有些类似。每一种扩展类相当于一种策略,基于 URL 消息总线,将参数传递给 ExtensionLoader,通过 ExtensionLoader 基于参数加载对应的扩展类,实现运行时动态调用到目标实例上。 + +## 2. Dubbo SPI 源码分析 + +### 2.1 加载固定的扩展类 + +Dubbo 中,SPI 加载固定扩展类的入口是 ExtensionLoader 的 getExtension 方法,下面我们对拓展类对象的获取过程进行详细的分析。 + +```java +public T getExtension(String name) { + if (name == null || name.length() == 0) + throw new IllegalArgumentException("Extension name == null"); + if ("true".equals(name)) { + // 获取默认的拓展实现类 + return getDefaultExtension(); + } + // Holder,顾名思义,用于持有目标对象 + Holder holder = cachedInstances.get(name); + // 这段逻辑保证了只有一个线程能够创建 Holder 对象 + if (holder == null) { + cachedInstances.putIfAbsent(name, new Holder()); + holder = cachedInstances.get(name); + } + Object instance = holder.get(); + // 双重检查 + if (instance == null) { + synchronized (holder) { + instance = holder.get(); + if (instance == null) { + // 创建拓展实例 + instance = createExtension(name); + // 设置实例到 holder 中 + holder.set(instance); + } + } + } + return (T) instance; +} +``` + +上面代码的逻辑比较简单,首先检查缓存,缓存未命中则创建拓展对象。下面我们来看一下创建拓展对象的过程是怎样的。 + +```java +private T createExtension(String name, boolean wrap) { + // 从配置文件中加载所有的拓展类,可得到“配置项名称”到“配置类”的映射关系表 + Class clazz = getExtensionClasses().get(name); + // 如果没有该接口的扩展,或者该接口的实现类不允许重复但实际上重复了,直接抛出异常 + if (clazz == null || unacceptableExceptions.contains(name)) { + throw findException(name); + } + try { + T instance = (T) EXTENSION_INSTANCES.get(clazz); + // 这段代码保证了扩展类只会被构造一次,也就是单例的. + if (instance == null) { + EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().newInstance()); + instance = (T) EXTENSION_INSTANCES.get(clazz); + } + // 向实例中注入依赖 + injectExtension(instance); + + // 如果启用包装的话,则自动为进行包装. + // 比如我基于 Protocol 定义了 DubboProtocol 的扩展,但实际上在 Dubbo 中不是直接使用的 DubboProtocol, 而是其包装类 + // ProtocolListenerWrapper + if (wrap) { + + List> wrapperClassesList = new ArrayList<>(); + if (cachedWrapperClasses != null) { + wrapperClassesList.addAll(cachedWrapperClasses); + wrapperClassesList.sort(WrapperComparator.COMPARATOR); + Collections.reverse(wrapperClassesList); + } + + // 循环创建 Wrapper 实例 + if (CollectionUtils.isNotEmpty(wrapperClassesList)) { + for (Class wrapperClass : wrapperClassesList) { + Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class); + if (wrapper == null + || (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) { + // 将当前 instance 作为参数传给 Wrapper 的构造方法,并通过反射创建 Wrapper 实例。 + // 然后向 Wrapper 实例中注入依赖,最后将 Wrapper 实例再次赋值给 instance 变量 + instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); + } + } + } + } + // 初始化 + initExtension(instance); + return instance; + } catch (Throwable t) { + throw new IllegalStateException("Extension instance (name: " + name + ", class: " + + type + ") couldn't be instantiated: " + t.getMessage(), t); + } +} +``` + +createExtension 方法的逻辑稍复杂一下,包含了如下的步骤: + +1. 通过 getExtensionClasses 获取所有的拓展类 +2. 通过反射创建拓展对象 +3. 向拓展对象中注入依赖 +4. 将拓展对象包裹在相应的 Wrapper 对象中 +5. 初始化拓展对象 + +以上步骤中,第一个步骤是加载拓展类的关键,第三和第四个步骤是 Dubbo IOC 与 AOP 的具体实现。在接下来的章节中,将会重点分析 getExtensionClasses 方法的逻辑,以及简单介绍 Dubbo IOC 的具体实现。 + +### 2.1.1 获取所有的拓展类 + +我们在通过名称获取拓展类之前,首先需要根据配置文件解析出拓展项名称到拓展类的映射关系表(Map\<名称, 拓展类\>),之后再根据拓展项名称从映射关系表中取出相应的拓展类即可。相关过程的代码分析如下: + +```java +private Map> getExtensionClasses() { + // 从缓存中获取已加载的拓展类 + Map> classes = cachedClasses.get(); + // 双重检查 + if (classes == null) { + synchronized (cachedClasses) { + classes = cachedClasses.get(); + if (classes == null) { + // 加载拓展类 + classes = loadExtensionClasses(); + cachedClasses.set(classes); + } + } + } + return classes; +} +``` + +这里也是先检查缓存,若缓存未命中,则通过 synchronized 加锁。加锁后再次检查缓存,并判空。此时如果 classes 仍为 null,则通过 loadExtensionClasses 加载拓展类。下面分析 loadExtensionClasses 方法的逻辑。 + +```java +private Map> loadExtensionClasses() { + // 缓存默认的 SPI 扩展名 + cacheDefaultExtensionName(); + + Map> extensionClasses = new HashMap<>(); + + // 基于策略来加载指定文件夹下的文件 + // 目前有四种策略,分别读取 META-INF/services/ META-INF/dubbo/ META-INF/dubbo/internal/ META-INF/dubbo/external/ 这四个目录下的配置文件 + for (LoadingStrategy strategy : strategies) { + loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); + loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages()); + } + + return extensionClasses; +} +``` + +loadExtensionClasses 方法总共做了两件事情,一是对 SPI 注解进行解析,二是调用 loadDirectory 方法加载指定文件夹配置文件。SPI 注解解析过程比较简单,无需多说。下面我们来看一下 loadDirectory 做了哪些事情。 + +```java +private void loadDirectory(Map> extensionClasses, String dir, String type, + boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) { + // fileName = 文件夹路径 + type 全限定名 + String fileName = dir + type; + try { + Enumeration urls = null; + ClassLoader classLoader = findClassLoader(); + + // try to load from ExtensionLoader's ClassLoader first + if (extensionLoaderClassLoaderFirst) { + ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader(); + if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) { + urls = extensionLoaderClassLoader.getResources(fileName); + } + } + // 根据文件名加载所有的同名文件 + if (urls == null || !urls.hasMoreElements()) { + if (classLoader != null) { + urls = classLoader.getResources(fileName); + } else { + urls = ClassLoader.getSystemResources(fileName); + } + } + + if (urls != null) { + while (urls.hasMoreElements()) { + java.net.URL resourceURL = urls.nextElement(); + // 加载资源 + loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages); + } + } + } catch (Throwable t) { + logger.error("Exception occurred when loading extension class (interface: " + + type + ", description file: " + fileName + ").", t); + } +} +``` + +loadDirectory 方法先通过 classLoader 获取所有资源链接,然后再通过 loadResource 方法加载资源。我们继续跟下去,看一下 loadResource 方法的实现。 + +```java +private void loadResource(Map> extensionClasses, ClassLoader classLoader, + java.net.URL resourceURL, boolean overridden, String... excludedPackages) { + try { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { + String line; + String clazz = null; + // 按行读取配置内容 + while ((line = reader.readLine()) != null) { + // 定位 # 字符 + final int ci = line.indexOf('#'); + if (ci >= 0) { + // 截取 # 之前的字符串,# 之后的内容为注释,需要忽略 + line = line.substring(0, ci); + } + line = line.trim(); + if (line.length() > 0) { + try { + String name = null; + // 以等于号 = 为界,截取键与值 + int i = line.indexOf('='); + if (i > 0) { + name = line.substring(0, i).trim(); + clazz = line.substring(i + 1).trim(); + } else { + clazz = line; + } + // 加载类,并通过 loadClass 方法对类进行缓存 + if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) { + loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden); + } + } catch (Throwable t) { + IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); + exceptions.put(line, e); + } + } + } + } + } catch (Throwable t) { + logger.error("Exception occurred when loading extension class (interface: " + + type + ", class file: " + resourceURL + ") in " + resourceURL, t); + } +} +``` + +loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作。loadClass 方法用于主要用于操作缓存,该方法的逻辑如下: + +```java +private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name, + boolean overridden) throws NoSuchMethodException { + if (!type.isAssignableFrom(clazz)) { + throw new IllegalStateException("Error occurred when loading extension class (interface: " + + type + ", class line: " + clazz.getName() + "), class " + + clazz.getName() + " is not subtype of interface."); + } + // 检测目标类上是否有 Adaptive 注解 + if (clazz.isAnnotationPresent(Adaptive.class)) { + cacheAdaptiveClass(clazz, overridden); + } else if (isWrapperClass(clazz)) { + // 缓存包装类 + cacheWrapperClass(clazz); + } else { + // 进入到这里,表明只是该类只是一个普通的拓展类 + // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常 + clazz.getConstructor(); + if (StringUtils.isEmpty(name)) { + // 如果 name 为空,则尝试从 Extension 注解中获取 name,或使用小写的类名作为 name + name = findAnnotationName(clazz); + if (name.length() == 0) { + throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); + } + } + + String[] names = NAME_SEPARATOR.split(name); + if (ArrayUtils.isNotEmpty(names)) { + // 如果类上有 Activate 注解,则使用 names 数组的第一个元素作为键, + // 存储 name 到 Activate 注解对象的映射关系 + cacheActivateClass(clazz, names[0]); + for (String n : names) { + // 存储 Class 到名称的映射关系 + cacheName(clazz, n); + // 存储 name 到 Class 的映射关系. + // 如果存在同一个扩展名对应多个实现类,基于 override 参数是否允许覆盖,如果不允许,则抛出异常. + saveInExtensionClass(extensionClasses, clazz, n, overridden); + } + } + } +} +``` + +如上,loadClass 方法操作了不同的缓存,比如 cachedAdaptiveClass、cachedWrapperClasses 和 cachedNames 等等。除此之外,该方法没有其他什么逻辑了。 + +到此,关于缓存类加载的过程就分析完了。整个过程没什么特别复杂的地方,大家按部就班的分析即可,不懂的地方可以调试一下。接下来,我们来聊聊 Dubbo IOC 方面的内容。 + +### 2.1.2 Dubbo IOC + +Dubbo IOC 是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。整个过程对应的代码如下: + +```java +private T injectExtension(T instance) { + + if (objectFactory == null) { + return instance; + } + + try { + // 遍历目标类的所有方法 + for (Method method : instance.getClass().getMethods()) { + // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public + if (!isSetter(method)) { + continue; + } + /** + * 检测是否有 DisableInject 注解修饰. + */ + if (method.getAnnotation(DisableInject.class) != null) { + continue; + } + // 基本类型不注入 + Class pt = method.getParameterTypes()[0]; + if (ReflectUtils.isPrimitives(pt)) { + continue; + } + + try { + // 获取属性名,比如 setName 方法对应属性名 name + String property = getSetterProperty(method); + // 从 ObjectFactory 中获取依赖对象 + Object object = objectFactory.getExtension(pt, property); + if (object != null) { + // 注入 + method.invoke(instance, object); + } + } catch (Exception e) { + logger.error("Failed to inject via method " + method.getName() + + " of interface " + type.getName() + ": " + e.getMessage(), e); + } + + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + return instance; +} +``` + +在上面代码中,objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。这两个类的类的代码不是很复杂,这里就不一一分析了。 + +Dubbo IOC 目前仅支持 setter 方式注入,总的来说,逻辑比较简单易懂。 + +### 2.2 加载自适应扩展类 + +自适应扩展类的含义是说,基于参数,在运行时动态选择到具体的目标类,然后执行。 +在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等。有时,有些拓展并不想在框架启动阶段被加载,而是希望在拓展方法被调用时,根据运行时参数进行加载。这听起来有些矛盾。拓展未被加载,那么拓展方法就无法被调用(静态方法除外)。拓展方法未被调用,拓展就无法被加载。对于这个矛盾的问题,Dubbo 通过自适应拓展机制很好的解决了。自适应拓展机制的实现逻辑比较复杂,首先 Dubbo 会为拓展接口生成具有代理功能的代码。然后通过 javassist 或 jdk 编译这段代码,得到 Class 类。最后再通过反射创建代理类,整个过程比较复杂。 + +加载自适应扩展类的入口是 ExtensionLoader 的 getAdaptiveExtension 方法。 + +```java +public T getAdaptiveExtension() { + // 从缓存中获取自适应拓展 + Object instance = cachedAdaptiveInstance.get(); + if (instance == null) { + // 如果存在异常,则直接抛出 + if (createAdaptiveInstanceError != null) { + throw new IllegalStateException("Failed to create adaptive instance: " + + createAdaptiveInstanceError.toString(), + createAdaptiveInstanceError); + } + + synchronized (cachedAdaptiveInstance) { + instance = cachedAdaptiveInstance.get(); + // double check + if (instance == null) { + try { + // 创建自适应拓展 + // 这里分为两种情况:一种是存在 Adaptive 类,另一个是需要生成 Adaptive 类 + instance = createAdaptiveExtension(); + cachedAdaptiveInstance.set(instance); + } catch (Throwable t) { + createAdaptiveInstanceError = t; + throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); + } + } + } + } + + return (T) instance; +} +``` + +getAdaptiveExtension 方法首先会检查缓存,缓存未命中,则调用 createAdaptiveExtension 方法创建自适应拓展。下面,我们看一下 createAdaptiveExtension 方法的代码。 + +```java +private T createAdaptiveExtension() { + try { + // 获取自适应拓展类,并通过反射实例化 + return injectExtension((T) getAdaptiveExtensionClass().newInstance()); + } catch (Exception e) { + throw new IllegalStateException("Can not create adaptive extension ..."); + } +} +``` +createAdaptiveExtension 方法的代码比较少,但却包含了三个逻辑,分别如下: + +1. 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象 +2. 通过反射进行实例化 +3. 调用 injectExtension 方法向拓展实例中注入依赖 + +前两个逻辑比较好理解,第三个逻辑用于向自适应拓展对象中注入依赖。这个逻辑看似多余,但有存在的必要,这里简单说明一下。前面说过,Dubbo 中有两种类型的自适应拓展,一种是手工编码的,一种是自动生成的。手工编码的自适应拓展中可能存在着一些依赖,而自动生成的 Adaptive 拓展则不会依赖其他类。这里调用 injectExtension 方法的目的是为手工编码的自适应拓展注入依赖,这一点需要大家注意一下。关于 injectExtension 方法,前文已经分析过了,这里不再赘述。接下来,分析 getAdaptiveExtensionClass 方法的逻辑。 + +```java +private Class getAdaptiveExtensionClass() { + // 通过 SPI 获取所有的拓展类 + getExtensionClasses(); + // 检查缓存,若缓存不为空,则直接返回缓存 + if (cachedAdaptiveClass != null) { + return cachedAdaptiveClass; + } + // 创建自适应拓展类 + return cachedAdaptiveClass = createAdaptiveExtensionClass(); +} +``` + +getAdaptiveExtensionClass 方法同样包含了三个逻辑,如下: + +1. 调用 getExtensionClasses 获取所有的拓展类 +2. 检查缓存,若缓存不为空,则返回缓存 +3. 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类 + +这三个逻辑看起来平淡无奇,似乎没有多讲的必要。但是这些平淡无奇的代码中隐藏了着一些细节,需要说明一下。首先从第一个逻辑说起,getExtensionClasses 这个方法用于获取某个接口的所有实现类。比如该方法可以获取 Protocol 接口的 DubboProtocol、HttpProtocol、InjvmProtocol 等实现类。在获取实现类的过程中,如果某个实现类被 Adaptive 注解修饰了,那么该类就会被赋值给 cachedAdaptiveClass 变量。此时,上面步骤中的第二步条件成立(缓存不为空),直接返回 cachedAdaptiveClass 即可。如果所有的实现类均未被 Adaptive 注解修饰,那么执行第三步逻辑,创建自适应拓展类。相关代码如下: + +```java +private Class createAdaptiveExtensionClass() { + // 构建自适应拓展代码 + String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); + ClassLoader classLoader = findClassLoader(); + // 获取编译器实现类 + org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); + // 编译代码,生成 Class + return compiler.compile(code, classLoader); +} +``` + +createAdaptiveExtensionClass 方法用于生成自适应拓展类,该方法首先会生成自适应拓展类的源码,然后通过 Compiler 实例(Dubbo 默认使用 javassist 作为编译器)编译源码,得到代理类 Class 实例。接下来,我们把重点放在代理类代码生成的逻辑上,其他逻辑大家自行分析。 + +### 2.2.1 自适应拓展类代码生成 + +AdaptiveClassCodeGenerator#generate 方法生成扩展类代码 +```java +public String generate() { + // 如果该接口中没有方法被 @Adaptive 注解修饰,直接抛出异常 + if (!hasAdaptiveMethod()) { + throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!"); + } + + StringBuilder code = new StringBuilder(); + // 生成包名、import、方法等. + code.append(generatePackageInfo()); + code.append(generateImports()); + code.append(generateClassDeclaration()); + + Method[] methods = type.getMethods(); + for (Method method : methods) { + code.append(generateMethod(method)); + } + code.append("}"); + + if (logger.isDebugEnabled()) { + logger.debug(code.toString()); + } + return code.toString(); +} + +``` + +#### 2.2.2 生成方法 + +上面代码中,生成方法的逻辑是最关键的,我们详细分析下。 +```java +private String generateMethod(Method method) { + String methodReturnType = method.getReturnType().getCanonicalName(); + String methodName = method.getName(); + // 生成方法内容 + String methodContent = generateMethodContent(method); + String methodArgs = generateMethodArguments(method); + String methodThrows = generateMethodThrows(method); + return String.format(CODE_METHOD_DECLARATION, methodReturnType, methodName, methodArgs, methodThrows, methodContent); +} +``` + +generateMethodContent 分析 + +```java +private String generateMethodContent(Method method) { + // 该方法上必须有 @Adaptive 注解修饰 + Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class); + StringBuilder code = new StringBuilder(512); + if (adaptiveAnnotation == null) { + // 没有 @Adaptive 注解修饰,生成异常信息 + return generateUnsupported(method); + } else { + // 获取 URL 在参数列表上的索引 + int urlTypeIndex = getUrlTypeIndex(method); + + if (urlTypeIndex != -1) { + // 如果参数列表上存在 URL,生成对 URL 进行空检查 + code.append(generateUrlNullCheck(urlTypeIndex)); + } else { + // 如果参数列表不存在 URL 类型的参数,那么就看参数列表上参数对象中是否包含 getUrl 方法 + // 有的话,生成 URL 空检查 + code.append(generateUrlAssignmentIndirectly(method)); + } + // 解析 Adaptive 注解上的 value 属性 + String[] value = getMethodAdaptiveValue(adaptiveAnnotation); + // 如果参数列表上有 Invocation 类型的参数,生成空检查并获取 methodName. + boolean hasInvocation = hasInvocationArgument(method); + + code.append(generateInvocationArgumentNullCheck(method)); + // 这段逻辑主要就是为了生成 extName(也就是扩展名) + // 分为多种情况: + // 1.defaultExtName 是否存在 + // 2.参数中是否存在 invocation 类型参数 + // 3.是否是为 protocol 生成代理 + // 为什么要对 protocol 单独考虑了?因为 URL 中有获取 protocol 值的方法 + code.append(generateExtNameAssignment(value, hasInvocation)); + // check extName == null? + code.append(generateExtNameNullCheck(value)); + + // 生成获取扩展(使用 ExtensionLoader.getExtension 方法) + code.append(generateExtensionAssignment()); + + // 生成返回语句 + code.append(generateReturnAndInvocation(method)); + } + + return code.toString(); +} +``` + +上面那段逻辑主要做了如下几件事: +1.检查方法上是否 Adaptive 注解修饰 +2.为方法生成代码的时候,参数列表上要有 URL(或参数对象中有 URL) +3.使用 ExtensionLoader.getExtension 获取扩展 +4.执行对应的方法 + +### 2.2.3 附一个动态生成代码后的例子 + +```java +package org.apache.dubbo.common.extension.adaptive; + +import org.apache.dubbo.common.extension.ExtensionLoader; + + +public class HasAdaptiveExt$Adaptive implements org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt { + public java.lang.String echo(org.apache.dubbo.common.URL arg0, + java.lang.String arg1) { + // URL 空校验 + if (arg0 == null) { + throw new IllegalArgumentException("url == null"); + } + + org.apache.dubbo.common.URL url = arg0; + // 获取扩展名 + String extName = url.getParameter("has.adaptive.ext", "adaptive"); + // 扩展名空校验 + if (extName == null) { + throw new IllegalStateException( + "Failed to get extension (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt) name from url (" + + url.toString() + ") use keys([has.adaptive.ext])"); + } + // 获取扩展 + org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt extension = (org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt) ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.extension.adaptive.HasAdaptiveExt.class) + .getExtension(extName); + // 执行对应的方法 + return extension.echo(arg0, arg1); + } +} + +``` + + +## 3.SPI 扩展示例 + +### 3.1 加载固定扩展类 + +### 3.1.1 编写 SPI 接口及实现类 + +不管是 Java SPI,还是 Dubbo 中实现的 SPI,都需要编写接口。不过 Dubbo 中的接口需要被 @SPI 注解修饰。 + +```java +@SPI +public interface DemoSpi { + void say(); +} + +public class DemoSpiImpl implements DemoSpi { + public void say() { + } +} +``` + +#### 3.1.2 将实现类放在特定目录下 + +从上面的代码可知,dubbo 在加载扩展类的时候,会从四个目录中读取。我们在 META-INF/dubbo 目录下新建一个以 DemoSpi 接口名为文件名的文件,内容如下: + + +```text +demoSpiImpl = com.xxx.xxx.DemoSpiImpl(为 DemoSpi 接口实现类的全类名) +``` + +#### 3.1.3 使用 + +```java +public class DubboSPITest { + + @Test + public void sayHello() throws Exception { + ExtensionLoader extensionLoader = + ExtensionLoader.getExtensionLoader(DemoSpi.class); + DemoSpi dmeoSpi = extensionLoader.getExtension("demoSpiImpl"); + optimusPrime.sayHello(); + } +} +``` + +### 3.2 加载自适应扩展类 + +这个以 Protocol 为例进行说明 + +### 3.2.1 Protocol 接口(抽取部分核心方法) + +```java +@SPI("dubbo") +public interface Protocol { + @Adaptive + Exporter export(Invoker invoker) throws RpcException; + + @Adaptive + Invoker refer(Class type, URL url) throws RpcException; +} + +public class DubboProtocol extends AbstractProtocol { + ...... + @Override + public Invoker refer(Class type, URL url) throws RpcException { + return protocolBindingRefer(type, url); + } + + @Override + public Exporter export(Invoker invoker) throws RpcException { + ...... + return exporter; + } +} +``` + +### 3.2.2 将实现类放在特定目录下 +在 dubbo 中,该配置路径 META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol +```text +dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol +``` + +需要说明一点的是,在 dubbo 中,并不是直接使用 DubboProtocol 的,而是使用的是其包装类。 + +### 3.2.3 使用 + +```java +public class DubboAdaptiveTest { + + @Test + public void sayHello() throws Exception { + URL url = URL.valueOf("dubbo://localhost/test"); + Protocol adaptiveProtocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); + adaptiveProtocol.refer(type, url); + } +} +``` + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/exchanger.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/exchanger.md new file mode 100644 index 000000000000..967bf84c3521 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/exchanger.md @@ -0,0 +1,96 @@ +--- +type: docs +title: "信息交换扩展" +linkTitle: "信息交换扩展" +weight: 18 +--- + +## 扩展说明 + +基于传输层之上,实现 Request-Response 信息交换语义。 + +## 扩展接口 + +* `org.apache.dubbo.remoting.exchange.Exchanger` +* `org.apache.dubbo.remoting.exchange.ExchangeServer` +* `org.apache.dubbo.remoting.exchange.ExchangeClient` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +`org.apache.dubbo.remoting.exchange.exchanger.HeaderExchanger` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxExchanger.java (实现Exchanger接口) + |-XxxExchangeServer.java (实现ExchangeServer接口) + |-XxxExchangeClient.java (实现ExchangeClient接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.remoting.exchange.Exchanger (纯文本文件,内容为:xxx=com.xxx.XxxExchanger) +``` + +XxxExchanger.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.exchange.Exchanger; + + +public class XxxExchanger implements Exchanger { + public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { + return new XxxExchangeServer(url, handler); + } + public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { + return new XxxExchangeClient(url, handler); + } +} +``` + +XxxExchangeServer.java: + +```java + +package com.xxx; + +import org.apache.dubbo.remoting.exchange.ExchangeServer; + +public class XxxExchangeServer impelements ExchangeServer { + // ... +} +``` + +XxxExchangeClient.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.exchange.ExchangeClient; + +public class XxxExchangeClient impelments ExchangeClient { + // ... +} +``` + +META-INF/dubbo/org.apache.dubbo.remoting.exchange.Exchanger: + +```properties +xxx=com.xxx.XxxExchanger +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/exporter-listener.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/exporter-listener.md new file mode 100644 index 000000000000..0ea4c9057c75 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/exporter-listener.md @@ -0,0 +1,71 @@ +--- +type: docs +title: "暴露监听扩展" +linkTitle: "暴露监听扩展" +weight: 4 +--- + +## 扩展说明 + +当有服务暴露时,触发该事件。 + +## 扩展接口 + +`org.apache.dubbo.rpc.ExporterListener` + +## 扩展配置 + +```xml + + + + +``` + +## 已知扩展 + +`org.apache.dubbo.registry.directory.RegistryExporterListener` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxExporterListener.java (实现ExporterListener接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.ExporterListener (纯文本文件,内容为:xxx=com.xxx.XxxExporterListener) +``` + +XxxExporterListener.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.ExporterListener; +import org.apache.dubbo.rpc.Exporter; +import org.apache.dubbo.rpc.RpcException; + + +public class XxxExporterListener implements ExporterListener { + public void exported(Exporter exporter) throws RpcException { + // ... + } + public void unexported(Exporter exporter) throws RpcException { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.ExporterListener: + +```properties +xxx=com.xxx.XxxExporterListener +``` + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/extension-factory.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/extension-factory.md new file mode 100644 index 000000000000..4901b5546f3f --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/extension-factory.md @@ -0,0 +1,62 @@ +--- +type: docs +title: "扩展点加载扩展" +linkTitle: "扩展点加载扩展" +weight: 11 +--- + +## 扩展说明 + +扩展点本身的加载容器,可从不同容器加载扩展点。 + +## 扩展接口 + +`org.apache.dubbo.common.extension.ExtensionFactory` + +## 扩展配置 + +```xml + +``` + +## 已知扩展 + +* `org.apache.dubbo.common.extension.factory.SpiExtensionFactory` +* `org.apache.dubbo.config.spring.extension.SpringExtensionFactory` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxExtensionFactory.java (实现ExtensionFactory接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.common.extension.ExtensionFactory (纯文本文件,内容为:xxx=com.xxx.XxxExtensionFactory) +``` + +XxxExtensionFactory.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.extension.ExtensionFactory; + +public class XxxExtensionFactory implements ExtensionFactory { + public Object getExtension(Class type, String name) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.common.extension.ExtensionFactory: + +```properties +xxx=com.xxx.XxxExtensionFactory +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/filter.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/filter.md new file mode 100644 index 000000000000..92b916813c08 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/filter.md @@ -0,0 +1,94 @@ +--- +type: docs +title: "调用拦截扩展" +linkTitle: "调用拦截扩展" +weight: 2 +--- + +## 扩展说明 + +服务提供方和服务消费方调用过程拦截,Dubbo 本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。 + +约定: + +* 用户自定义 filter 默认在内置 filter 之后。 +* 特殊值 `default`,表示缺省扩展点插入的位置。比如:`filter="xxx,default,yyy"`,表示 `xxx` 在缺省 filter 之前,`yyy` 在缺省 filter 之后。 +* 特殊符号 `-`,表示剔除。比如:`filter="-foo1"`,剔除添加缺省扩展点 `foo1`。比如:`filter="-default"`,剔除添加所有缺省扩展点。 +* provider 和 service 同时配置的 filter 时,累加所有 filter,而不是覆盖。比如:`` 和 ``,则 `xxx`,`yyy`,`aaa`,`bbb` 均会生效。如果要覆盖,需配置:`` + +## 扩展接口 + +`org.apache.dubbo.rpc.Filter` + +## 扩展配置 + +```xml + + + + + + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.rpc.filter.EchoFilter` +* `org.apache.dubbo.rpc.filter.GenericFilter` +* `org.apache.dubbo.rpc.filter.GenericImplFilter` +* `org.apache.dubbo.rpc.filter.TokenFilter` +* `org.apache.dubbo.rpc.filter.AccessLogFilter` +* `org.apache.dubbo.rpc.filter.CountFilter` +* `org.apache.dubbo.rpc.filter.ActiveLimitFilter` +* `org.apache.dubbo.rpc.filter.ClassLoaderFilter` +* `org.apache.dubbo.rpc.filter.ContextFilter` +* `org.apache.dubbo.rpc.filter.ConsumerContextFilter` +* `org.apache.dubbo.rpc.filter.ExceptionFilter` +* `org.apache.dubbo.rpc.filter.ExecuteLimitFilter` +* `org.apache.dubbo.rpc.filter.DeprecatedFilter` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxFilter.java (实现Filter接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter) +``` + +XxxFilter.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.Filter; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.Result; +import org.apache.dubbo.rpc.RpcException; + +public class XxxFilter implements Filter { + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + // before filter ... + Result result = invoker.invoke(invocation); + // after filter ... + return result; + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.Filter: + +```properties +xxx=com.xxx.XxxFilter +``` \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/invoker-listener.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/invoker-listener.md new file mode 100644 index 000000000000..a6e130058a7e --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/invoker-listener.md @@ -0,0 +1,69 @@ +--- +type: docs +title: "引用监听扩展" +linkTitle: "引用监听扩展" +weight: 3 +--- + +## 扩展说明 + +当有服务引用时,触发该事件。 + +## 扩展接口 + +`org.apache.dubbo.rpc.InvokerListener` + +## 扩展配置 + +```xml + + + + +``` + +## 已知扩展 + +`org.apache.dubbo.rpc.listener.DeprecatedInvokerListener` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxInvokerListener.java (实现InvokerListener接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.InvokerListener (纯文本文件,内容为:xxx=com.xxx.XxxInvokerListener) +``` + +XxxInvokerListener.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.InvokerListener; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcException; + +public class XxxInvokerListener implements InvokerListener { + public void referred(Invoker invoker) throws RpcException { + // ... + } + public void destroyed(Invoker invoker) throws RpcException { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.InvokerListener: + +```properties +xxx=com.xxx.XxxInvokerListener +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/load-balance.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/load-balance.md new file mode 100644 index 000000000000..fa934238547d --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/load-balance.md @@ -0,0 +1,70 @@ +--- +type: docs +title: "负载均衡扩展" +linkTitle: "负载均衡扩展" +weight: 7 +--- + +## 扩展说明 + +从多个服务提供方中选择一个进行调用 + +## 扩展接口 + +`org.apache.dubbo.rpc.cluster.LoadBalance` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance` +* `org.apache.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance` +* `org.apache.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance` +* `org.apache.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance` +* `org.apache.dubbo.rpc.cluster.loadbalance.ShortestResponseLoadBalance` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxLoadBalance.java (实现LoadBalance接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.cluster.LoadBalance (纯文本文件,内容为:xxx=com.xxx.XxxLoadBalance) +``` + +XxxLoadBalance.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.cluster.LoadBalance; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcException; + +public class XxxLoadBalance implements LoadBalance { + public Invoker select(List> invokers, Invocation invocation) throws RpcException { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.cluster.LoadBalance: + +```properties +xxx=com.xxx.XxxLoadBalance +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/logger-adapter.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/logger-adapter.md new file mode 100644 index 000000000000..55fac975a12c --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/logger-adapter.md @@ -0,0 +1,89 @@ +--- +type: docs +title: "日志适配扩展" +linkTitle: "日志适配扩展" +weight: 26 +--- + +## 扩展说明 + +日志输出适配扩展点。 + +## 扩展接口 + +`org.apache.dubbo.common.logger.LoggerAdapter` + +## 扩展配置 + +```xml + +``` + +或者: + +```sh +-Ddubbo:application.logger=xxx +``` + +## 已知扩展 + +* `org.apache.dubbo.common.logger.slf4j.Slf4jLoggerAdapter` +* `org.apache.dubbo.common.logger.jcl.JclLoggerAdapter` +* `org.apache.dubbo.common.logger.log4j.Log4jLoggerAdapter` +* `org.apache.dubbo.common.logger.log4j2.Log4j2LoggerAdapter` +* `org.apache.dubbo.common.logger.jdk.JdkLoggerAdapter` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxLoggerAdapter.java (实现LoggerAdapter接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.common.logger.LoggerAdapter (纯文本文件,内容为:xxx=com.xxx.XxxLoggerAdapter) +``` + +XxxLoggerAdapter.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.logger.LoggerAdapter; + +public class XxxLoggerAdapter implements LoggerAdapter { + public Logger getLogger(URL url) { + // ... + } +} +``` + +XxxLogger.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.logger.Logger; + +public class XxxLogger implements Logger { + public XxxLogger(URL url) { + // ... + } + public void info(String msg) { + // ... + } + // ... +} +``` + +META-INF/dubbo/org.apache.dubbo.common.logger.LoggerAdapter: + +```properties +xxx=com.xxx.XxxLoggerAdapter +``` \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/merger.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/merger.md new file mode 100644 index 000000000000..549b39be5a8f --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/merger.md @@ -0,0 +1,73 @@ +--- +type: docs +title: "合并结果扩展" +linkTitle: "合并结果扩展" +weight: 8 +--- + +## 扩展说明 + +合并返回结果,用于分组聚合。 + +## 扩展接口 + +`org.apache.dubbo.rpc.cluster.Merger` + +## 扩展配置 + +```xml + +``` + +## 已知扩展 + +* `org.apache.dubbo.rpc.cluster.merger.ArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.ListMerger` +* `org.apache.dubbo.rpc.cluster.merger.SetMerger` +* `org.apache.dubbo.rpc.cluster.merger.MapMerger` +* `org.apache.dubbo.rpc.cluster.merger.ByteArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.CharArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.ShortArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.IntArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.LongArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.FloatArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.DoubleArrayMerger` +* `org.apache.dubbo.rpc.cluster.merger.BooleanArrayMerger` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxMerger.java (实现Merger接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.cluster.Merger (纯文本文件,内容为:xxx=com.xxx.XxxMerger) +``` + +XxxMerger.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.cluster.Merger; + +public class XxxMerger implements Merger { + public T merge(T... results) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.cluster.Merger: + +```properties +xxx=com.xxx.XxxMerger +``` + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/monitor.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/monitor.md new file mode 100644 index 000000000000..8e7c07cf9ffb --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/monitor.md @@ -0,0 +1,80 @@ +--- +type: docs +title: "监控中心扩展" +linkTitle: "监控中心扩展" +weight: 10 +--- + +## 扩展说明 + +负责服务调用次和调用时间的监控。 + +## 扩展接口 + +* `org.apache.dubbo.monitor.MonitorFactory` +* `org.apache.dubbo.monitor.Monitor` + +## 扩展配置 + +```xml + + +``` + +## 已知扩展 + +org.apache.dubbo.monitor.support.dubbo.DubboMonitorFactory + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxMonitorFactoryjava (实现MonitorFactory接口) + |-XxxMonitor.java (实现Monitor接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.monitor.MonitorFactory (纯文本文件,内容为:xxx=com.xxx.XxxMonitorFactory) +``` + +XxxMonitorFactory.java: + +```java +package com.xxx; + +import org.apache.dubbo.monitor.MonitorFactory; +import org.apache.dubbo.monitor.Monitor; +import org.apache.dubbo.common.URL; + +public class XxxMonitorFactory implements MonitorFactory { + public Monitor getMonitor(URL url) { + return new XxxMonitor(url); + } +} +``` + +XxxMonitor.java: + +```java +package com.xxx; + +import org.apache.dubbo.monitor.Monitor; + +public class XxxMonitor implements Monitor { + public void count(URL statistics) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.monitor.MonitorFactory: + +```properties +xxx=com.xxx.XxxMonitorFactory +``` \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/networker.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/networker.md new file mode 100644 index 000000000000..74a81332956b --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/networker.md @@ -0,0 +1,64 @@ +--- +type: docs +title: "组网扩展" +linkTitle: "组网扩展" +weight: 19 +--- + +## 扩展说明 + +对等网络节点组网器。 + +## 扩展接口 + +`org.apache.dubbo.remoting.p2p.Networker` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.remoting.p2p.support.MulticastNetworker` +* `org.apache.dubbo.remoting.p2p.support.FileNetworker` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxNetworker.java (实现Networker接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.remoting.p2p.Networker (纯文本文件,内容为:xxx=com.xxx.XxxNetworker) +``` + +XxxNetworker.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.p2p.Networker; + +public class XxxNetworker implements Networker { + public Group lookup(URL url) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.remoting.p2p.Networker: + +```properties +xxx=com.xxx.XxxNetworker +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/page.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/page.md new file mode 100644 index 000000000000..f12c38d45d93 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/page.md @@ -0,0 +1,61 @@ +# 页面扩展 + +## 扩展说明 + +对等网络节点组网器。 + +## 扩展接口 + +`org.apache.dubbo.container.page.PageHandler` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.container.page.pages.HomePageHandler` +* `org.apache.dubbo.container.page.pages.StatusPageHandler` +* `org.apache.dubbo.container.page.pages.LogPageHandler` +* `org.apache.dubbo.container.page.pages.SystemPageHandler` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxPageHandler.java (实现PageHandler接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.container.page.PageHandler (纯文本文件,内容为:xxx=com.xxx.XxxPageHandler) +``` + +XxxPageHandler.java: + +```java +package com.xxx; + +import org.apache.dubbo.container.page.PageHandler; + +public class XxxPageHandler implements PageHandler { + public Group lookup(URL url) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.container.page.PageHandler: + +```properties +xxx=com.xxx.XxxPageHandler +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/protocol.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/protocol.md new file mode 100644 index 000000000000..6eee12537641 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/protocol.md @@ -0,0 +1,158 @@ +--- +type: docs +title: "协议扩展" +linkTitle: "协议扩展" +weight: 1 +--- + +## 扩展说明 + +RPC 协议扩展,封装远程调用细节。 + +契约: + +* 当用户调用 `refer()` 所返回的 `Invoker` 对象的 `invoke()` 方法时,协议需相应执行同 URL 远端 `export()` 传入的 `Invoker` 对象的 `invoke()` 方法。 +* 其中,`refer()` 返回的 `Invoker` 由协议实现,协议通常需要在此 `Invoker` 中发送远程请求,`export()` 传入的 `Invoker` 由框架实现并传入,协议不需要关心。 + +注意: + +* 协议不关心业务接口的透明代理,以 `Invoker` 为中心,由外层将 `Invoker` 转换为业务接口。 +* 协议不一定要是 TCP 网络通讯,比如通过共享文件,IPC 进程间通讯等。 + +## 扩展接口 + +* `org.apache.dubbo.rpc.Protocol` +* `org.apache.dubbo.rpc.Exporter` +* `org.apache.dubbo.rpc.Invoker` + +```java +public interface Protocol { + /** + * 暴露远程服务:
+ * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();
+ * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。
+ * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。
+ * + * @param 服务的类型 + * @param invoker 服务的执行体 + * @return exporter 暴露服务的引用,用于取消暴露 + * @throws RpcException 当暴露服务出错时抛出,比如端口已占用 + */ + Exporter export(Invoker invoker) throws RpcException; + + /** + * 引用远程服务:
+ * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。
+ * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。
+ * 3. 当url中有设置check=false时,连接失败不能抛出异常,需内部自动恢复。
+ * + * @param 服务的类型 + * @param type 服务的类型 + * @param url 远程服务的URL地址 + * @return invoker 服务的本地代理 + * @throws RpcException 当连接服务提供方失败时抛出 + */ + Invoker refer(Class type, URL url) throws RpcException; + +} +``` + +## 扩展配置 + +```xml + + + + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol` +* `org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol` +* `org.apache.dubbo.rpc.protocol.rmi.RmiProtocol` +* `org.apache.dubbo.rpc.protocol.http.HttpProtocol` +* `org.apache.dubbo.rpc.protocol.http.hessian.HessianProtocol` +* `org.apache.dubbo.rpc.support.MockProtocol` + +## 扩展示例 + +Maven项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxProtocol.java (实现Protocol接口) + |-XxxExporter.java (实现Exporter接口) + |-XxxInvoker.java (实现Invoker接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.Protocol (纯文本文件,内容为:xxx=com.xxx.XxxProtocol) +``` + +XxxProtocol.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.Protocol; + +public class XxxProtocol implements Protocol { + public Exporter export(Invoker invoker) throws RpcException { + return new XxxExporter(invoker); + } + public Invoker refer(Class type, URL url) throws RpcException { + return new XxxInvoker(type, url); + } +} +``` + +XxxExporter.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.support.AbstractExporter; + +public class XxxExporter extends AbstractExporter { + public XxxExporter(Invoker invoker) throws RemotingException{ + super(invoker); + // ... + } + public void unexport() { + super.unexport(); + // ... + } +} +``` + +XxxInvoker.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.support.AbstractInvoker; + +public class XxxInvoker extends AbstractInvoker { + public XxxInvoker(Class type, URL url) throws RemotingException{ + super(type, url); + } + + @Override + protected Result doInvoke(Invocation invocation) throws Throwable { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.Protocol: + +```properties +xxx=com.xxx.XxxProtocol +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/proxy-factory.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/proxy-factory.md new file mode 100644 index 000000000000..ff712ba75dc0 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/proxy-factory.md @@ -0,0 +1,70 @@ +--- +type: docs +title: "动态代理扩展" +linkTitle: "动态代理扩展" +weight: 12 +--- + +## 扩展说明 + +将 `Invoker` 接口转换成业务接口。 + +## 扩展接口 + +`org.apache.dubbo.rpc.ProxyFactory` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.rpc.proxy.JdkProxyFactory` +* `org.apache.dubbo.rpc.proxy.JavassistProxyFactory` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxProxyFactory.java (实现ProxyFactory接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.ProxyFactory (纯文本文件,内容为:xxx=com.xxx.XxxProxyFactory) +``` + +XxxProxyFactory.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.ProxyFactory; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.RpcException; + + +public class XxxProxyFactory implements ProxyFactory { + public T getProxy(Invoker invoker) throws RpcException { + // ... + } + public Invoker getInvoker(T proxy, Class type, URL url) throws RpcException { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.ProxyFactory: + +```properties +xxx=com.xxx.XxxProxyFactory +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/registry.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/registry.md new file mode 100644 index 000000000000..b6b738720676 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/registry.md @@ -0,0 +1,210 @@ +--- +type: docs +title: "注册中心扩展" +linkTitle: "注册中心扩展" +weight: 9 +--- + +## 扩展说明 + +负责服务的注册与发现。 + +## 扩展接口 + +* `org.apache.dubbo.registry.RegistryFactory` +* `org.apache.dubbo.registry.Registry` + +## 扩展配置 + +```xml + + + + + + +``` + +## 扩展契约 + +RegistryFactory.java: + +```java +public interface RegistryFactory { + /** + * 连接注册中心. + * + * 连接注册中心需处理契约:
+ * 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。
+ * 2. 支持URL上的username:password权限认证。
+ * 3. 支持backup=10.20.153.10备选注册中心集群地址。
+ * 4. 支持file=registry.cache本地磁盘文件缓存。
+ * 5. 支持timeout=1000请求超时设置。
+ * 6. 支持session=60000会话超时或过期设置。
+ * + * @param url 注册中心地址,不允许为空 + * @return 注册中心引用,总不返回空 + */ + Registry getRegistry(URL url); +} +``` + +RegistryService.java: + +```java +public interface RegistryService { // Registry extends RegistryService + /** + * 注册服务. + * + * 注册需处理契约:
+ * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。
+ * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。
+ * 3. 当URL设置了category=overrides时,表示分类存储,缺省类别为providers,可按分类部分通知数据。
+ * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。
+ * 5. 允许URI相同但参数不同的URL并存,不能覆盖。
+ * + * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin + */ + void register(URL url); + + /** + * 取消注册服务. + * + * 取消注册需处理契约:
+ * 1. 如果是dynamic=false的持久存储数据,找不到注册数据,则抛IllegalStateException,否则忽略。
+ * 2. 按全URL匹配取消注册。
+ * + * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin + */ + void unregister(URL url); + + /** + * 订阅服务. + * + * 订阅需处理契约:
+ * 1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。
+ * 2. 当URL设置了category=overrides,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。
+ * 3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&version=1.0.0
+ * 4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&group=*&version=*&classifier=*
+ * 5. 当注册中心重启,网络抖动,需自动恢复订阅请求。
+ * 6. 允许URI相同但参数不同的URL并存,不能覆盖。
+ * 7. 必须阻塞订阅过程,等第一次通知完后再返回。
+ * + * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin + * @param listener 变更事件监听器,不允许为空 + */ + void subscribe(URL url, NotifyListener listener); + + /** + * 取消订阅服务. + * + * 取消订阅需处理契约:
+ * 1. 如果没有订阅,直接忽略。
+ * 2. 按全URL匹配取消订阅。
+ * + * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin + * @param listener 变更事件监听器,不允许为空 + */ + void unsubscribe(URL url, NotifyListener listener); + + /** + * 查询注册列表,与订阅的推模式相对应,这里为拉模式,只返回一次结果。 + * + * @see org.apache.dubbo.registry.NotifyListener#notify(List) + * @param url 查询条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin + * @return 已注册信息列表,可能为空,含义同{@link org.apache.dubbo.registry.NotifyListener#notify(List)}的参数。 + */ + List lookup(URL url); + +} +``` + +NotifyListener.java: + +```java +public interface NotifyListener { + /** + * 当收到服务变更通知时触发。 + * + * 通知需处理契约:
+ * 1. 总是以服务接口和数据类型为维度全量通知,即不会通知一个服务的同类型的部分数据,用户不需要对比上一次通知结果。
+ * 2. 订阅时的第一次通知,必须是一个服务的所有类型数据的全量通知。
+ * 3. 中途变更时,允许不同类型的数据分开通知,比如:providers, consumers, routes, overrides,允许只通知其中一种类型,但该类型的数据必须是全量的,不是增量的。
+ * 4. 如果一种类型的数据为空,需通知一个empty协议并带category参数的标识性URL数据。
+ * 5. 通知者(即注册中心实现)需保证通知的顺序,比如:单线程推送,队列串行化,带版本对比。
+ * + * @param urls 已注册信息列表,总不为空,含义同{@link org.apache.dubbo.registry.RegistryService#lookup(URL)}的返回值。 + */ + void notify(List urls); + +} +``` + +## 已知扩展 + +`org.apache.dubbo.registry.support.dubbo.DubboRegistryFactory` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxRegistryFactoryjava (实现RegistryFactory接口) + |-XxxRegistry.java (实现Registry接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.registry.RegistryFactory (纯文本文件,内容为:xxx=com.xxx.XxxRegistryFactory) +``` + +XxxRegistryFactory.java: + +```java +package com.xxx; + +import org.apache.dubbo.registry.RegistryFactory; +import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.common.URL; + +public class XxxRegistryFactory implements RegistryFactory { + public Registry getRegistry(URL url) { + return new XxxRegistry(url); + } +} +``` + +XxxRegistry.java: + +```java +package com.xxx; + +import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.registry.NotifyListener; +import org.apache.dubbo.common.URL; + +public class XxxRegistry implements Registry { + public void register(URL url) { + // ... + } + public void unregister(URL url) { + // ... + } + public void subscribe(URL url, NotifyListener listener) { + // ... + } + public void unsubscribe(URL url, NotifyListener listener) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.registry.RegistryFactory: + +```properties +xxx=com.xxx.XxxRegistryFactory +``` \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/remoting.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/remoting.md new file mode 100644 index 000000000000..e243e8942fa1 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/remoting.md @@ -0,0 +1,127 @@ +--- +type: docs +title: "网络传输扩展" +linkTitle: "网络传输扩展" +weight: 17 +--- + +## 扩展说明 + +远程通讯的服务器及客户端传输实现。 + +## 扩展接口 + +* `org.apache.dubbo.remoting.Transporter` +* `org.apache.dubbo.remoting.Server` +* `org.apache.dubbo.remoting.Client` + +## 扩展配置 + +```xml + + + + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.remoting.transport.transporter.netty.NettyTransporter` +* `org.apache.dubbo.remoting.transport.transporter.mina.MinaTransporter` +* `org.apache.dubbo.remoting.transport.transporter.grizzly.GrizzlyTransporter` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxTransporter.java (实现Transporter接口) + |-XxxServer.java (实现Server接口) + |-XxxClient.java (实现Client接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.remoting.Transporter (纯文本文件,内容为:xxx=com.xxx.XxxTransporter) +``` + +XxxTransporter.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.Transporter; + +public class XxxTransporter implements Transporter { + public Server bind(URL url, ChannelHandler handler) throws RemotingException { + return new XxxServer(url, handler); + } + public Client connect(URL url, ChannelHandler handler) throws RemotingException { + return new XxxClient(url, handler); + } +} +``` + +XxxServer.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.transport.transporter.AbstractServer; + +public class XxxServer extends AbstractServer { + public XxxServer(URL url, ChannelHandler handler) throws RemotingException{ + super(url, handler); + } + protected void doOpen() throws Throwable { + // ... + } + protected void doClose() throws Throwable { + // ... + } + public Collection getChannels() { + // ... + } + public Channel getChannel(InetSocketAddress remoteAddress) { + // ... + } +} +``` + +XxxClient.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.transport.transporter.AbstractClient; + +public class XxxClient extends AbstractClient { + public XxxServer(URL url, ChannelHandler handler) throws RemotingException{ + super(url, handler); + } + protected void doOpen() throws Throwable { + // ... + } + protected void doClose() throws Throwable { + // ... + } + protected void doConnect() throws Throwable { + // ... + } + public Channel getChannel() { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.remoting.Transporter: + +```properties +xxx=com.xxx.XxxTransporter +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/router.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/router.md new file mode 100644 index 000000000000..24d11d06b1d6 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/router.md @@ -0,0 +1,69 @@ +--- +type: docs +title: "路由扩展" +linkTitle: "路由扩展" +weight: 6 +--- + +## 扩展说明 + +从多个服务提供方中选择一个进行调用。 + +## 扩展接口 + +* `org.apache.dubbo.rpc.cluster.RouterFactory` +* `org.apache.dubbo.rpc.cluster.Router` + +## 已知扩展 + +* `org.apache.dubbo.rpc.cluster.router.ScriptRouterFactory` +* `org.apache.dubbo.rpc.cluster.router.FileRouterFactory` +* `org.apache.dubbo.rpc.cluster.router.condition.config.AppRouterFactory` +* `org.apache.dubbo.rpc.cluster.CacheableRouterFactory` +* `org.apache.dubbo.rpc.cluster.router.condition.ConditionRouterFactory` +* `org.apache.dubbo.rpc.cluster.router.mock.MockRouterFactory` +* `org.apache.dubbo.rpc.cluster.router.condition.config.ServiceRouterFactory` +* `org.apache.dubbo.rpc.cluster.router.tag.TagRouterFactory` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxRouterFactory.java (实现RouterFactory接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.rpc.cluster.RouterFactory (纯文本文件,内容为:xxx=com.xxx.XxxRouterFactory) + +``` + +XxxRouterFactory.java: + +```java +package com.xxx; + +import org.apache.dubbo.rpc.cluster.RouterFactory; +import org.apache.dubbo.rpc.Invoker; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.RpcException; + +public class XxxRouterFactory implements RouterFactory { + public Router getRouter(URL url) { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.rpc.cluster.RouterFactory: + +```properties +xxx=com.xxx.XxxRouterFactory +``` + + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/serialize.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/serialize.md new file mode 100644 index 000000000000..f8e5be849cb8 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/serialize.md @@ -0,0 +1,77 @@ +--- +type: docs +title: "序列化扩展" +linkTitle: "序列化扩展" +weight: 16 +--- + +## 扩展说明 + +将对象转成字节流,用于网络传输,以及将字节流转为对象,用于在收到字节流数据后还原成对象。 + +## 扩展接口 + +* `org.apache.dubbo.common.serialize.Serialization` +* `org.apache.dubbo.common.serialize.ObjectInput` +* `org.apache.dubbo.common.serialize.ObjectOutput` + +## 扩展配置 + +```xml + + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.common.serialize.dubbo.DubboSerialization` +* `org.apache.dubbo.common.serialize.hessian.Hessian2Serialization` +* `org.apache.dubbo.common.serialize.java.JavaSerialization` +* `org.apache.dubbo.common.serialize.java.CompactedJavaSerialization` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxSerialization.java (实现Serialization接口) + |-XxxObjectInput.java (实现ObjectInput接口) + |-XxxObjectOutput.java (实现ObjectOutput接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.common.serialize.Serialization (纯文本文件,内容为:xxx=com.xxx.XxxSerialization) +``` + +XxxSerialization.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.serialize.Serialization; +import org.apache.dubbo.common.serialize.ObjectInput; +import org.apache.dubbo.common.serialize.ObjectOutput; + + +public class XxxSerialization implements Serialization { + public ObjectOutput serialize(Parameters parameters, OutputStream output) throws IOException { + return new XxxObjectOutput(output); + } + public ObjectInput deserialize(Parameters parameters, InputStream input) throws IOException { + return new XxxObjectInput(input); + } +} +``` + +META-INF/dubbo/org.apache.dubbo.common.serialize.Serialization: + +```properties +xxx=com.xxx.XxxSerialization +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/status-checker.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/status-checker.md new file mode 100644 index 000000000000..3caece6d1fc9 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/status-checker.md @@ -0,0 +1,69 @@ +--- +type: docs +title: "状态检查扩展" +linkTitle: "状态检查扩展" +weight: 21 +--- + +## 扩展说明 + +检查服务依赖各种资源的状态,此状态检查可同时用于 telnet 的 status 命令和 hosting 的 status 页面。 + +## 扩展接口 + +`org.apache.dubbo.common.status.StatusChecker` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.common.status.support.MemoryStatusChecker` +* `org.apache.dubbo.common.status.support.LoadStatusChecker` +* `org.apache.dubbo.rpc.dubbo.status.ServerStatusChecker` +* `org.apache.dubbo.rpc.dubbo.status.ThreadPoolStatusChecker` +* `org.apache.dubbo.registry.directory.RegistryStatusChecker` +* `org.apache.dubbo.rpc.config.spring.status.SpringStatusChecker` +* `org.apache.dubbo.rpc.config.spring.status.DataSourceStatusChecker` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxStatusChecker.java (实现StatusChecker接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.common.status.StatusChecker (纯文本文件,内容为:xxx=com.xxx.XxxStatusChecker) +``` + +XxxStatusChecker.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.status.StatusChecker; + +public class XxxStatusChecker implements StatusChecker { + public Status check() { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.common.status.StatusChecker: + +```properties +xxx=com.xxx.XxxStatusChecker +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/telnet-handler.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/telnet-handler.md new file mode 100644 index 000000000000..b4a8e5d9f09f --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/telnet-handler.md @@ -0,0 +1,82 @@ +--- +type: docs +title: "Telnet 命令扩展" +linkTitle: "Telnet 命令扩展" +weight: 20 +--- + +## 扩展说明 + +所有服务器均支持 telnet 访问,用于人工干预。 + +## 扩展接口 + +`org.apache.dubbo.remoting.telnet.TelnetHandler` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.remoting.telnet.support.ClearTelnetHandler` +* `org.apache.dubbo.remoting.telnet.support.ExitTelnetHandler` +* `org.apache.dubbo.remoting.telnet.support.HelpTelnetHandler` +* `org.apache.dubbo.remoting.telnet.support.StatusTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.ListTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.ChangeTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.CurrentTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.InvokeTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.TraceTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.CountTelnetHandler` +* `org.apache.dubbo.rpc.dubbo.telnet.PortTelnetHandler` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxTelnetHandler.java (实现TelnetHandler接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.remoting.telnet.TelnetHandler (纯文本文件,内容为:xxx=com.xxx.XxxTelnetHandler) +``` + +XxxTelnetHandler.java: + +```java +package com.xxx; + +import org.apache.dubbo.remoting.telnet.TelnetHandler; + +@Help(parameter="...", summary="...", detail="...") + +public class XxxTelnetHandler implements TelnetHandler { + public String telnet(Channel channel, String message) throws RemotingException { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.remoting.telnet.TelnetHandler: + +```properties +xxx=com.xxx.XxxTelnetHandler +``` + +## 用法 + +```sh +telnet 127.0.0.1 20880 +dubbo> xxx args +``` diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/threadpool.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/threadpool.md new file mode 100644 index 000000000000..466456d684fc --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/threadpool.md @@ -0,0 +1,66 @@ +--- +type: docs +title: "线程池扩展" +linkTitle: "线程池扩展" +weight: 15 +--- + +## 扩展说明 + +服务提供方线程池实现策略,当服务器收到一个请求时,需要在线程池中创建一个线程去执行服务提供方业务逻辑。 + +## 扩展接口 + +`org.apache.dubbo.common.threadpool.ThreadPool` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +* `org.apache.dubbo.common.threadpool.FixedThreadPool` +* `org.apache.dubbo.common.threadpool.CachedThreadPool` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxThreadPool.java (实现ThreadPool接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.common.threadpool.ThreadPool (纯文本文件,内容为:xxx=com.xxx.XxxThreadPool) +``` + +XxxThreadPool.java: + +```java +package com.xxx; + +import org.apache.dubbo.common.threadpool.ThreadPool; +import java.util.concurrent.Executor; + +public class XxxThreadPool implements ThreadPool { + public Executor getExecutor() { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.common.threadpool.ThreadPool: + +```properties +xxx=com.xxx.XxxThreadPool +``` + diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/description/validation.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/validation.md new file mode 100644 index 000000000000..a5e0178bd615 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/description/validation.md @@ -0,0 +1,80 @@ +--- +type: docs +title: "验证扩展" +linkTitle: "验证扩展" +weight: 25 +--- + +## 扩展说明 + +参数验证扩展点。 + +## 扩展接口 + +`org.apache.dubbo.validation.Validation` + +## 扩展配置 + +```xml + + + +``` + +## 已知扩展 + +`org.apache.dubbo.validation.support.jvalidation.JValidation` + +## 扩展示例 + +Maven 项目结构: + +``` +src + |-main + |-java + |-com + |-xxx + |-XxxValidation.java (实现Validation接口) + |-resources + |-META-INF + |-dubbo + |-org.apache.dubbo.validation.Validation (纯文本文件,内容为:xxx=com.xxx.XxxValidation) +``` + +XxxValidation.java: + +```java +package com.xxx; + +import org.apache.dubbo.validation.Validation; + +public class XxxValidation implements Validation { + public Object getValidator(URL url) { + // ... + } +} +``` + +XxxValidator.java: + +```java +package com.xxx; + +import org.apache.dubbo.validation.Validator; + +public class XxxValidator implements Validator { + public XxxValidator(URL url) { + // ... + } + public void validate(Invocation invocation) throws Exception { + // ... + } +} +``` + +META-INF/dubbo/org.apache.dubbo.validation.Validation: + +```properties +xxx=com.xxx.XxxValidation +``` \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/reference-manual/spi/overview/_index.md b/content/zh/docs3-building/java-sdk/reference-manual/spi/overview/_index.md new file mode 100644 index 000000000000..1e4143d8d413 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/reference-manual/spi/overview/_index.md @@ -0,0 +1,101 @@ +--- +type: docs +title: "Dubbo SPI概述" +linkTitle: "Dubbo SPI概述" +weight: 1 +description: "Dubbo 通过 SPI 机制提供了非常灵活的可扩展性" +--- + +## 扩展设计理念 + +可扩展性是任何一个系统所追求的,对于 Dubbo 来说是同样适用。 + +### 什么是可扩展性 + +可扩展性是一种设计理念,代表了我们对未来的一种预想,我们希望在现有的架构或设计基础上,当未来某些方面发生变化的时候,我们能够以最小的改动来适应这种变化。 + +### 可扩展性的优点 + +可扩展性的优点主要表现模块之间解耦,它符合开闭原则,对扩展开放,对修改关闭。当系统增加新功能时,不需要对现有系统的结构和代码进行修改,仅仅新增一个扩展即可。 + +### 扩展实现方式 + +一般来说,系统会采用 Factory、IoC、OSGI 等方式管理扩展(插件)生命周期。考虑到 Dubbo 的适用面,不想强依赖 Spring 等 IoC 容器。 +而自己造一个小的 IoC 容器,也觉得有点过度设计,所以选择最简单的 Factory 方式管理扩展(插件)。在 Dubbo 中,所有内部实现和第三方实现都是平等的。 + +### Dubbo 中的可扩展性 + +* 平等对待第三方的实现。在 Dubbo 中,所有内部实现和第三方实现都是平等的,用户可以基于自身业务需求,替换 Dubbo 提供的原生实现。 +* 每个扩展点只封装一个变化因子,最大化复用。每个扩展点的实现者,往往都只是关心一件事。如果用户有需求需要进行扩展,那么只需要对其关注的扩展点进行扩展就好,极大的减少用户的工作量。 + +## Dubbo 扩展的特性 + +Dubbo 中的扩展能力是从 JDK 标准的 SPI 扩展点发现机制加强而来,它改进了 JDK 标准的 SPI 以下问题: + +* JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。 +* 如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。 + +用户能够基于 Dubbo 提供的扩展能力,很方便基于自身需求扩展其他协议、过滤器、路由等。下面介绍下 Dubbo 扩展能力的特性。 + +* 按需加载。Dubbo 的扩展能力不会一次性实例化所有实现,而是用那个扩展类则实例化那个扩展类,减少资源浪费。 +* 增加扩展类的 IOC 能力。Dubbo 的扩展能力并不仅仅只是发现扩展服务实现类,而是在此基础上更进一步,如果该扩展类的属性依赖其他对象,则 Dubbo 会自动的完成该依赖对象的注入功能。 +* 增加扩展类的 AOP 能力。Dubbo 扩展能力会自动的发现扩展类的包装类,完成包装类的构造,增强扩展类的功能。 +* 具备动态选择扩展实现的能力。Dubbo 扩展会基于参数,在运行时动态选择对应的扩展类,提高了 Dubbo 的扩展能力。 +* 可以对扩展实现进行排序。能够基于用户需求,指定扩展实现的执行顺序。 +* 提供扩展点的 Adaptive 能力。该能力可以使的一些扩展类在 consumer 端生效,一些扩展类在 provider 端生效。 + +从 Dubbo 扩展的设计目标可以看出,Dubbo 实现的一些例如动态选择扩展实现、IOC、AOP 等特性,能够为用户提供非常灵活的扩展能力。 + +## Dubbo 扩展加载流程 + +Dubbo 加载扩展的整个流程如下: + +![//imgs/v3/concepts/extension-load.png](/imgs/v3/concepts/extension-load.png) + +主要步骤为 4 个: +* 读取并解析配置文件 +* 缓存所有扩展实现 +* 基于用户执行的扩展名,实例化对应的扩展实现 +* 进行扩展实例属性的 IOC 注入以及实例化扩展的包装类,实现 AOP 特性 + +## 如何使用 Dubbo 扩展能力进行扩展 + +下面以扩展注册中心为例进行说明如何利用 Dubbo 提供的扩展能力扩展 Triple 协议。 + +(1) 在协议的实现 jar 包内放置文本文件:META-INF/dubbo/org.apache.dubbo.remoting.api.WireProtocol +```text +tri=org.apache.dubbo.rpc.protocol.tri.TripleHttp2Protocol +``` + +(2) 实现类内容 +```java +@Activate +public class TripleHttp2Protocol extends Http2WireProtocol { + // ... +} +``` + +说明下:Http2WireProtocol 实现了 WireProtocol 接口 + +(3) Dubbo 配置模块中,扩展点均有对应配置属性或标签,通过配置指定使用哪个扩展实现。比如: +```text + +``` + +从上面的扩展步骤可以看出,用户基本在黑盒下就完成了扩展。 + +## Dubbo 扩展的应用 + +Dubbo 的扩展能力非常灵活,在自身功能的实现上无处不在。 + +![//imgs/v3/concepts/extension-use.png](/imgs/v3/concepts/extension-use.png) + +Dubbo 扩展能力使得 Dubbo 项目很方便的切分成一个一个的子模块,实现热插拔特性。用户完全可以基于自身需求,替换 Dubbo 原生实现,来满足自身业务需求。 + +## 使用场景 + +* 如果你需要自定义负载均衡策略,你可以使用 Dubbo 扩展能力。 +* 如果你需要实现自定义的注册中心,你可以使用 Dubbo 扩展能力。 +* 如果你需要实现自定义的过滤器,你可以使用 Dubbo 扩展能力。 + +Dubbo 扩展平等的对待内部实现和第三方实现。更多使用场景,参见 [SPI 扩展实现](../../references/spis/) \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/2.x-to-3.x-compatibility-guide.md b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/2.x-to-3.x-compatibility-guide.md new file mode 100644 index 000000000000..86265a57b6f9 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/2.x-to-3.x-compatibility-guide.md @@ -0,0 +1,46 @@ +--- +type: docs +title: "2.x升级至3.x操作指南" +linkTitle: "2.x升级至3.x操作指南" +weight: 1 +description: "快速了解 Dubbo3 的升级步骤与兼容性" +--- + +**无需改动任何代码,直接升级到 Dubbo 3.0。** + +在 3.0 版本的设计与开发之初,我们就定下了兼容老版本 Dubbo 用户(2.5、2.6、2.7)的目标。因此,往 3.0 版本的升级过程将会是完全透明的,用户无需做任何业务改造,升级 3.x 后的框架行为将保持与 2.x 版本完全一致。 + +```xml + + org.apache.dubbo + dubbo + 3.0.0 + +``` + + +但也要注意,透明升级仅仅是通往 3.0 的第一步,因为 "框架行为保持一致" 也就意味着用户将无法体验到 3.0 的新特性。**如果要启用 3.0 的带来的新特性,用户则需要进行一定的改造,我们称这个过程为迁移,这是一个按需开启的过程。** + + + +因此,对老用户而言,有两条不同的迁移路径: + +* **分两步走,先以兼容模式推动业务升级到 3.0 版本(无需改造),之后在某些时机按需启用新特性(按需改造);** +* **升级与迁移同步完成,在业务升级到 3.0 版本的同时,完成改造并启用新特性;** + + + +Dubbo 3.0 提供的新特性包括: + +* **新的地址发现模型(应用级服务发现)。** + * 查看[应用级服务发现的迁移步骤](../migration-service-discovery) + * 查看[应用级服务发现的使用方式]() + * 查看[应用级服务发现设计与实现]()。 +* **下一代基于 HTTP/2 的 Triple 协议。** + * 查看[Triple 协议迁移步骤](../migration-triple) + * 查看 [Triple 协议使用方式](../../references/protocols/tri) + * 查看 Triple 协议设计与实现。 +* **统一的路由规则。** + * 查看[统一路由规则的迁移步骤](../migration-routingrule) + * 查看[统一路由规则使用方式](../../references/routerule) + * 查看统一路由规则设计与实现 \ No newline at end of file diff --git a/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/_index.md b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/_index.md new file mode 100755 index 000000000000..9598f4b44120 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/_index.md @@ -0,0 +1,8 @@ + +--- +type: docs +title: "升级和兼容性" +linkTitle: "升级和兼容性" +weight: 6 +--- + diff --git a/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-service-discovery.md b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-service-discovery.md new file mode 100644 index 000000000000..9e009e4340bf --- /dev/null +++ b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-service-discovery.md @@ -0,0 +1,160 @@ +--- +type: docs +title: "接口级服务发现迁移至应用级服务发现指南" +linkTitle: "接口级服务发现迁移至应用级服务发现指南" +weight: 3 +description: "本文档专门针对使用 2.x 版本的老用户,详细阐述了升级到 3.x 后的默认地址注册与发现行为、如何平滑的迁移到新版本的地址模型。" +--- + +**总体上来说,在地址注册与发现环节,3.x 是完全兼容 2.x 版本的,这意味着,用户可以选择将集群内任意数量的应用或机器升级到 3.x,同时在这个过程中保持与 2.x 版本的互操作性。** + + + +## 1 快速升级步骤 + +简单的修改 pom.xml 到最新版本就可以完成升级,如果要迁移到应用级地址,只需要调整开关控制 3.x 版本的默认行为。 + +1. 升级 Provider 应用到最新 3.x 版本依赖,配置双注册开关`dubbo.application.register-mode=all`(建议通过全局配置中心设置,默认已自动开启),完成应用发布。 +2. 升级 Consumer 应用到最新 3.x 版本依赖,配置双订阅开关`dubbo.application.service-discovery.migration=APPLICATION_FIRST`(建议通过全局配置中心设置,默认已自动开启),完成应用发布。 +3. 在确认 Provider 的上有 Consumer 全部完成应用级地址迁移后,Provider 切到应用级地址单注册。完成升级 + + + +以下是关于迁移流程的详细描述。 + +## 2 Provider 端升级过程详解 + +在不改变任何 Dubbo 配置的情况下,可以将一个应用或实例升级到 3.x 版本,升级后的 Dubbo 实例会默保保证与 2.x 版本的兼容性,即会正常注册 2.x 格式的地址到注册中心,因此升级后的实例仍会对整个集群仍保持可见状态。 + + + +同时新的地址发现模型(注册应用级别的地址)也将会自动注册。 + +![//imgs/v3/migration/provider-registration.png](/imgs/v3/migration/provider-registration.png) + +通过 -D 参数,可以指定 provider 启动时的注册行为 + +```text +-Ddubbo.application.register-mode=all +# 可选值 interface、instance、all,默认是 all,即接口级地址、应用级地址都注册 +``` + + + +另外,可以在配置中心修改全局默认行为,来控制所有 3.x 实例注册行为。其中,全局性开关的优先级低于 -D 参数。 + + + +为了保证平滑迁移,即升级到 3.x 的实例能同时被 2.x 与 3.x 的消费者实例发现,3.x 实例需要开启双注册;当所有上游的消费端都迁移到 3.x 的地址模型后,提供端就可以切换到 instance 模式(只注册应用级地址)。对于如何升级消费端到 3.x 请参见下一小节。 + +### 2.1 双注册带来的资源消耗 + +双注册不可避免的会带来额外的注册中心存储压力,但考虑到应用级地址发现模型的数据量在存储方面的极大优势,即使对于一些超大规模集群的用户而言,新增的数据量也并不会带来存储问题。总体来说,对于一个普通集群而言,数据增长可控制在之前数据总量的 1/100 ~ 1/1000 + +以一个中等规模的集群实例来说: 2000 实例、50个应用(500 个 Dubbo 接口,平均每个应用 10 个接口)。 + +​ 假设每个接口级 URL 地址平均大小为 5kb,每个应用级 URL 平均大小为 0.5kb + +​ 老的接口级地址量:2000 * 500 * 5kb ≈ 4.8G + +​ 新的应用级地址量:2000 * 50 * 0.5kb ≈ 48M + +​ 双注册后仅仅增加了 48M 的数据量。 + + + +## 3 Consumer 端升级过程 + +对于 2.x 的消费者实例,它们看到的自然都是 2.x 版本的提供者地址列表; + +对于 3.x 的消费者,它具备同时发现 2.x 与 3.x 提供者地址列表的能力。在默认情况下,如果集群中存在可以消费的 3.x 的地址,将自动消费 3.x 的地址,如果不存在新地址则自动消费 2.x 的地址。Dubbo3 提供了开关来控制这个行为: + +```text +dubbo.application.service-discovery.migration=APPLICATION_FIRST +# 可选值 +# FORCE_INTERFACE,只消费接口级地址,如无地址则报错,单订阅 2.x 地址 +# APPLICATION_FIRST,智能决策接口级/应用级地址,双订阅 +# FORCE_APPLICATION,只消费应用级地址,如无地址则报错,单订阅 3.x 地址 +``` + +`dubbo.application.service-discovery.migration ` 支持通过 `-D` 以及 `全局配置中心` 两种方式进行配置。 + + + +![//imgs/v3/migration/consumer-subscription.png](/imgs/v3/migration/consumer-subscription.png) + + +接下来,我们就具体看一下,如何通过双订阅模式(APPLICATION_FIRST)让升级到 3.x 的消费端迁移到应用级别的地址。在具体展开之前,先明确一条消费端的选址行为:**对于双订阅的场景,消费端虽然可同时持有 2.x 地址与 3.x 地址,但选址过程中两份地址是完全隔离的:要么用 2.x 地址,要么用 3.x 地址,不存在两份地址混合调用的情况,这个决策过程是在收到第一次地址通知后就完成了的。** + + + +下面,我们看一个`APPLICATION_FIRST`策略的具体操作过程。 + +首先,提前在全局配置中心 Nacos 配置一条配置项(所有消费端都将默认执行这个选址策略): + +![//imgs/v3/migration/nacos-migration-item.png](/imgs/v3/migration/nacos-migration-item.png) + + + +紧接着,升级消费端到 3.x 版本并启动,这时消费端读取到`APPLICATION_FIRST`配置后,执行双订阅逻辑(订阅 2.x 接口级地址与 3.x 应用级地址) + + + +至此,升级操作就完成了,剩下的就是框架内部的执行了。在调用发生前,框架在消费端会有一个“选址过程”,注意这里的选址和之前 2.x 版本是有区别的,选址过程包含了两层筛选: + +* 先进行地址列表(ClusterInvoker)筛选(接口级地址 or 应用级地址) +* 再进行实际的 provider 地址(Invoker)筛选。 + +![//imgs/v3/migration/migration-cluster-item.png](/imgs/v3/migration/migration-cluster-invoker.png) + +ClusterInvoker 筛选的依据,可以通过 MigrationAddressComparator SPI 自行定义,目前官方提供了一个简单的地址数量比对策略,即当 `应用级地址数量 == 接口级地址数量` 满足时则进行迁移。 + +> 其实 FORCE_INTERFACE、APPLICATION_FIRST、FORCE_APPLICATION 控制的都是这里的 ClusterInvoker 类型的筛选策略 + + + +### 3.1 双订阅带来的资源消耗 + +双订阅不可避免的会增加消费端的内存消耗,但由于应用级地址发现在地址总量方面的优势,这个过程通常是可接受的,我们从两个方面进行分析: + +1. 双订阅带来的地址推送数据量增长。这点我们在 ”双注册资源消耗“ 一节中做过介绍,应用级服务发现带来的注册中心数据量增长非常有限。 +2. 双订阅带来的消费端内存增长。要注意双订阅只存在于启动瞬态,在ClusterInvoker选址决策之后其中一份地址就会被完全销毁;对单个服务来说,启动阶段双订阅带来的内存增长大概能控制在原内存量的 30% ~ 40%,随后就会下降到单订阅水平,如果切到应用级地址,能实现内存 50% 的下降。 + + + +### 3.2 消费端更细粒度的控制 + +除了全局的迁移策略之外,Dubbo 在消费端提供了更细粒度的迁移策略支持。控制单位可以是某一个消费者应用,它消费的服务A、服务B可以有各自独立的迁移策略,具体是用方式是在消费端配置迁移规则: + + +```yaml +key: demo-consumer +step: APPLICATION_FIRST +applications: + - name: demo-provider + step: FORCE_APPLICATION +services: + - serviceKey: org.apache.dubbo.config.api.DemoService:1.0.0 + step: FORCE_INTERFACE +``` + +使用这种方式能做到比较精细迁移控制,但是当下及后续的改造成本会比较高,除了一些特别场景,我们不太推荐启用这种配置方式。 +([迁移指南](../../advanced/migration-invoker/)) **官方推荐使用的全局的开关式的迁移策略,让消费端实例在启动阶段自行决策使用哪份可用的地址列表。** + + + +## 4 迁移状态的收敛 + +为了同时兼容 2.x 版本,升级到 3.x 版本的应用在一段时间内要么处在双注册状态,要么处在双订阅状态。 + +解决这个问题,我们还是从 Provider 视角来看,当所有的 Provider 都切换到应用级地址注册之后,也就不存在双订阅的问题了。 + +### 4.1 不同的升级策略影响很大 + +毫无疑问越早越彻底的升级,就能尽快摆脱这个局面。设想,如果可以将组织内所有的应用都升级到 3.x 版本,则版本收敛就变的非常简单:升级过程中 Provider 始终保持双注册,当所有的应用都升级到 3.x 之后,就可以调整全局默认行为,让 Provider 都变成应用级地址单注册了,这个过程并不会给 Consumer 应用带来困扰,因为它们已经是可以识别应用级地址的 3.x 版本了。 + +如果没有办法做到应用的全量升级,甚至在相当长的时间内只能升级一部分应用,则不可避免的迁移状态要持续比较长的时间。 +在这种情况下,我们追求的只能是尽量保持已升级应用的上下游实现版本及功能收敛。推动某些 Provider 的上游消费者都升级到 Dubbo3,这样就可以解除这部分 Provider 的双注册,要做到这一点,可能需要一些辅助统计工具的支持。 + +1. 要能分析出应用间的依赖关系,比如一个 Provdier 应用被哪些消费端应用消费,这可以通过 Dubbo 提供的服务元数据上报能力来实现。 +2. 要能知道每个应用当前使用的 dubbo 版本,可以通过扫描或者主动上报手段。 diff --git a/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-triple.md b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-triple.md new file mode 100644 index 000000000000..c9c4095ee014 --- /dev/null +++ b/content/zh/docs3-building/java-sdk/upgrades-and-compatibility/migration-triple.md @@ -0,0 +1,344 @@ +--- +type: docs +title: "Dubbo协议迁移至Triple协议指南" +linkTitle: "Dubbo协议迁移至Triple协议指南" +weight: 2 +description: "Triple协议迁移指南" +--- + +## Triple 介绍 + +`Triple` 协议的格式和原理请参阅 [RPC 通信协议](https://dubbo.apache.org/zh/docs/concepts/rpc-protocol/) + +根据 Triple 设计的目标,`Triple` 协议有以下优势: + +- 具备跨语言交互的能力,传统的多语言多 SDK 模式和 Mesh 化跨语言模式都需要一种更通用易扩展的数据传输协议。 +- 提供更完善的请求模型,除了支持传统的 Request/Response 模型(Unary 单向通信),还支持 Stream(流式通信) 和 Bidirectional(双向通信)。 +- 易扩展、穿透性高,包括但不限于 Tracing / Monitoring 等支持,也应该能被各层设备识别,网关设施等可以识别数据报文,对 Service Mesh 部署友好,降低用户理解难度。 +- 完全兼容 grpc,客户端/服务端可以与原生grpc客户端打通。 +- 可以复用现有 grpc 生态下的组件, 满足云原生场景下的跨语言、跨环境、跨平台的互通需求。 + +当前使用其他协议的 Dubbo 用户,框架提供了兼容现有序列化方式的迁移能力,在不影响线上已有业务的前提下,迁移协议的成本几乎为零。 + +需要新增对接 Grpc 服务的 Dubbo 用户,可以直接使用 Triple 协议来实现打通,不需要单独引入 grpc client 来完成,不仅能保留已有的 Dubbo 易用性,也能降低程序的复杂度和开发运维成本,不需要额外进行适配和开发即可接入现有生态。 + +对于需要网关接入的 Dubbo 用户,Triple 协议提供了更加原生的方式,让网关开发或者使用开源的 grpc 网关组件更加简单。网关可以选择不解析 payload ,在性能上也有很大提高。在使用 Dubbo 协议时,语言相关的序列化方式是网关的一个很大痛点,而传统的 HTTP 转 Dubbo 的方式对于跨语言序列化几乎是无能为力的。同时,由于 Triple 的协议元数据都存储在请求头中,网关可以轻松的实现定制需求,如路由和限流等功能。 + + +## Dubbo2 协议迁移流程 + +Dubbo2 的用户使用 dubbo 协议 + 自定义序列化,如 hessian2 完成远程调用。 + +而 Grpc 的默认仅支持 Protobuf 序列化,对于 Java 语言中的多参数以及方法重载也无法支持。 + +Dubbo3的之初就有一条目标是完美兼容 Dubbo2,所以为了 Dubbo2 能够平滑升级, Dubbo 框架侧做了很多工作来保证升级的无感,目前默认的序列化和 Dubbo2 保持一致为`hessian2`。 + +所以,如果决定要升级到 Dubbo3 的 `Triple` 协议,只需要修改配置中的协议名称为 `tri` (注意: 不是triple)即可。 + +接下来我们我们以一个使用 Dubbo2 协议的[工程](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/org/apache/dubbo/sample/tri/migration) 来举例,如何一步一步安全的升级。 + +1. 仅使用 `dubbo` 协议启动 `provider` 和 `consumer`,并完成调用。 +2. 使用 `dubbo` 和 `tri` 协议 启动`provider`,以 `dubbo` 协议启动 `consumer`,并完成调用。 +3. 仅使用 `tri` 协议 启动 `provider`和 `consumer`,并完成调用。 + +### 定义服务 + +1. 定义接口 +```java +public interface IWrapperGreeter { + + //... + + /** + * 这是一个普通接口,没有使用 pb 序列化 + */ + String sayHello(String request); + +} +``` + +2. 实现类如下 +```java +public class IGreeter2Impl implements IWrapperGreeter { + + @Override + public String sayHello(String request) { + return "hello," + request; + } +} +``` + +### 仅使用 dubbo 协议 + +为保证兼容性,我们先将部分 provider 升级到 `dubbo3` 版本并使用 `dubbo` 协议。 + +使用 `dubbo` 协议启动一个 [`Provider`](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/com/apache/dubbo/sample/basic/migration/ApiMigrationDubboProvider) 和 [`Consumer`](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/com/apache/dubbo/sample/basic/migration/ApiMigrationDubboConsumer) ,完成调用,输出如下: +![result](/imgs/v3/migration/tri/dubbo3-tri-migration-dubbo-dubbo-result.png) + +### 同时使用 dubbo 和 triple 协议 + +对于线上服务的升级,不可能一蹴而就同时完成 provider 和 consumer 升级, 需要按步操作,保证业务稳定。 +第二步, provider 提供双协议的方式同时支持 dubbo + tri 两种协议的客户端。 + +结构如图所示: +![strust](/imgs/v3/migration/tri/migrate-dubbo-tri-strust.png) + +> 按照推荐升级步骤,provider 已经支持了tri协议,所以 dubbo3的 consumer 可以直接使用 tri 协议 + +使用`dubbo`协议和`triple`协议启动[`Provider`](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/com/apache/dubbo/sample/basic/migration/ApiMigrationBothProvider)和[`Consumer`](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/com/apache/dubbo/sample/basic/migration/ApiMigrationBothConsumer),完成调用,输出如下: + +![result](/imgs/v3/migration/tri/dubbo3-tri-migration-both-dubbo-tri-result.png) + + +### 仅使用 triple 协议 + +当所有的 consuemr 都升级至支持 `Triple` 协议的版本后,provider 可切换至仅使用 `Triple` 协议启动 + +结构如图所示: +![strust](/imgs/v3/migration/tri/migrate-only-tri-strust.png) + +[Provider](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/com/apache/dubbo/sample/basic/migration/ApiMigrationTriProvider) +和 [Consumer](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/src/main/java/com/apache/dubbo/sample/basic/migration/ApiMigrationTriConsumer) 完成调用,输出如下: + +![result](/imgs/v3/migration/tri/dubbo3-tri-migration-tri-tri-result.png) + + +### 实现原理 + +通过上面介绍的升级过程,我们可以很简单的通过修改协议类型来完成升级。框架是怎么帮我们做到这些的呢? + +通过对 `Triple` 协议的介绍,我们知道Dubbo3的 `Triple` 的数据类型是 `protobuf` 对象,那为什么非 `protobuf` 的 java 对象也可以被正常传输呢。 + +这里 Dubbo3 使用了一个巧妙的设计,首先判断参数类型是否为 `protobuf` 对象,如果不是。用一个 `protobuf` 对象将 `request` 和 `response` 进行 wrapper,这样就屏蔽了其他各种序列化带来的复杂度。在 `wrapper` 对象内部声明序列化类型,来支持序列化的扩展。 + +wrapper 的`protobuf`的 IDL如下: +```proto +syntax = "proto3"; + +package org.apache.dubbo.triple; + +message TripleRequestWrapper { + // hessian4 + // json + string serializeType = 1; + repeated bytes args = 2; + repeated string argTypes = 3; +} + +message TripleResponseWrapper { + string serializeType = 1; + bytes data = 2; + string type = 3; +} +``` + +对于请求,使用`TripleRequestWrapper`进行包装,对于响应使用`TripleResponseWrapper`进行包装。 + +> 对于请求参数,可以看到 args 被`repeated`修饰,这是因为需要支持 java 方法的多个参数。当然,序列化只能是一种。序列化的实现沿用 Dubbo2 实现的 spi + + +## 多语言用户 (正在使用 Protobuf) +> 建议新服务均使用该方式 + +对于 Dubbo3 和 Triple 来说,主推的是使用 `protobuf` 序列化,并且使用 `proto` 定义的 `IDL` 来生成相关接口定义。以 `IDL` 做为多语言中的通用接口约定,加上 `Triple` 与 `Grpc` 的天然互通性,可以轻松地实现跨语言交互,例如 Go 语言等。 + + +将编写好的 `.proto` 文件使用 `dubbo-compiler` 插件进行编译并编写实现类,完成方法调用: + +![result](/imgs/v3/migration/tri/dubbo3-tri-migration-tri-tri-result.png) + +从上面升级的例子我们可以知道,`Triple` 协议使用 `protbuf` 对象序列化后进行传输,所以对于本身就是 `protobuf` 对象的方法来说,没有任何其他逻辑。 + +使用 `protobuf` 插件编译后接口如下: +```java +public interface PbGreeter { + + static final String JAVA_SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; + static final String SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; + + static final boolean inited = PbGreeterDubbo.init(); + + org.apache.dubbo.sample.tri.GreeterReply greet(org.apache.dubbo.sample.tri.GreeterRequest request); + + default CompletableFuture greetAsync(org.apache.dubbo.sample.tri.GreeterRequest request){ + return CompletableFuture.supplyAsync(() -> greet(request)); + } + + void greetServerStream(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver responseObserver); + + org.apache.dubbo.common.stream.StreamObserver greetStream(org.apache.dubbo.common.stream.StreamObserver responseObserver); +} +``` + +## 开启 Triple 新特性 —— Stream (流) +Stream 是 Dubbo3 新提供的一种调用类型,在以下场景时建议使用流的方式: + +- 接口需要发送大量数据,这些数据无法被放在一个 RPC 的请求或响应中,需要分批发送,但应用层如果按照传统的多次 RPC 方式无法解决顺序和性能的问题,如果需要保证有序,则只能串行发送 +- 流式场景,数据需要按照发送顺序处理, 数据本身是没有确定边界的 +- 推送类场景,多个消息在同一个调用的上下文中被发送和处理 + +Stream 分为以下三种: +- SERVER_STREAM(服务端流) +![SERVER_STREAM](/imgs/v3/migration/tri/migrate-server-stream.png) +- CLIENT_STREAM(客户端流) +![CLIENT_STREAM](/imgs/v3/migration/tri/migrate-client-stream.png) +- BIDIRECTIONAL_STREAM(双向流) +![BIDIRECTIONAL_STREAM](/imgs/v3/migration/tri/migrate-bi-stream.png) + +> 由于 `java` 语言的限制,BIDIRECTIONAL_STREAM 和 CLIENT_STREAM 的实现是一样的。 + +在 Dubbo3 中,流式接口以 `SteamObserver` 声明和使用,用户可以通过使用和实现这个接口来发送和处理流的数据、异常和结束。 + +> 对于 Dubbo2 用户来说,可能会对StreamObserver感到陌生,这是Dubbo3定义的一种流类型,Dubbo2 中并不存在 Stream 的类型,所以对于迁移场景没有任何影响。 + +流的语义保证 +- 提供消息边界,可以方便地对消息单独处理 +- 严格有序,发送端的顺序和接收端顺序一致 +- 全双工,发送不需要等待 +- 支持取消和超时 + +### 非 PB 序列化的流 +1. api +```java +public interface IWrapperGreeter { + + StreamObserver sayHelloStream(StreamObserver response); + + void sayHelloServerStream(String request, StreamObserver response); +} +``` + +> Stream 方法的方法入参和返回值是严格约定的,为防止写错而导致问题,Dubbo3 框架侧做了对参数的检查, 如果出错则会抛出异常。 +> 对于 `双向流(BIDIRECTIONAL_STREAM)`, 需要注意参数中的 `StreamObserver` 是响应流,返回参数中的 `StreamObserver` 为请求流。 + +2. 实现类 +```java +public class WrapGreeterImpl implements WrapGreeter { + + //... + + @Override + public StreamObserver sayHelloStream(StreamObserver response) { + return new StreamObserver() { + @Override + public void onNext(String data) { + System.out.println(data); + response.onNext("hello,"+data); + } + + @Override + public void onError(Throwable throwable) { + throwable.printStackTrace(); + } + + @Override + public void onCompleted() { + System.out.println("onCompleted"); + response.onCompleted(); + } + }; + } + + @Override + public void sayHelloServerStream(String request, StreamObserver response) { + for (int i = 0; i < 10; i++) { + response.onNext("hello," + request); + } + response.onCompleted(); + } +} +``` + +3. 调用方式 +```java +delegate.sayHelloServerStream("server stream", new StreamObserver() { + @Override + public void onNext(String data) { + System.out.println(data); + } + + @Override + public void onError(Throwable throwable) { + throwable.printStackTrace(); + } + + @Override + public void onCompleted() { + System.out.println("onCompleted"); + } +}); + + +StreamObserver request = delegate.sayHelloStream(new StreamObserver() { + @Override + public void onNext(String data) { + System.out.println(data); + } + + @Override + public void onError(Throwable throwable) { + throwable.printStackTrace(); + } + + @Override + public void onCompleted() { + System.out.println("onCompleted"); + } +}); +for (int i = 0; i < n; i++) { + request.onNext("stream request" + i); +} +request.onCompleted(); +``` + +## 使用 Protobuf 序列化的流 + +对于 `Protobuf` 序列化方式,推荐编写 `IDL` 使用 `compiler` 插件进行编译生成。生成的代码大致如下: +```java +public interface PbGreeter { + + static final String JAVA_SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; + static final String SERVICE_NAME = "org.apache.dubbo.sample.tri.PbGreeter"; + + static final boolean inited = PbGreeterDubbo.init(); + + //... + + void greetServerStream(org.apache.dubbo.sample.tri.GreeterRequest request, org.apache.dubbo.common.stream.StreamObserver responseObserver); + + org.apache.dubbo.common.stream.StreamObserver greetStream(org.apache.dubbo.common.stream.StreamObserver responseObserver); +} +``` + +### 流的实现原理 + +`Triple`协议的流模式是怎么支持的呢? + +- 从协议层来说,`Triple` 是建立在 `HTTP2` 基础上的,所以直接拥有所有 `HTTP2` 的能力,故拥有了分 `stream` 和全双工的能力。 + +- 框架层来说,`StreamObserver` 作为流的接口提供给用户,用于入参和出参提供流式处理。框架在收发 stream data 时进行相应的接口调用, 从而保证流的生命周期完整。 + +## Triple 与应用级注册发现 + +关于 Triple 协议的应用级服务注册和发现和其他语言是一致的,可以通过下列内容了解更多。 + +- [服务发现](https://dubbo.apache.org/zh/docs/concepts/service-discovery/) +- [应用级地址发现迁移指南](https://dubbo.apache.org/zh/docs/migration/migration-service-discovery/) + +## 与 GRPC 互通 + +通过对于协议的介绍,我们知道 `Triple` 协议是基于 `HTTP2` 并兼容 `GRPC`。为了保证和验证与`GRPC`互通能力,Dubbo3 也编写了各种从场景下的测试。详细的可以通过[这里](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-triple/README.MD) 了解更多。 + +## 未来: Everything on Stub + +用过 `Grpc` 的同学应该对 `Stub` 都不陌生。 +Grpc 使用 `compiler` 将编写的 `proto` 文件编译为相关的 protobuf 对象和相关 rpc 接口。默认的会同时生成几种不同的 `stub` + +- blockingStub +- futureStub +- reactorStub +- ... + +`stub` 用一种统一的使用方式帮我们屏蔽了不同调用方式的细节。不过目前 `Dubbo3` 暂时只支持传统定义接口并进行调用的使用方式。 + +在不久的未来,`Triple` 也将实现各种常用的 `Stub`,让用户写一份`proto`文件,通过 `comipler` 可以在任意场景方便的使用,请拭目以待。 \ No newline at end of file diff --git a/content/zh/users/_index.md b/content/zh/users/_index.md index 5a9cfe6dfc93..ee2f7b5005e3 100644 --- a/content/zh/users/_index.md +++ b/content/zh/users/_index.md @@ -1,30 +1,62 @@ + --- -title: 用户 +title: "用户案例" +weight: 3 +description: Dubbo 用户案例分享,涵盖最新 3.0 版本 menu: main: - weight: 70 + weight: 20 --- - -{{% blocks/lead color="info" %}} -谁在使用 Dubbo - -请在 [Wanted: who's using dubbo](https://github.com/apache/dubbo/issues/1012) 上提供信息来帮助 Dubbo 做的更好。 -{{% /blocks/lead %}} - -{{< blocks/section color="white">}} -{{< figure src="/imgs/users_alibaba.png" height="80" width="140">}} -{{< figure src="/imgs/users_icbc.png" height="80" width="140">}} -{{< figure src="/imgs/users_telecom.png" height="80" width="140">}} -{{< figure src="/imgs/users_yinlian.png" height="80" width="140">}} -{{< figure src="/imgs/users_chinalife.png" height="80" width="140">}} -{{< figure src="/imgs/users_wanglian.png" height="80" width="140">}} -{{< figure src="/imgs/users_zhengcaiyun.png" height="80" width="140">}} -{{< figure src="/imgs/users_kaola.png" height="80" width="140">}} -{{< figure src="/imgs/users_didi.png" height="80" width="140">}} -{{< figure src="/imgs/users_qunar.png" height="80" width="140">}} -{{< figure src="/imgs/users_handu.png" height="80" width="140">}} -{{< figure src="/imgs/users_dangdang.png" height="80" width="140">}} -{{< figure src="/imgs/users_weidian.png" height="80" width="140">}} -{{< figure src="/imgs/users_kingdee.png" height="80" width="140">}} -{{< /blocks/section >}} +{{< blocks/cover height="sm" color="primary" >}} +{{< page/header >}} +{{< /blocks/cover >}} + +
+ +
+{{< page/toc collapsed=true placement="inline" >}} +
+ +
+
+ +Apache Dubbo 诞生于阿里巴巴微服务实践之中,在开源之后深受企业用户喜爱并迅速成为国内开源服务框架选型的事实标准产品,用户范围涵盖互联网、金融保险、科技公司、制造业、零售物流等领域的几乎所有头部用户。 + + +{{< cardpane >}} + {{< card header="阿里巴巴的 Dubbo3 落地实践" >}} +阿里巴巴电商核心系统已成功升级到 Dubbo3 版本,用于取代上一代 HSF2 服务框架,2022 年起双 11 核心链路都将跑在 Dubbo3 之上。
+了解更多 + {{< /card >}} + + {{< card header="工商银行 Dubbo3 应用级服务发现实践" >}} + 工商银行为什么要选型 Dubbo3 应用级服务发现架构那?核心原因是 2.x 版本架构在超大规模集群实战上的性能和容量瓶颈。
+ 了解更多 + {{< /card >}} + + {{< card header="小米的 Dubbo3 实践" >}} + 小米对 Dubbo 多语言版本 Java、Golang 都有着广泛的使用。
+ 了解更多 + {{< /card >}} +{{< /cardpane >}} + + {{< cardpane >}} + {{< card header="饿了么全站成功升级 Dubbo3" >}} + 饿了么当前有超过 2000 应用,10 万实例跑在 Dubbo3 之上,通过应用级服务发现与 Triple 协议解决了跨单元的互联互通问题。
+ 了解更多 + {{< /card >}} + {{< card header="平安健康" >}} + 平安健康目前正从 2.x 版本迁移到 Dubbo3
+ 了解更多 + {{< /card >}} + {{< card header="烽火递" >}} + 烽火递的所有新业务都使用 Dubbo3 构建,由于没有 dubbo2 的迁移成本,业务得以很快的稳定上线。
+ 了解更多 + {{< /card >}} + {{< /cardpane >}} + + +
+
+
\ No newline at end of file diff --git a/content/zh/users/alibaba.md b/content/zh/users/alibaba.md new file mode 100644 index 000000000000..983179735c75 --- /dev/null +++ b/content/zh/users/alibaba.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "阿里巴巴" +linkTitle: "阿里巴巴" +weight: 1 +description: Dubbo 用户案例分享,涵盖最新 3.0 版本 +--- \ No newline at end of file diff --git a/content/zh/users/icbc.md b/content/zh/users/icbc.md new file mode 100644 index 000000000000..745974d6c4a7 --- /dev/null +++ b/content/zh/users/icbc.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "阿里巴巴" +linkTitle: "阿里巴巴" +weight: 2 +description: Dubbo 用户案例分享,涵盖最新 3.0 版本 +--- \ No newline at end of file diff --git a/content/zh/users/xiaomi.md b/content/zh/users/xiaomi.md new file mode 100644 index 000000000000..4b67996f0d56 --- /dev/null +++ b/content/zh/users/xiaomi.md @@ -0,0 +1,7 @@ +--- +type: docs +title: "小米" +linkTitle: "小米" +weight: 3 +description: Dubbo 用户案例分享,涵盖最新 3.0 版本 +--- \ No newline at end of file diff --git a/layouts/partials/docs/prog-lang-home.html b/layouts/partials/docs/prog-lang-home.html new file mode 100644 index 000000000000..211934832c15 --- /dev/null +++ b/layouts/partials/docs/prog-lang-home.html @@ -0,0 +1,35 @@ +{{ $Lang := .Params.title | default .Params.language }} + +
+
+
+
+

+ Quick start +

+

+ Run your first {{ replace $Lang " " "-" }} gRPC app in minutes! +

+
+
+
+ + {{ if .GetPage "basics" }} +
+
+
+

+ Basics tutorial +

+

+ Learn about {{ replace $Lang " " "-" }} gRPC basics. +

+
+
+
+ {{ end }} +
+ {{ with .Content }} +
+ {{ . }} + {{ end }} \ No newline at end of file diff --git a/layouts/partials/docs/toc-inline.html b/layouts/partials/docs/toc-inline.html new file mode 100644 index 000000000000..0e2bd3833b4c --- /dev/null +++ b/layouts/partials/docs/toc-inline.html @@ -0,0 +1,14 @@ +{{ if not .Params.notoc }} + {{ with .TableOfContents }} + {{ if ge (len .) 200 }} + +
+ {{ . }} +
+ + {{ end }} + {{ end }} +{{ end }} diff --git a/layouts/shortcodes/docs/content_box.md b/layouts/shortcodes/docs/content_box.md new file mode 100644 index 000000000000..85fd54a2f411 --- /dev/null +++ b/layouts/shortcodes/docs/content_box.md @@ -0,0 +1,27 @@ +{{ $lang := lower ($.Page.Params.language | default $.Page.Params.title) -}} +{{ $src_repo_url := $.Page.Params.src_repo | default (printf "https://github.com/apache/dubbo-%s" $lang) -}} +{{ $src_repo_link := printf "[dubbo-%s repo](%s)" $lang $src_repo_url -}} + +{{ with .Page.Params.content -}} +
+{{ range $list_entry := . }} +{{ range $heading, $items := $list_entry }} +{{ $hd := printf "### %s" (humanize $heading) -}} + +{{ range $items }} +
+
+
+ {{ $item := replace . "$src_repo_url" $src_repo_url }} + {{ $item = replace $item "$src_repo_link" $src_repo_link }} +

+ {{ $item | $.Page.RenderString }} +

+
+
+
+{{ end }} +{{ end }} +{{ end }} +
+{{ end -}} diff --git a/layouts/shortcodes/docs/prog-lang-home-content.md b/layouts/shortcodes/docs/prog-lang-home-content.md new file mode 100644 index 000000000000..77c56456a647 --- /dev/null +++ b/layouts/shortcodes/docs/prog-lang-home-content.md @@ -0,0 +1,25 @@ +{{ $lang := lower ($.Page.Params.language | default $.Page.Params.title) -}} +{{ $src_repo_url := $.Page.Params.src_repo | default (printf "https://github.com/apache/dubbo-%s" $lang) -}} +{{ $src_repo_link := printf "[dubbo-%s repo](%s)" $lang $src_repo_url -}} + +{{ with .Page.Params.content -}} +
+{{ range $list_entry := . }} +{{ range $heading, $items := $list_entry }} +{{ $hd := printf "### %s" (humanize $heading) -}} +
+ +{{ $hd }} + +
    +{{ range $items }} + {{ $item := replace . "$src_repo_url" $src_repo_url }} + {{ $item = replace $item "$src_repo_link" $src_repo_link }} +
  • {{ $item | $.Page.RenderString }}
  • +{{ end }} +
+
+{{ end }} +{{ end }} +
+{{ end -}} diff --git a/layouts/shortcodes/page/header.html b/layouts/shortcodes/page/header.html new file mode 100644 index 000000000000..2d579e836b08 --- /dev/null +++ b/layouts/shortcodes/page/header.html @@ -0,0 +1,13 @@ +{{ $title := $.Page.Params.title -}} +{{ $desc := $.Page.Params.description | markdownify -}} +
+

+ {{- $title -}} +

+ + {{- with $desc -}} +

+ {{- . -}} +

+ {{ end -}} +
\ No newline at end of file diff --git a/layouts/shortcodes/page/page-meta-links.html b/layouts/shortcodes/page/page-meta-links.html new file mode 100644 index 000000000000..5a1d4d3a22e2 --- /dev/null +++ b/layouts/shortcodes/page/page-meta-links.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/layouts/shortcodes/page/toc.html b/layouts/shortcodes/page/toc.html new file mode 100644 index 000000000000..5fe1433eefa5 --- /dev/null +++ b/layouts/shortcodes/page/toc.html @@ -0,0 +1,30 @@ +{{ $collapsed := .Get "collapsed" | default false }} +{{ $placement := .Get "placement" | default "sidebar" }} + +{{ with .Page.TableOfContents }} + {{ if (eq $placement "sidebar") }} +
+
+ {{ else if (eq $placement "inline") }} +
+
+ {{ end }} + {{ if $collapsed }} + +
+ {{ . }} +
+ + {{ else }} +
Contents
+ {{ partial "page-meta-links.html" $.Page }} +
+ {{ . }} +
+ {{ end }} +
+
+{{ end }} \ No newline at end of file diff --git a/static/imgs/v3/concepts/architecture-1.png b/static/imgs/v3/concepts/architecture-1.png new file mode 100644 index 0000000000000000000000000000000000000000..972ed543745154bad2b8941875b7be02cc3c360a GIT binary patch literal 15318 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�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 b-@f&CxL79rq0W8(L#u?StVo5B{`da}joF(4 literal 0 HcmV?d00001 diff --git a/static/imgs/v3/concepts/capacity.png b/static/imgs/v3/concepts/capacity.png new file mode 100644 index 0000000000000000000000000000000000000000..8e081476acd8f0f0bf3a0bbbdda62d2fcf08ccf5 GIT binary patch literal 103042 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

LUU*M_#nRwI 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 ag>&s(A&M-WC3pw^NQ%mRt@xt%^Zx_I!}pT_ literal 0 HcmV?d00001 diff --git a/static/imgs/v3/concepts/rpc.png b/static/imgs/v3/concepts/rpc.png new file mode 100644 index 0000000000000000000000000000000000000000..702b126e25ba2754ddf0d6dbbbf078042230531b GIT binary patch literal 22695 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 literal 0 HcmV?d00001 diff --git a/static/imgs/v3/mesh/dubbo-proxyless.png b/static/imgs/v3/mesh/dubbo-proxyless.png new file mode 100644 index 0000000000000000000000000000000000000000..b33be1cba2b4d29e30641ede64776b87c8dc7a92 GIT binary patch literal 118155 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

  • cPG4DH`b5Nxyg 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
  • Fm}x zy>V+wR>N<@*tXN7-{S9;mo^K9rT66YWll*0y5}UX6T9W<>p1hl*h_EH%RkK0k zw*yH=hKqv9Ie29K5}Ncj{zWIR{vW#DI~=a4T^~+Dq6MQ%)X_x?5e9=8L<=IKMQ=gW z(Ys(UB1#Yvy#$fyb@VnOh~7J+_ukvLCBO5YbDisa`(LxSwV$=tvz~I__h!?saN*;V$%0{bYC9-KhznM29?Fq4U02A5?xg+`Cz87gX=Hc2NHjnXvnPj9Z$#LO-EF9 zoHm^O?!$dC_0k+V&6 z!dP8@mv`np&`)J1m!W++iofgGi&&fk!5iYcgT=+dJHHo%9NCh*fYttFxoVO1r>=$q&UnR>Aoe zy!#P(sLX+HwFi-J|7IaK*(SOBaSeNn>UkN9X1V-jd#c{0w;c10nJF=_su(<7))YH# z!bBSzkpPvWzL9V+m+o=i~t`xLbtlwQ604^%M2Xy;*)>eUfh~EFk7uI0>{Yijv6l_&FmeDs%aY_$c`d zF}>5ycihxU+7XL5i52>bj`&+Q3P287{hjo3V7vYT;3HGNT8=N?Do0$$iDkT^G#|C@ z?oM0DQ*%>Mi-4&&wz-~a7JNVbR|SCpxs$oKELcKzaDnMTtSx2At9Yad(SzJz>IHVm zi;(R%y}S5y0}Ov+E3_&rsms+^n|mLralT-JUfLt6;dV4$#wK2t4I!kM7pqtk?ECbA z|A`MDzt_GjydKJCyP4Dgphf_NLA#h~-?*isD{8+hcQh8v@p9~0X5~4!XXOujcWZ6J5UBi@kpF3d)B8YA9{RuEW@WjwZv9|`uy$rw zBuRvd%i+IjYAZJ_e8&HzF#l?5PmfNyrw^Z$Ew0^=O{w_-%NEn{<~XVS@3C#{e!F z{ib_?22O(ioeBP%n=i{xYN9)8Jz)P;6b=V~-pRKA9l-qSyzuu}C>v~NLyaxKzV7bS zVu!Z&UWr|<5LzDpT^3V^@PwTMBRhyKX&=2L*KDR>5i~hO6jlMlq zd;_l7>ZL-3J>+isAJw;Jp!(ZKTn(|)@{s5Rw&UEJGA963K%w9A>iz$~O9TNX$z)jZ z*zB_RE$y;H16Twd1nO|QoPvlbB-19ZvEqNAuyvew$AOCHYA;n4FPf|r7vA^*hiaav z4f9(h;(x%}U3yJ>>o7!eZlhJUY5*udP8o@H_Aciq(gC9KFfV;=EnN4%(Gx>WEj)A) zwJW4?cW!>Ffy8n#nH0wdI%-DVqwpBhz5ju<`1f1yB9&~Rf1vxi9Y{UjJCtDFcaZU1 z#^jul5$zz}jvf2CGQ2Jwwupnrc>=VV(T9Oja>k?D;oZ(gtAdnKl#@0|_^ zYzXu2U3Jv|Izpd)^3%u)(PE&wG_4%M>1<6he=5tG9-$so6TlvaZwXVFs=pyWj zm}q%lcZ=pv>Q4vr+p99Y{!$PD${vYNig>M(&~*J0{o%37io6hBI zY8kGror&y2S!?Ot2SHU8#hAMcFOh-$R4$ve0lV5?E)BjB-W2g`b-t6$^ZurbSp8lz zF>$D3uYo*fN9FW&^A~Mf5ZOh~);lbIZm(*Yhym(4=z()#c|;7gWOb+2=<{%9lW+lo z@(9)Z(HiOkQRHq3PTMLp6b)IC_O=F4hOO&i)$|19eeFzBaXm^zA^cWMqBxI7Ur|lG ztlxg{(ZVB_vG;qTtYPR0HCKa8yxNyhgB+FngdoIWM0Oqm8VMac;*!8 z{$c5_ud+873D~5_p6Psv=n(IqU15-bdhF~p8|B*&A zs8$T)!gmLu@KyQ;tzEEkX6BGNM+j|O2sj9WO=;71+PuPOKm_ylSzhF*2gW*R;5LZU zM^KE%EKmGr@Pb`9E48)r#UYg|#XZrfX76XIEHBjT7$XJOLUvk%ZjU<`fEM0RPwZ9Y zuEYG~bt`VFVC5_@z1e`t)ugUTH}6Pwr1b=EYLWS#1KZNq6`Pk~6j4)>wPXy_(lSS)!bIxQRf;mRp4ZJ;&y_XxxT9BrQ#QH#=D6Ixz4D2fGu~ zwPA;Y$ea3?Yp9(2BFoPogwcW@$xn=`m@kSgwd5;V9g=s)e{Kx`#fQ(6r6d3h zm%oYHZJ?gi4th!=6o@G^eh+YB?{vE7p0kZNQekmlv}b>kOOmxnHB0XdrZ|L|!5ljE zZ|{C_dNGG(_mgzqM}3VGqS?Vm3Z3=s6O@l3sFva|<2E+m(ebJ4I{ORX^PF zEXkp%__&$i_)DJF9b6AatEpgqZwif+QQ^h>Wr99sG}W$6_lXWCrjemeER7HQd7XT< zKE(Z?A?jX}2m)jgTuqSpXm=J+ZhDBNigUi zAS#O&C7ZNZ5sq(=T9vHyaO5r~a*G5&)#I4A4DpRke64MIgB4o>j+^Jy<6!w=VGIb;7~(kFzDYY1v7)oKM8UlD z@Vr@4X-%{P@t~v{GNhLr0Da|y{=EXuK#)7xGLLYgiR2;?`-b*Y!Jy4VA`KR>FM2T> zAAV%En>t6>_Ji}Lxx3ib3xi5H%kzoI!@N17>B2f;yDk3Gtw@5?^sb_a)5UYE+-9Ss z`G@O+!^YT<@9WVT>T7#v8H01_oWPMwIF1MBv>u?$toQ_gPfHL0Ug|Rf60e+|-~!Wo zQjej(LtBaGLk9#hPjAp-i{SQ?q9y)Tq!RK^6by>j{UrLrAYV&ez)CaK6ND~CQ zhV$oGB~U>ho}$fpWXw>2e~F{fXc=PAu_*zCGzKgmqY&ExFZDg3+z|cU9gXwDP@??5EZViN)8J;^VP@aKsyj1L;0}K>)u@3S-NfJ57A% zog;X7-hKA^Z)DDz+#vS62X%AF49%1KNI&3po81By#5ES{c-cw|C;wVoc*%I zo;--1+^*#a9-MMyU~TUB&8Rci!c73q^}!VwHSIrm=glB2g;SNueut@fmE;6vX~UJT z6JJT-pYQy$NZAu0>UIB*MdbFqhgncLSv@_~4y2*myq^cs*%fVc=khz}ox%y~KooVo ze4`?a}$m!LQ@O^jt|D#?GV}3%E|H785{trqhc|w2-5vvq*{{N4F z*&6cH7F6}uiToe8Du70rZ3-*osh0$hl1sDMH~+Ey|9GYvGKQK;&A_|*@16wc($goOQ-?msFLmk? z4%Zb)ZSi&_4jqKiZCszR6-H<%FW07zM^WD6I97in;r8E0(swG1GT&Z%kL7Zpe!SL% zYNBcTQX78stkO~TZ+2uXZi+T;tZ1F++KZ2H819x2@#cO{%}IP z+*R9_@t+lT8g$~aFP7R>?sPwE&guA<+4{&# z>K4sAGaLQtzhqrUfPcY0_%L>oo1jZn^ExIm47XZBo85V_Mrb_CRi*0vYD|T#Py?UP z(9`W=>;KSPt3j2`G!3?I=-;);mT7s7C?H}^cqIV6&lDSpAct))wOG^JnyF_!J3d!L8L|$BQgT6}K&U5Sg`DIN9s#`*TnAT!kN1b&m zRM+Z@JW9v=1^K#SmH}0$#9_Ak3KbJp5-s*nL%eNa30891y>9yHY^a0j_FETSe>LR5 zKq@Agle~VTtYuDH6EaUT?YFifK=3e<2;dawq zQ=8`gG!k@i;9W(uN=9u;Z3TfC>Hai9U5nlOPHga%=fy(+W)_m&BaxeVfH%GiH0fIT ztCf8+It9yfff?vHpS!-0`;0dQ$ih%X5;c^VkOhCEz#y?A0i zP_nXAb8nghi$m;ToZU-hVVGlS_PxyVhON1Oh^~T6{2*oUy9Mlqe)VAmypU4)kpWQxK zhvF!FO)CX2=^#p}`NZ?uWqyU(4bn!iwmD~3Ko%+A*lITsp|_<46kk1u-cAg0yGP=M z$pSTTnNT6z=&ev3vwP-!Ntr(V>?Ubyh3NZ=5A;|VmA#%j4ibKllb}mo_}p~2=|-x@ zW~+`ilDKvs)a1=Iq~dtWd6=Q%-kjC&QW9?;1Cuf=THU^JL^1xS%iMH0(`F5htzkK0 zDdTTrgloT?&%TyFYPhf=pGp>oRh@sX@hkbO0`|egH&ZClB|r^L9<~)?=1|s1uKH}s z&k0CcYkQb{UCtTbtA8}`1>13E&`{lrX7dSfo>Dz(u>CG=>YgZt+;+UrjQu!>J`T|5 z40W!_m*8-LIjbWhzUeOGe z^Yn)D-=JnKN8&=huS?Bxo`x^8GMpthhJO2GY^RTGZKpYgQQ5#mafV85vJljE1QNsL zJIK}FZO)MZTKb<^M4t@toOjIZenmjAh0z5^+?lE-?jIDl!^*Z>&18)^j0K{F>?x_R z`&bRzT=re;2J}~nQ+>pC6zRY|t&%}k} za#Mf(;10_>c?Jl*Bp<NR#u8 zAe9u4m(4OO#_b84i_04L;ZnN&^&vty{slp(8%|G52w;r4AQX|c~Ex zhQASu0Ag+L@e&_i*7Cu0T01>E$2$!ErS}lMSYB zL~Cn_?-v~P`L?t=BX1jN0xUobk-gGxc0Zl-UtEau_&VunEhv85C7siycm_5q@HZT3 zy?Aabw2mO@=MabH;W204U5X;M{s4;Xl(~DGD3g;|<><2LtwL{qU}z-Uy78Dy2eFU9 zK}&-6qo_FLTIyPnn!U%XmnXAkGvC^|fCotNT3Pbd9$pn?1L=WZx2OcbNvnY7 zgt?8GUTIb~*gTt1J9~cpJox~Y9>qq5EzN6)6m+j!g_#|DZjc$wC$R^Er@bF-VzTf( z6qFRC#Txw zuv-K9!U)mc>Rj#TJG=ySFHD?xqus7}hZ^eLn9;eCUy{T)ynKHWA??0BCZM3dQ}f8$ zg_-uQtv~qJ!|`D+0R-qdLvO3RnsDJtv$fki#t2Ek`n9o_oeK6xwC;0+f#8AV4Ze4` ztNUzpbbe5f?{dfQDy9Rm1q?In(7tEGkH|V>l+Si~H!(%L8asrnL7Mq9!S%$VM*7R= zA@!(b?+28Kef>(ibuQ&-f7ZANa?Wt`JN8^v^SE|+8j_pGo|u}ZG{gQ;PH7Bt=lH(AN$A0^ialG`b&NV%FU+acbW zRT+2ut$IgMnG=H7L~gJ}DdmNUQ>pqw9sB$E&83U?dH@}b1Jj))QvnF4<`zm@z}E@C zUa0JFaLBxKw9G(f-i1ETj!YPDO`_z|#IMsWH6Bprcs}ist718l<-$D1ve<}pwH8L0 z)9+4cx&O{Ay=(kQ>RSs9Fa17BxxDVFZBmxNWVQ3(movLh46eZBLo}$GpFZ;;C{P^^ z6+O+jx{i-=Ji{NC@7Uji&V%`V437dya;Pn#pRY$T`GTUlaN%r=^|C^2) z4~38l7pmc~R}(B}c1vH(M$~CU1WVsd9OO6b?nIXxY3Q0Xjv^V$5 zrp9;ZwG1SIrb%DY>#y#blU>eqdtWd~sy*tgkG1C`cH&~%6dQTEN!k3etn#hx^-F^; zN3;FC$*b^ohU<1eW=u_2>T@6O1=g1>z0(OLWs5g*l~pe;RAJZG+W}Vw`MAN|a%Vw^o zt1t`qdgsxaqVIMYWlRgQ> zJp{K_W>dsVhpO!T?)lDKk=3$WbVSTORImcs1ftRso z8*~9eRJf_vzY3XJj;=3S4V6Z}IE_U8Dg;^_j;VmBfdI62GOBOQjPXUq*&G z&cvq{7G1~e3E8T}waHe|}zM8aI6`smZSVtNuAp1!-|oK9q;=3f#jALtt7s3(<55(!P%*@%8 zBn<8L4NPqAth->+;+yS5Y))k!`Ney!Us-J{Cd^2?@{R_3(#0~BMI z-Eq@-z#KrGzgELy0BXFfe=cSx-T$$EE&iuu!>DgT=BCqS&1wS+x`%E`jqhMC{ya&% zmI8m5B}E$9+5V|NAdOHAx5?{hXKP_&5hvzJ{FW-9-lEz~H$`Br;U?L;7%{uq-u_Ya zrSRIMsHZ&C?chDiB@|CB9syAIuc9r3O}4*qt@Awo?EKO5mr74a)pANqhtIr$1ZNeNovu6M$5 zwwEU5DKVU6b8iHn0P6I?r6H(W&vav{Lm&;1|E{pv)~S(J94uQ>W0*iF{^ z>-vkoGj||YN?aDw$`+d`B>2j3Ex_GYmTgN{;T`qbOONk4)`JN8YU=kLS$rqvzh4~}uZ?WCH zcHtaf0i9X;;uRk_^%Rrw0`|r#rvDkPTpM~aN>9q9HoeNw zd$(-u;b6Ogg@E_1PeX9j=#1a1jb-#0l(tY2zR8oOp0KURLOKj8HP?I(shtkGB{%8I z9dh;|42$PiZk;jBp2#{{HnjI?;x5pT~zD7C*_X|NeJ?X!zVl>bE(3o?ART zBor`O8X5iH#kpb<^13lE2}t~X@ZemyMVDPUjMdw_(w|7*75Bhs|2#;z(~nn2BU%7~ zi?A$`=;PYZPrfi_e=Y+~HXW9(`9PGTqEQ+67CwGE%@-m+?XXw9y?@tV4<+wWrW+BX z2gwrBs+0`V7S=1Iix4eNWhx;d;N6T9MaM*j-9i-DNS;4Red1-#KUtwXpv~tSsRg(z zXWf2yHxK&Iwjlny3L-t~WI6==tEG$k<1slPkWFlN%ZAWdIVkLX?5C6}2<%{_xHP zkri9DDSgms@`^VJVT{Ez$(jhZTVqhgB)qi1)?-;R-xmEzk%X39ys1YB_CBVb+n1TR zTC-=yQ&&FI1>N^@(<V+>3c@%L1uh)_^Y775Oe|wGDVV+y zH;p9K+!j>bWHIRzU&-mC)OfXLoc6_ofew*QJw-*qIB$;S&b6)~i~Q*)U_ZeO|t$J^y-%%X!ta=~a9oN|(e zBgi928G7%}-*$@v#gVQBQCq;fpX;o3sWtpP-i?#z75lp-TmrF$@u~wES77uTweG(% zrJvyM&erCR9buAK(VN@pxf5m+(W;UG{i)-e?I}B6@K&iu(qa0H-{S9zW^iGW@-jFZ z8PSH1@ZiuqE3WNCH6wCNfGlJn&RS_zaLi-ZL0#MlDbJk+9FqYUIJyFYop#31Ro(Oq z(~fKH!?@|UaD3XiQT}$0zS-^FT9Pam8V<9vch~<|Oc|iPCXoNVdHU3`YVEk<1-rxJ zXKELYs1H1n*sZ;*1Oa9z(YQUu*ZWbmm=5M{+uQ>F>0k&B!Zj7sYGQn0d8#H#{DI)J zt-&tGNUJy5DyK`SosTHx(4Nn$Uj+v-JH`4hh@h^h>w{z82kRVX9AnLMIwQgrFBr;} z0}2eq5C^B?3sq!oz5Ih+wTfVSN1Lc%36crEHsy}vK8YG-pO5qeZO@mL&kZHbynKHY zSuG$qgR+*$JZmFuo_I7A;;6d6sUloFIK30{)x9&5SIv~$@I0@PVtiATOPKsTx4}Cl z$W)btmbUt1{W3xNK_i>5l7x&%M2@)Iu2&gQRdDQB??z2K!m~1hATTo<9VO@Q7dXY3 zgO}{KrEgpfFb$&-2X%;q*BuX}16kQw1!d_i%dfwNyg>%KGpa)|n6H)B=}mM9irQV! zEKpmKPRxqQe=8Q&|1gc80N!oL1Li$Xx7uGErtMD72Q{~LLNqGl6VHq_&%Oi`j;-iN5E6KyMi&bubC(iCb<7)a| zNZX>-bOOK%pAeujLqKAAAdMt02gIPh&5`op-5&PwXBC16c#(5gL2P3sw!SYJY*8oU za+B<`*Nj~iO;=kTqMsst;*X4IbB+^ZBbB!*21)MV3sG>r^7q!;8y$WW1EPwoMB3C8 znyjTzr2^ zys`|~PJr~>*(nD$i->fQBBI^*na6h`!_8iu*lBM_bx=Wel!esO@d_Lf_ z-B>rg?I%|bUMNR%OY0NW5}?D6t;T@&uv)%(vwuOK=EHa57!XhR*?k-68G_+OrMZ*( zHkVJE^r|dPFT=tGhwnTjJ#svlYlWAtz$fXcF#eRv}U_?3;2%I{2UU{SIbr6@0w30jWm#^+)69>OP1^8 z&%Tmzq8I&N+8Ck>d8|VPNZAXng?s$wh5j%$-|ize#5~g=e*5{$UrOTanaY8xXRze$bZ<;GQ&$=(pX<51`6Ce z5XA_%n%VU)hvLqGe-X)7&|&Nt0-$=ep+4K32N99&`bVy9<}sVCH3<14Bb-IZVgoYq zzs%G?fw5^}QEZMN=52fUeyI(_UXVKV!Q4%b7u^&)lI(aMo&sUBch?pw&bFajqzt@J zRXLCi_m6G6cG3Qzr!lq{=Uh`@0WPq|diSm+aC^rx6x6o4{Od1?KpN0TtR5;r=~sR)l^1q#?R~yYG9aXGiSJPW6MH%j z$DdgpUgXDv`{p)v^%3l*yX(PdpX1I4FqkNsvp`uJ)bdC-qe6B2gUK;w1R1w|IqUhP zj!A;D5pGNiNsY_tWrVg;xWN>O6&Ijb&}RTJgS?FErS_?3q>H#qQ}G0`64rK$-qL+- zwQ1S`$N2qxpeSQA_?Nm5_fjuA#KP>O##Lfdy%Ejm$n|`~1)y;xs*gY=s|T;pye&Fp zK`$reP#Kj`bOc9D;liLEuksrUNHAvhF}P(ewB? zGpBYdcC(BGv+@$a!tachD1XEOUr=504ywxP#`On+sNR14-OMBJp+H<-`0ID;JbAq^ z-fs1_trh$GV&IHLzy%QQI=$~vJRz*lT*P+*=DnYbYCbHQ=uocWc$>XXW@3#CPFd4@ z9rJ?{BX2sFvc)~Nc_=%0cRQOBBmMG7=Q<9xQW1e@rS1-W9GYR^#Sil#Yj$;>x!COxsCkWdUQpk0kub>+{dapZjh2)x;8@-wYpnJ>Q z(|Zy`eh@$3m9^*JQbn!{ek7p8aDGegemg6Mx0@r)T}80_EO9z$@iNPIYTm7H7Ccr> z@RmrNn_$<9YbHq^91*I6fD~BT;0(hPK$UlZvXm4qmOTlWo*j@L26QEwWEf*ip*pas zU5NgD`nHpf^HrF4pTv*MsbGTLN9U9n*VF~tY>LOf_rtGdCDM0E2Q~pJRZ^;FNx=7q z3&vf&R0;fixZIm0m78V2cjt1kQb`js_zVD?RIDc^IZz)NLQZT80JRRvU((;D!~A7c_dg|6C3E`tenYvyWkVRnhOwG^0Yc-#iJ%% zf>TM%gl>C*M75$j&M>y*T9j~KK^NS;sE1rOt&Ym+aWDXSDnsuEWiu{`Bl0ORN0Y4; z86dfjIk?RSz541F%UMaIXHtr98tb!dbU$#+=`Im3?;+Q_DO5#kz4wf=cqGGJI#F}) z(;80AC62pVA{M1&W)|CBn9)w|u|12w=0GU&hIWTMeF}#@{Z5z_yth9RKXZ=0@jXJX*FHLFm<(l9{dmMd zqndriOKjcY=Y$78vT^!|sv|^hkE=7S?m2?p;f!e0Q%+lZr5Zvij0SxrsLk^NB8RZ~ zwlHhP95<59H=$o>ieiDQvYJ4hnE+68%%=zIV)p1CH1I{ zu449>`Ztd}Gf6Q%k%r0=eC{F&K8gBUvC%Q+@AqX1;0nxO-gHj*Mx(CbAQHEsm90&0tFc?N(Y39_3P*?}_o7`N<%6Ng>%Ya`b2~u{Bfv7UpYb@o=o; z>gZ$DZ=lq2?{sQIYi#f%M?5(H)#4&SXMpkek@+c%1T)aV1LHzliKG-=FK)&MLD*iA zV5AF~w_vq@(t`lKwkZ@(b*F85{Mm+Z$yX+3U#0OK?Ls|182`;oY`-AJF?9}%(AHSD=rG3kEFUkI+ zv+-1YGH>+DX>PWpQ_Kd#SmRntg7hHqb9*)1;!H8Gwp|#*5OK*X z+z3K6J+#5B@Sgib4l7-V`1&Q?(aCIWBlP{%tv*Q?`|QvA?b&XNerLlBjQn;%sM^C= zn@2;*&=wcd2EOKkIL-RsuPZmGd`8YS23f)Ed!Eikj{K^w*SxJo>$Q5DN=X)ZZ@`lc zmwW@msgd3`&9bw+Viu>eif2iPfyV&H+WRrq~oBFpg4ZdnS zi?83o=c&MaxY3 z$@!qn4e{9Tz2Fv|x8rRY5Mw>_jUg22`q@2dc+1RNKO zbxGZptSz+XM~kB!@~W;y{9u)F4s-6KLL9D^nG!7(8;+>bBHlOt@=)M2fAGTD%yP6j zuBllPeqx9WdUff3fg(LDC3g|YLo^gwyO7;pA|WJ0i^Lxfd5XWKgm3S`9-Qdsu~Weh9=9wH zdtZ;`h*L;JJV&2@i9vdu2$8xpg__DIHk>Y72d z>7V5Q+d#(GPGh$DtE{4CYCrUHeKIO6=@92i$R%+ z2~ONC}sJ@`dCD_sytRwG4=E&r$gHfckb5Sj}vcS@z5=#DY z`Yp=+ip@A =_od?j!#F+0aIj9_!8`LKQU-MrT!jMJVU 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 APyhe` literal 0 HcmV?d00001 diff --git a/static/imgs/v3/mesh/dubbo-sidecar.png b/static/imgs/v3/mesh/dubbo-sidecar.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc7b83cd228de9794cd3f34c5e970443b7fb76d GIT binary patch literal 127440 zcmZsCby$>L_pTCBQqocaN=PHp3|-Qqgmi;6j0}zB&@Bx!gn$Z?(p^I{gbEB@Ge{2I zobi3XbAI2s&iP}w=9*_e``ORhd#!cf_gX~jXsHkrJ|?_(?;f$5s^aT=_pnfy&pZ4F zm|p@7YxeHl`*=@HQSOb8*-jf#(lez`001FR)t~1MoPhL~ls0yLCH5uYto*$aL`*pXo2Vi#uMVo#B^=vSp5od zKu^IB4)|SP5q(ia7)&WvzfIx^2Ss}`@rPlFCdO2cxn`3lvR*;Ysk!kw^F!a!)I`!t zyOjf*Zf7&M+Vk_e7Pk@fP$}5DUHkD0{4yZ>$ypeJMhvhEP%fAuxXN-1lX8<-p68-f z*~@ecb!05$k9?FvU(7t&ZeI1LG4!<9Hg>GYTkwtXx}ZRF?=caA^o;hxHA9Nso6mcD zeSeUa@BMFQ!@A1Nqhc3kPMNDc@N?>uVU4q3rpJ=&QHNbGTSo7Gu)9nQM9!1u*!w+d z#$&;m8Z%W94wGq>1>t?#iapqMAG}EsIzW49iDkI@E|p7cDV!+@PDQ_Ef_T?glvQ@W zkm&;3H{m~2^5+S6*$|F%X$YX;c^s?csF`#oY0eDc=ka@Y5g**WQf2W|;u2C4AsIZ` z9Cs9kc=Yvla@cOQ zJ96ND<>qdbSiu@{Q5iE_AG#_*FwX*pGTag@PeFcKzY}&c-=`aqKfcXWoeSbRpl$%~ z?vhqHGo*bM9X_~8t~Tw7R_M)_nXRvD(5_i0$`0`D(BRKr9seex7^pzM6ieCu?NnfBKStd`C@B;gEB@1oWj5VwZ4?n9g zvheMZ=9Ks+EnMrYkA1?fc<+8*!T@)boEt0*B2cr&S%_wPnc{k3mnAxVA!Ip-)Vdmj zlp_~4$1~2x0~Y!mHLC{jwL$F~8T%Qt9`ASJD0f^hHluGyUYga0jwfwbN`MY>3at@O zHyApX7KAiw89%%l@={=;AMV^8P9WzR>B*Hjnjo#7*`}YR^r~Gh5eqD$8rs_rc>O2k znRQOlCzbtT6w#_+OJPWKg?^H!aVrytinEvWO;Jvvt;fQ!wW*{)HSfE}PR7N0kn{46 z(AK5t^v0m@q}n5Yc9<>GEu1trFG2uGd+hB4_u_!ge9_6{A;w}}*6D9LpmvfouZulA zr_{6=ojNA|aX&vSbDHEa{V2VvT7I46*7<>t3lg1t)0Ikx+!($p&6QmjXDH;K9F)*p z6S44=c2koX{wjX+gwD<_z zjKKO`R8oQ4gK>H*Tzv0Sui6Og=GbWeT(gjCtp@1TIrvcc!fhY>PEWA=dmvj(x2}m+ znuMs=$u_~2i(1_#LExY?*dRE_t3mFH&W+j1XG0LCcSDA9=!rS+z8~)5(J)1M6`6=} ze=B?88tMtImLfK@4_M?&S1QK8r=G%1J?^v4M<_}7!xyw1Q-A_%*s;x~MT~`v0iDs+ zawr+AnD3E3NiQ{j=k)Cs#C~pMbiIHV1ELZ4BOgigS%y=?IBf3Dy;lpXxvVoo;}X}K z!foP?<6RSof7YK51xnt4hQXVw$)oE(-Q(G4i-UE{r9>XEdn;mp1HFbo6U#cVrujM8 zrlSELdAn>v1#|fKGXjX_T<|V&78#ET`Uw-Xm7$11;SNnm_?gRKqa)soOnt;;T%JpN z&{-jl2+rE4HVusLYm5_klyU&5YqZmQT~^lE#?M$M_VSAKfO}OmWNXUx;s)*a=hlQs z+=|Ji4wNIs0N?XELAf1b_WBHMh|_3)sU0b!DoseQ6x-m@y+ipKQJ-NNVmGZ#v$eKT zbclO~V%=0k9Bm5#7T;z~24UvuzpBbqog@upWkmkiVP@7C$Ik62>Chct&Zba>@-vCB z(r!Fhjm4?Mw(hU%F_%g6=+i~0#*s4BhV@GPVaUv^30>edSAK+2vhg5kW&%POO^1Bs z!RPP9+QS~?<~GXb-m|HzhE$V(Z2Wl7L~79wHkLknzmYGSnN+L}yWevnS+ZiLI`GkQ zW)<36L9N@z%HVXy2S7foN`pTgSy4@!Cq$!zK8rZ#Yn9RT5M<7I+h{Q-pURa$@UV#d zk7Qv)rff>}^Q&Iix#m?CaV0sal$LuydikOz4X=Lq(~TT4fRu=H%c@^Sf9hpQquZs5 zJ4=Mfu5f5+$ffB!{E3eJ|k8Eg}e^18lQQqN5+p4?;s}z$>aCi-C6I8S%z2pi#E_500qPB)Y~Yg>Z0e;&%6^CksL`h*~k~{tM4ho%|#_Eb)Xm z^0`|4z)9s0*N2_#*{YKOH3WGf?~S#wTGS*#MtGwI)^;Lsq;Wl$6+<`vWW+0;OHMv_ zcj?b^_vk#Fhc5S0f&NA-_a3{IInpGMXRIupyAq{tgXO|kA~{QiY4^x|65=`9zFgJ{ zU&Y40{7MS)Qy%G~FkRmq5>;ZeXw-hvT&BIe^=fibk}ow{?u<>q!Ad)>ZOg0HG>(+b zb)?C9(khwB+_{D6retspca0LO$`p(7q)6LAZRKfrDDIc0uv{hzW+tF7$$)j(IFKcp z=ypBfK_Z1tx=VU05Uot}V33i+D_0~-_!~hs1*(;Cmct}bLw!7}m@&VtG=Y$Ohl=$RnO==b=HJo4*01$-+*bw14K@^umUCELN%H>^Gl_fjUQmR?d@Pw?6L^ zTYGw7WHea=qDPiN{_UzCG@YM?B-l&7@b!Q!7vf?C{^TA}Qr8V0Aqa8ByPNUOHrx#< zC4scv&&>Za5podv#5GY?^sf5uAmO!4oi6k!Tn@$#;B$q9JK#C&IXF}F57Ul4`_|K& zjTW<6A+YAuu79@X!`X|^?{AqJ;N9>RtaLN7_9>B+2)K3Q5xkaaBC~^gLcmv*Dtt^F zE;yOvibrvBut51{nR{3PL33uHDyi z)WX|w_$F;R;ms|1C7k29@0})93UHdYnAdz?=C9@fAM&iBLr-}E7>JO zJs}mF26NkR!HwzM+g0-qb?LrZw5F+Q?>7poFoD@mrO-Q3spL#ShkH?n#~sVXr*gl^ zmSyIiNk7nJrU%^8(lqL;`R6y6v zvhXYOA1qBhmY3nM3eph1!8J-qg)9xz`%8$=x$SI;<5f7nP^#aS7Q9z~f;!jwb>@X5 zWWjEpW}S}8oAJY%tIz@gD>(eiREA6PQLfrB;c=aYXK5Qth->}$P1f|M@)=fz_d;vb zwqjTXP-(#Cldo(|?uBI36Xw@h{F~*nFv1YlV3845>&bFf3L^JyN_s6A)(|U1zBtz_ zKty9VgW+nkO`Z%DV8C*eBi5k`ZQ00VENyJnMZvk%eZYnv2ycPS; zV(CmDH7JK*HbLMl?Vi2w!vo0b0jxp!=35HE6d@4*%stTBfe-4;j7#J}PH|p^KBn7$ z=SRZExQ6R7zE!Pu4EETeoGjj|UcoQMumo3xxNr`@O_rZ}PNvhIKW+euzs>orJa(4U;lPpL>(*Ta=Z(gAUWk*U8hc*r zL9d>;h;H2-^C`D8ib1&Qo=zmSCy-9fh=>J03IW(jt(TQLhK!9Q1lD#E;`mJ*uCoV& zw4lQ-gS#KfWknfV`S{^R_o%M>`D>L+YjOOJSopFAi!2bT+=m$RXS4bar6M}nM`Q&$Gq8s?yXAHo8lr<_!vPBpkM>kUY{- z+h`FaP>~+a&jZj4aqTDIqvl-5UxG5zvt7*wXyHmQbt2CL*xKFESSw8ZVI(E>^L5<7 zC?W{ydE#JBA16}Jyvvy3j4IJ({o)anj^3CPKGmc}2sB@_mUtC1#{H;3QwfW4W9kO| z8!*T2KU5(rN<(z*E~vkggtf4sEWLRa0^_WJvJSE2;Wmpy=3Pc}C#l_o4cP0KJLWdJJWvg7k~8pAKS;LoaEqTGB2O&16CMZn-i_o#dEg2=Ed`W|n=j~D>WVNHUk}-HWnLN#OTZh> zt=SXFwlIn!>Vx9pd)zk-$$$KS_F=DaDVDTihYkC+CEaQYv$`fcCX<{ZY;0Fx&AWzI z5OpXM&g=`vCIHr30>3}ST9|(zDvx&~kCg`_z2wevpLh9b+1z$a`-GR+RMosLRA@I= zPIQ-$ezn`9tpw7jJsE2_#m@G6%Ex{Eq8&b(o57<@A%|_LaT1^X$yCUep8YbMX(TO> z>I{3yQ4NtI#$JAdo&rb$6d{>jLnb^Hw;ubA1KuME7lF_lUUHO=Y8r0yRf|NLnU_;X zs^9=to;7@9rue487Lya7fE$Yo3=-JhH;RYkFZ4f+NUgeu^o`oaGJ=+pu8S&66Oc;a z_+{bcDVP{STWp^q<77*MUP!_hqGwjdM{!7z_Yxek{s0Sr68)w!+_lR z-SZM7@xETUL%}Xy?Zfg|uzhwRl~lE*Y@;UocOR-=^l7f_*#f_vzjNoTKO_ZLLfgT! zRanjdIz_13vz46(}vv|*c($U#LF6EjC(1UFsZ!fEO4m~ubEGu8_u60{$wYZJa7ws=3CW7`bzT&hkXQWSh(GdrlCwwaR!u&mCm<50Yl&1E}9 z&~iea{wUNTM>$vCy^&?B~-Szbu z9sg_@a5`LFvmT?^bPKpa*TMm62Eh*LuXk!b%Dz)2l}O#Tnq2VVrH0Imnrwe4De4K{unadC?*CZbqe z6ySqa&5@5W9!X1sYR2)-t(v7R8^A2RqX>!7zEq6!Q*k_zO2cO)$g?ZuG?n=(@zjDxM>YMS%&R>zPy9HOjXwK@^YQu^^SJP%PfJ)M zwilLeb+}p|OLpj)YpJT^!T9EY5Xoz)pb@D$o00Nl0C2?Bt2%M1%Bym@b;}_n!B4>V zGYj7ri?NI|b#bTz-1>EoB4L;jJoz?@WPPL^b~1J7%eJ+hu&_k^`J&XQ3&&;U^wW=0 zf=vjMu4SYA@E7?}fG??~Bt{jh%%zxfjsDpPCs5HO2)Y)t1we8(dbS zK$$dZ&tQbRHR?^&*T48RO==l(p5jx;&A99>XHqEOrR}Je zRWW}#!905;N`=Y{R*Rb)jvi^D8W`hnD!O_f%MC|Ea3yfgR~0|zc$Z+2*_LUd(XU)8 z96ER*m0s*NIWa^}DI~hbzh`2o% z=6#=4oL{xBldU+YJfB3{>8=yEO5F(rP54fuvGUk`{I@wzA~E8K?K_o8lH8JhKM6q3{wQT|9{Vnt~ed1YqX^7%Vw(U1}e&a8O(r>c7t zR-H*wU3%q4(TA2ZPTD(Fn^61{>y02QCWDQMB?ta`+9YGK1}6npr_6JWNafDxm^a>N z?sh2@s6k|DYsBnbbdC|tJb&zG@9cOF6`>sYUaTizx2x<0QHt)GpW)T;g_1Y)77bA@5 zotg9EkTjFmImHXK51WRD*t}YU-7R>PsLi1{A682$I~xxa^{#!%I-Nyxv>F&)WhZDZ zZ3krNG7&jiFL-Ju?Y1nqkr=67DxMPOsT2kWrX*e~Sj=^6 zpMp_fp_)upZNSu`&Ta|}42yIQGhP&Jx&=yUD957BO;lg?aB6pdrI=(M&D;>nFGpfn&J_x8snJ>gwIWU!U^)uNw-YX58j~eEw;p z9RFloph?ehF1PLA?4``Gi_Xk$Ccy{Gg0aGx{;=U#4bqb^JM)xhvZ=cYv|a;DGDl%V zQSABZUgP)ABD8sl=bmFR6KBMD)SPZp|~z$IDcVG%3GS@vY%|^>2SN z|2ZER4``K-Pdx6{^+`RqB;7XEMz9AP{{-D;QlSE1Y#3h9{9$nLCokgZZgJ8DvmGn$ z)Mak;!l^=b{#@=RLggeDoVp|abTX%elBkB*wW$P^5AL&mHV6uD3~;rE5BwH%~hLE1S=Tj%%6^2kLk61ZAg`oc>${~-mNW|FPU|-9v_WOeVyG>(obhS zRO*$ioh?ywI}WzpGC8jyQwFUTjM56$?g z3w$jicly|7YEla{d{t7;CWBh13OEYXRbyJVLu4&F^apNxK~ahsPrOU{d=yLbliV@T~!?wgJbeyMSv>W{)MI zQN25xOA`jrk+_H7j7XMbg9z}Oh5rj%Zlrv>>Bacl9fuI6!hFxoJ{-Dm+1->6HXQrM zG08(ee4v3Zq`6*uba6N(-KzZ=GgGy#{F7tjSD4|?S$scv%h!4vB3|@K(1ztw^)z;; zl;u}O2kk9WHOhA!r*0UjAOpO%_@c?p1^Q5O)~tIw~$ycwfv zrf9?5lmi^!b+!5;%dC|z)|Xnw0~goEZN^-Gj(qOxX~0S8MIp@#x80cqqAmV&-&a`! z&P@@&chDDHK``=-J<#9jo+An0Lb)lu=rN;Tuu8#ejQ<%eqDh4!?3-8CCIZ@h(}q0% zg!xsRrVPupAQOS|2;s{}RuA{>N)h~b>SzC%+6)RdYcRx2`e~@-%fDkW(vcapu z0dz{s-QQ`x>;UTzg;*ox1x0mLpyMwY)tP_>;ZE~S|ExyHRM1TEpKH(Hl1dHKKZ-aj z;zWA;sF0_l1#I-8?V(2h%s*T6D%@a$i!x?R<&*5b;+SIQ3q8`yBwrTYJ5-6o#)M?i z-&$UdqCuNgLtv^y9%0(wM?V~WPHy(8REJZ7`2&t0C-JsK>%w@2XZ~?oWmLq)!5oEJ zZsj|#R;sPq|zzWo><~ zU7!O5e8p*QdCnrEG6-2X)P#fe^TGYBV+`^Ev*+pfp^S8Iv81O-{@Dj3@c+4|gaQSx zfq!SX-_0r=9`wuuPzQJ?B0|LosOEia(iV1mmRS6*nQBs@@O)x*g*fXW-DWq|LjN}! z=Q6&*UH*DZHDy=U7q>>t_uE$JirT&lXQ89QWeR(1og>RnMz6Q^mD$j?)mr{op(~eD z={<1Q39EMcndG^U0W`}+ds49A^kLg`UTxTmk>QG}NSWZxcKMvp+>e8_>()sLYC+g; zx1s>9GePOSEyF6vnn#TT=WPNyC)a7O-cEsudC!}Mq+@8hEoR(wY>fuG(Xf$IV!9eX z;3^3o5w{t0x{zdsjmIefs9VRq=LhQH*q7U@1kap^?0ZITpK4JtvCYUxfE<^a%6B+0 z088_0&UoH=hf{}5&OD}?arIn3`rxZng#5&XDz1dH>ht!3@k>~}vN{Z;ctg=r`0N|# z9b$F3J#_E181RRFR(5Qc&Fm!G!DcsFQSs~YT}^a_c>L>M-+gUE`HE%j*G86*FbEPx z^DvMtC+eJsg9b!DDj<$RcbN&aCP#7C)ZD(0nU1uUM_IEMtWG`ww&*T zIaAD^T4)@2bouM#no-WQ3~I^bB326 z+>PA>WSJzNj-|$Of{Z_)m`Iy~m3E`>D6%cd@|$I$?-gak z({}7jc$UMz^#;Ux&tr--7F6k@BE%IX^UoA%o9a%aw3`s)t!1h~-nf3;Znk>QKjn?G z!DJXNC@pLwaaRM3oN4i*5QQN>B?m1EodrS?hz4hTP7_@UE;WrGUMY(jlcNX$Xa3Sd z0Ij)4^o|+~!-=Z^K?=1waH{?^vO)9(HDjvA;>?@6Rn)@7A;G-gg4)eg#yp`nrm*70 z5>Iq}GEJH}0H>}a2<$FMpE8yGA;eEr@VqkbfThv372R)g!K%g4SjXE3uLnLjXRzaV z7V8%NzK0yUl#B>i&rve6gOsVC*t-S;r%A~Z;2z#$RY^!(ql0q$P0*mX3Bt|%&AR>$ zA@Dbf>(fw^8U(8?j?4(V`XDS>${8gllu6bft6UlyC42f+HcBC&(9A@*(?6kJjU2TY zamWU*?N4VX`IE{ByrKXZ!8wK#=cst;L>r_ueeYoXdH6aqzlTAABN08{EkFbw$&vL3 zee@k3q+TQU|qI4^An!`%&BSu-Lpj>PVIdzw~ zjNkK|Y1BQVBRw*{HF?SZbJPiojE@I+djYwd9o6UD6fPbheKvW)kWLaYy_enM$^)N8 zcl!GJze(IPiQJER3C4%1%b42Z%?UQzi4p*t&Ivj99|?J3<^4@LC_clmO{FLF<0Uy- z2&P$!wn7tX`Pjais~kIrhjkAtsuyP(d9f*42EL8i#?u~iz$@^z`9v9wKZ2<2J)kSWHsxSxQR<~N%Ucy0oy@CRPq3&!_Fwfk;OrJp%^%RG9 zZpk=BBjQVT#-GiLNJ?ad4=Ytn(}Uls%<{!Y;yZIVVvI9($BCF^0X$1^G^L(>I<0^u zU6QMpnMq2vh-Dft1_b%LGG@^Nn=o=dp%SR8RMvjHkP-Nlj6IP8+8Y-4GH7D?4zA z5V$p13eLqFRY0j`sPBrXea%@kYs=ZQ889B_f!BJV2HkRZ-;An%I0{f8q&dmwZl%wHf7ajEXIaYO!P$KCN~SCsap_7V~s9Rf#CB9GSMw%RDm=Z z>gnqWS$lHM-dY-VgaRb0iufzXcYkyvdC_aAea7c<6<;a$E*1`6F8JtS!Zz1@guMDH z!6@uJGoh^LE{f|tQDvv%ZtuBxw^Zl*HnYLL^sN?W#MjNw&AtGZHsl|11E6FvN81*9 z3|aNVC7QP(fo*0I?J}{-HdXcN(a-!%W-ho~4N@NJm0xFC<#-L{%w;)j@HEyS_R!_@ z!}$fJqQzpiQNZ!Pf8RNMRRfnl{II47gzKAlPto*oeV50QT^}{Af(d7pdj^({pQnA# zG-r-Wk2;SuDxmrvsox)7>MdMExk7D)P4_GI`_nK>8KHjqzzBGTz;O6#=xF#&CZ6Jy z_c9hgMm60XaVvdq_B97dwATaD^rxhX@n&1kM0(BqAV^u4V;05A+mE{>L-l*xH7J}C zl7=w(V-WyfsXKq?Aq`2O1-^Sv-r|Zlpu0*<(9fWnD#WX?woB|)rNZIF|1lK>1#%a%|(io&^)-sX?70NPzV$w zS??<$%XrhMz|ewlj7)WmQhyXS78pVFym(lxVkh(8b0f$6@a-|Hyzc}ia>rIt`?10U z7wJBUWV=v)?h}bOf2Fu(73fVtfA{AvF5_Av1U4=7QXhf-p7q2h;j(xu#AE$I9ogNQ zK2#Yx+t3~X`Fr%0?uS;DE5b|+G-Ud;?*wyRwtsNEdK!)11XQa(C&|gEMo-4RR=p}? zUJvxNhLc~Zu86E^JvdXF7{b;hvy?(nkTk3S>&DBSQ#HPN8A84qESU4fX+~^6w9<%TLj}n?UB{H0w zi2hJ!l|}Ch#4LP)S$*-e%$JOUpD^?Zdvb28=}*|L_VD^I{&4Y2>|x%=dxfzP$E$;N zzI=qh@WxKB?~7hamG|?#?DQN;W%*ep8Pj1RRLyk5Tqz5$6eSKHu-Ct4>NX+VEkCFa z`^TnoKjd=ghH)qcx(O|LeLhM06P}9#(gkz#zmT)DM1cjW`6Q)&93Q-Yo^Lebbs^tl z6lRl_Dshn4>lP3aTE9FNir@!?i%%`!QKOpn=Q$j5V3)?v{uJM;CXk**JAZ3+SdLCO z`p5MNBMOgnr6F`Vu~a4pHLTWp+2orkRob!O(XhhS*F=c7y}1h7J^t;^K(GG$E3V*w z@&@@E`ns$MDqY{lmDOC>J)AwF*`jGp#1s}&P4O)K|aG!H(67q8JbI9(i_+slV=X!?evSS~-Ds66JzSg#~6~4;;<#?%3&#OigD3pQY7bRvA zQD~t149=_4Kvq$KUJqa1ed9vmVg|6C|ml2>JXNP+42PrByAS(w6?ylzF%DP`O7eoCR>4j z#2!OXPyUif=Kt?i%rkn&{*J}ujxH#df9!uO6!Kk17;Kg~{tEYU2k`;#$4;Hwa`4wu4yp*3tx^R$XJyMP>SKS zS8W0iqmVs9&!8*j4qN_kIcrr95-nu(8_vzgCfY&x$%{k}G-r*=|3M0&?P|hOOn#^W z)LF-!G|N{Ch|Kpd+hM*vz!Oy$Br20V>#-uegkN$7N;mi-Oz^|{@p(-sW4t@t z$SMfGp+n3B9EKLH{ss^26=nl7Ut3{u@vOD6Jjo)`;PajdgO1CeDE{D!*sEH)H9g2> z+K*E6iy?#g9R{<5P#T?C+u`&kqZlJu+-7EKqT{z023e6p4zKIVkR}tmN%_YmF#rEC zWa&X7sN-sZZbH8o2{>z-_-SCidka+730b5|g8kq+?herBgtH8h|LTUIWiU2oZg-k1 zD$}W>XMqL$_bjAb@C+WKM-}EB`C?L14AN_K;{!9DsoH?q8{gr7XRG}?n@D1>n4(z3 z5Bzc8nh{nHff1qDON+8X8uFpo|FR<2wF2RBV&IN-Rx3xo4{P&sC{bbH*#by;wHg08 zToU^^{3b~X^*YXa;plckb3&Hh?9J1N7($>@rL5lsM)>$tQTz9lU|#Nd$XMfgin=o$ zzJ1WZk@3={{_)~jdxp<%Q_$>TqRefhX2)i>6Xz%uN^}88Azki2sm7rMIq&80^w@h1 zWg~U*S=ctCXKChgK7@NSQXI+f(|SsU*#vF@dZ!)|HpA6g6mk} zD{AId79qT6Va(SbCpYpwaA8Wd>*P7@mOT$%rD6qISUj;d=(;#Us&rh8y6uP4(X*6){T|Pf$@<7IJsk%<56+*RNM?=d;@fi5;O^xm0fDc z%tdqLJ2Z4*F)H$0|Ee(NQqG?`UG(2aog&;yYt9z^-HvzcWpXB21{$1aAGKkYnm{6b ztYgfy=%+-BIRnaAjAovP>6Oxg?|y8aI~-)1(p57Jg?-oI%*KZFT;KCaP5W@bC~*-G zi&304O|O~ou0_XZZJ0iS@@wTDXMh?+_4t^C5xw<}cYXfsle_jyFD;L}_KP#cHvm)< zE*a9w_Y;UwOw7I-N&SIBrvCqAkM<`KR+U9>L-_i{x;5tOvhaaY&X?<{+OH*kNyr>@ zVskMT(rBT5Mj#X$p#v_!gB)c) z+}0^x_R8E*wo;(}K1aq&yGVNy@0>s#?;3rnTv`}`X)M0zq0bfPa#Occca$tB*geXn zFfQ7lK&Toks3RFfy!i&)?3qMD57IGR)DB@gYK~x8XTJ zMQX7!5#7qkyLnf}ApaMiDUm-BYqu-0cpXgnLUxIGrPX32?g8gB539l4d6{;e$vXeU zWZb;**LBa~29;)c6wU28ms$4tjz`}+Zv9?yQ=ojv(uNb_<8>eZa)g0=5T?1ow-@!- zeKh7fcHECY=8g}P!0uQEuKMd$h+rW2ozmc4dv(RrdA*j5hG#E6JCzpS{E2<}f>l@f zSH9|)+hB-cxxtN@CzH00cj2pa<2RVZ2BayMQ^(P*)sT8j*6nQmNOxygR$n*9ge%R3 z6D$yE3$a)DPNYSeBa&%G);YBn19DpC#=RNgIy2w%@84UKoLrgJa-|VfBXlYt`n+zX z#=tz(VExR+jr`kRZqp7uGK$g1xf~T;(x_hGtQ`kjDF$CoK{yvOWHZpaGB;b=TooG7 zC0}G-JfBZJf8PpTT=7L)iT`G&FKH<;>)I{Tm z`%YPVB-Gtvgfy?fq+NDE?I^QJZBE_MP!T942m@JE@Wm%r$lN|N+A$u7hW-V1dsvVj z7962i-bZCcdJ`$=X))?q`lXR@&0Z^?t@C;Z3oASi4`&9hHu>|>^w$=XXEq%k!(K+7 z!xVQbj#)ATG%u9Pn-O0XK$1249wzR?Ajf%9LoFH;>N8SPVfa2)9H~dL!__(`1H@&T zqa=>-*tH9Y#mK^DXc;lwx^22&<7=oEF;&_8=Xp*7m7Z9jL`Z@mFevlri+14iXn&{% z)S6Cdkpu>Hn{S*b2|W2`KAM)n!smJ(TS#XZ!h}d^XTkSa6E_!V;a3h* ztPeAYGj>y;zOrf;K8jD5v64E_Q?ro^?lXv~XG7R8J^k}o?jNqsQY;Hj4q14j+v;b} z)9yVh*2!A5o<}L+!4R$GifzsbsI%Z+ zZjR;@aer?N^%)aKDkAD#Vlg3ACa1%FMV=cPf=T(qB5pP|PcK(UgH=sx?cz20AEEFw zeprho&v!*%9>gRV3Z!Ey8-Ouoxo=#Qb|E@ZwthfP54 zu92w1SU%QDiBjvP0W-Yr)V~^<@)}w%Bq1r~i(&4*ko>&Fl>2NJcyu4BOKu`CBb$CYoGZ&fkw05K+ES=~aI39Mk)x zl|d==8y&j`vPf;g!#F9QaKm)E{Y_fww}x{Eh?BeAp*{>NttZfslRrnGd*)`rdEf)} zXWo??=9ffR9|KlG0gjjQA<^~l2)TLw>+7k|bD7V5H)#uCVKcKmga0y~9~HTl=eYEl zO%Rc`IMJpMHNi0};3#W6F6gutGV3yIOC5WD`MYB=9rwxJg3w5*^sb}#?x`7gC!?ej zDX?=dqB02_@Z8!wPByCH1d9gN9MaBvBwty~IcAJ#)a(AA_}Ge+-XA1*N9L4B zN=!U~kX)!Dk2ZE~dR7Y%H=_p5+GZ$~=6OF3M1IoeYde13vOvV&3jc1Lkm>ESBz~IP z)qOXuryc+6yQ!1)GN}aWiJt-&|I|R|Sz_?@+5`)jL4NVWcc+Es59<{Mv>0sc4KR)% zORtzHWNXkJ@qN!7w_u3rTj*y>*k>i*?-^^U|1`Dy5{&6u&k)H(-Vad8BpP}Gkj@K^ zX~-i-rTmRF?ie8S8ODM$3-1i#i2#YHaSOS&57OY16*AoAKXze*XT-5rqloLGi7R)o z>mvAHH;&y_^FEzlI0OuQ$aE{pf7r*Y$6Uct@XZe8IAH*hZ*QjdfJAy6@ zoQ>tUAanZnq{!z^wMEL@q7x?;Kuf#q8LQPWrr5oXyP%f63t(s1m+yET4-L0AzPIcR zMf)gaR=eR!>=^L0$AfYi$y4jtC1S0b%Txf3Lo@W|Y5AT$eFa%8?O*nKH)~8`!F<@{ z{0(f3q#A!i2;P(48MhW$?R4_>V>`!i4n;vG!XK2U@k()nfal||>Lqz~IR-Os|-)m@iFj{o4oRvNo^DjO|P=%}r?^c&U|d6^>v~Sz+XXCmXCQWf4!^@=>5Re{ml!%fYaV z0B0R>^Bf0hr8FER+9n}mE)5-2Ebsndd=d60UYeAjq{S|V&6Ot*i@z2wtQ-f-?#;8Qy4GHD;wLimn8Yv$ErnJztj%+z=S{{1A(ht^(V`ILF+ z&vx7mYq6Zy_z63wG|_jwg$8_Hwm)*$;#S#cOl#o81AL%QWh|yY_mnux<$(8>NfeQy z-8?gUuWbV_Qk|i8MssSiQDYhmNd8a|6rK})3@y_3>7y?8@YjrrZOZgWjOQxF3e>V( zXy%ky32%*x@CjUrwYYAUyldXB*^>U4bd*Joiauu&5=2ebSC6{Af(~C`VKF-8)yift z1=PEl*o_eYm14LnTZOW7b4NUoV|f!`nQKcw=!0REQEJ=m{`3Op1;f4_vPozY(ob)M z;P~li27U!Ay>jn6z1APc35m`ER&@OJ&_g4^?G2VfMa6e(dDBfkz2!Zz-pexKLNZm3 z@xIE2zpX!E8k@PUEuJISLNt8fED3?rGK^nHFi@`K_`LVdqjj<|z^j4f%9sk}MR<4W zrdxNWSIe5-x6Q&IUFbEowRgRA^Q-uTOnwZL!N^xr08g{WUcQEU(Z)F?+9`0sKZE88 z_j-RF*_&~g#`uhRxl|G!lPGZyzh3#-V_+Wm<7$dd4%g5A$v>M>GVp+qg=iO(ZDy;S z;z{XQVmNj`i_+IUNDC7643e%L=z;eDnfoP-hIZuL zwEQ@$1ovH@R6GB+8F!l!t*$)wWMzP?OU`jHTiFWcGrrhrfBRRWpd?;C9M&VhI)aq0 zo``}@m4gyl)&m|OKH5^##P1fKjG2aL)kiFHHcRv2U2VhKUsQP6!o!iomD0&l>n5TI ze?B54_KNyHyWf=QnDuOVbQ%Pb%av zB_!sCk0|!-8ma_h?Gs<#XYw+mVcf392&*yEOoV8`6`t!t)??BIN7Zh#^Fn!CKbqy5 z;kc*;wRc+wHw&b__JNeN`p7t|-x482=UDN|d!dL9Z) zbJ}Wq!2!g2#8`hTWY*>bwz*3g>+g{IETZs6#!3Mgo~7&-8dH_fSo^oaKuL_1zo}rZ z&u7*m-JwEu*v3?acg@O5XU%6&);)aq1b(I5(C=G0GF#L3G zqY!BRs}MNQPVp+kcP|L}F^%YoLW@oz4bk1H`C@DXTe4dSluYhRCHWaVBWHg41?L|x znDC#m0G#EyXZ-;OhrF8!HDvpHr=71cLOq@S<#TvX5mg9@_GKt8k+XQvsDOUgitv9J zyxH;l^^lN|I2|11IE49TQ?#QRVG$D4c^^-a-*-@5$QDgq?=>#{I~~`(`eQ?T6GCZL zPfPss=TFvt_eJO3!)*0*LcZ^a{43(!ParYSx4X;hwK?#a|pz75$?BUP*l^-q_b-ascP`TRg z=+Klz_fqU)^n|deh^6KaPMcIC{`+kp;MHz}-Yn+Z@Amg}p#j6`yc0P~*PAG~0_l$p z!Dy~-jKoxf_*X5%bu^$Nj4;P^I2Z6>5E9(MT>dn-G;}5yA42bxK=h!Nwu^)tJ}OkN zcUth;s9mExgAn-T5^dA*xUS0KEmU1FL4ZQkg&m8LH0RJMw))pa2p*H_7TLn}R~t+# z>d7zT-THBfZJw4qQ2S9{ns+~){Y)KcAWETrCtupX5GjQePfHNKiwF0jvnxUvxPNI!uM=yqi@h1kHPZc_m-IRB|jTDiYbPN4O5B zsodoLwlM^=MmWLKhMLCyL&gKK|Jvk0JohAyzC?Z@sjqpv&6UOouKhdxRR{?Q^Ea$E zM$Gdn&Dtb5fPnTR#_|mPe zMs{#!%O%U{Jx2=tOQWCQ`lW8maZ~2uc#~O;h;m^NiAF#42-y>P7&>#a$OTja7(R!a zMhZAMk93-WDN#A1ZVBHTR|!|ek+~Euh7T<64?L+JRky=A0B3n2eA7cbm0xA24nu}N%Ts=zL|p-K6uk} z>z79bU9T~#1m7M(iemYiqgwyuCTy_!Gq(0xss2^xKm5nBkwOTWszEQ~0u)X-!x2Vy z{vx}tqZ(qS6rMVd2@hFI-Fjo8O>s(CZBgSGnpXt5*|GOVcxLsnw%?vqHrU>e@^5%} z4Rn~Vg-2qyZVDw?S?9U+Ib&|u%x5LSd%?4}uIFm38u^g= zwlv@GeVzC2J9_M3xE$UD{VU%rOJOlqV5NF|LsTrxk;~aT7#GS%k-Yu)pLKg%LEoQ`ONk)`_|^gq8jP#siD5+g<$F z`hHE@-&;Ic&B*O`&i?1U+V5*yO)V-$6q zU(19?I;#w*h>P<7#n)HIMb&j}(+wpc%U|DJ z4`TDkwD*49a&*AWTpXqWBv)6$^Y?HSx(En3*FnsWT&^Cyl%LnV2T(GMgBhNFMhE8T z10E|wn(o=0|I`)mY#8uX@_kMH?COZi?*jK+YR!mqx+p^PBn7Nb95P zPAcVb>$ErC@co92S_Xv@EO$}pe{8GJb*7N&E(t3zYL9&(2guY}y!?z9f{q8Ex3Wtr z3y#@fj}9&i3Y$qv+`n%d%t^#MOby)B8bgxO%j{9T#4F!nz7xvvu)BQoCDy-)8{yZ_ zyDdH`QdV?aD!Xo6+-{zDA-wD3g9q< zes^9KTYgbDFSdW5#QD>}=W7riwNjAkIHWy@3EON`aq=50};kI}N z`(ATxm9_Q4@`=~giPRFx(*5RRXeMjFewlkD;dbPu2Yu`t0A0Dl=wy&OzM;m->$ukD_2MWtMB<(}Yw96QbWJOf0hAV}-mK}J%UCa=&#}8kDu@$b^T@l*eHs1VVXP;u+001}#kNKPS2O<4lTTHy znzPe+&L4ih_@&0r33ExqXeM6|?b~CKFGp(&J-l3Zc}lsMRdE>N5W1CS7@Feu=r31k zQ5DZzV%oh`wY&_*5FS;}PT^<$aje&J=MnxSo2jnp;dc{gzxQ@@Q;eDHwK(o^O}O~> zSic^K=#p*}$E1&BQmi&nb=lU==r7g!!LLIj5z@+5c%8lS0ri{VOxv}2P3{b&5mR)G zWqv&_b2wCX5psQceH!5kt5nRGlAM_OCa7DX`(GwELCy(x8(SjZwGOkWzz?t(yH6%$3hQ>ggS+ zBANg|k8g&7&L%suji7)S`}M=?&gHeo(~5xb=}>ps-TxUVTX3hUW-LI%zdI zHD@Ze)S=JiCoX_`VcPe8Y_UeCXPo70h|^5otvP#4_5r!tku}<$g>J;v?gQ3t*#KA; zG|?ckMy}7cU{z?4vl$@34Ykw|w;evO5ik>O9>F;!$#(}!uwBMK2(oR_v?tz|Vwy2qMFzmhjIdcvB_4g2Jg zJ&t`SU``=O+&2h#s^39P-eV@r=DeE~JP>yn$tappvqLBabb@-VtDV9*l%Lq(Gk=tG zI**Dd|515prSA_L+jvfjKRC~Uf&(P|BB9gZy9@9Pg&$iAs|21%(oSyS=4P03%Dr8y zXW=6c!2wMlvL~y^np8R!2O0+cI8tLU)3_wtstu8!z}l0&{=W1gk|XLvUqOSR`*>x5 z1-6MWxu%rnv-%2t!!>Wi1&+qoK&X6(N&#IYkclwKV6w-!eo4B2c`)B@jp1`k?YTIp z^p@&pxbU!YsN?%$#6+m+^Y(m<=79WHElXO3&%&O3sbbL0sbMMl1s4@1<@|@pPds7*KP?tvn1gFo4B}90wYL=98iV6Tzek zIeJhHj|((Yhf{R3Z%hPv_f6j{0A!aETpLWuizooB1nT=Rm)b|!pTtky(CF(a)@T*l zO8M}%$xOYnxT6QXQ{*DV6V3TFqbb7k$cR0L8!RB{ITZP)i$1lBkKmsomo}^^tZs=B z(pA!t*d|HgO&TSn(7*$?4NS>md5mHzt|yHd1J+{UZ5s`n*}eJMnq5-)%zSueKoNM+ z&RPba7sA}k+)mO7O|@QYDrE^a73TL!k;DEGQx0Y8KUzAyMwHzbUJg65FP7Uz7~6g! zrc$GXf^rk?TT-ueTKp`=&Qxx=%aRIk2)i5wm~)mP*mix+iqAUceEaVI@M#X9QF4WlOf8vb{yih;j_Z81nt^68L>WC{5aba z?Q@F6pPtnU=9~OW3L0o|Aa5JYr)aFb-+O()KI5|7uh z2mP|-xRdsTc~EY6$W4ZipPrST1A+?9_>z|q9P?~Pg6NIl;IKG?P`J{@p^mPY!h|2I{gm%4lGVk`bZ!gj4UzDjvuHR*bKL3+tdl z0E-3*qmgK|_4MZN52?OPv>Y=`Iqp&53_eJ@Q8>rtubEMbRQC(E;M8|tJzE(k3a+>IIgO_9} zx8(buPQ-)^&urdvwNH-i&i zQ>LA%Hru3w>4#scAXc}zO(gpiG?Z3_SL2M|g&a0W*XWWurYP+>kAhvWi!^1VeD;hD zre9@4H;*{7V`Z)*ZX*5nYqSr8UAH=v%wc&qAUB85Kq5X3o|86IgR0mflGKu zcTTWR$X@(?A|1TNJ3V;vGoY7YmmO2mx4#fY+v$GmHuv=B74gE@kwURJ21`@j4M~=Q zgzZ`hE(ituXc3#C9*Y1OzsW>^Y5scjGIoRV22Rt;C;nB>K-i9nrwa%oxooH@#jpoQpj9K4`v!N0ncEEl*1F*?i=6 zc!m?xF0zWf%rKi-LT|EtSp3G?dOrqV#tJKY=MLsvvC~$tRLCWoJ4P3olA-sTi@AI{ zPndtsR3v?>?UKQEW0GQ;G9?_-tH%8&4s8Ctd0w1Q&+FiL;()sIZz@ZMS%UeT?@Mjs zSms(&VJ5z-R1hyS26XmBK+QK2o-IxE%Cc7}J+I}NH6%dDHsEz@(jlc7KdbFSlnl#< z5H`hnZH8oxSMsyZKHJ~@jzN^Y?h%xo=Y%f7C|Lx>o(~~UO~~f=gk*fS zBJ~G6=5?oJD4^2u2-vtvzIXSNJtyYTBWn9U(Hox-r;HjF_LE0~DF@!jc5OmAxxcdG zt4(G_MQP*pKpG2=m<^YRCjY=#P(q|ve~t?(s`F$+n-Md)>8YOrPxU*i8{Rz6xe&+- z0K$JW#3c%K)h^>@iHO}T_3wD2)&dfsQ@*oL!`QJ4*4F0!k?d7F89KJ{(G{L8GFk&; z)IZ%~C$wElvR076+EiKvZ4J zTB|cdk$qm)Z$pC`FBwOl=Ig_#?C$q)&bk@zl%cmQ2po2AMj~j2OqvwmUDOVk%z!7< z%HFRfk;$;gKi6LdLwG=LZ%DHM*EexUZB4!y3{bZoVqPCsz4Hh8##n0d?e7yny zK-mB+XILPwNOSz`$vFLgWkbL?gZyrBY5%$^QDLY#p$>Y&fQ$tHf?Ol*{X^s+FhU8{o*`-2>yv7fY6<}8-6K~-wj00<9{XX z(M|8w5dj^UV~2+0;J?$>q24e5&o3@ZpZlDa`|zP3{rlZek2n<(yG0_T|Nbv_&p18M zl8lIiVlnEsL7NKzTSg!~z<-8Gg$Ce`io!B7TA#&Sv#L=KE=p}l7D0uK7JFF$UL>44 zA3U`!)Oz3UoIfao7f>etWtOzDRU)Tv@H@V6LBe3)k-?5>@@w;hX!=4AJ~9Pq;aAyX zl8QME3i{puudnL8jdTx>)1B1>NFqkrV?YwXkssb=$Y)R&4<9Sw1w1Mx()eYx!i63# zl}L)$G)8o{gw)PH9?>&S8~oUvJa_u11>VO2^=@t^-BegSqx;^2Lx`fte}0(VAv6PjHSI_AcB>>tQx`0uaA@MpgHzDTbPD>i2#I3F@w{$@T+9W@Y{GQOO{VR_7+L=It zvawA)BhJN{2B#aNpuzUfKH~iWTV8Imf}8Fu9JeKF6yT&^B%-p?FL#SFg#(@)Jm|BZ z6I5QF{7L9UXDi=o^9P$OxG+{UB5wpo0r8T$N(Y`w@Usdlzk6+~X6cC3Ji_RV&~@dZ*1#EI|lBIm-iVbNzRUyQLn9- zWcV8IVh{jvFS=a_v4}7C&x1YF2`-0HsPg;yV@TEj{Yf5?Y`ZzV9*uFP>NI5Plt>B3 zeQa~=ttYd^`bh<05*#m!k7dawOQTJ>KCfD^E~F%>`r$7**i)&~ZnE(aA{n~7vnY<8 z7ILU+vM;ddf8;bhvDpF<;1&zskSbyvMZ|y?wz_!RF*ii)CSKE2r!~9UJ@^@C6mN$g z=~d7xcotaD!7p?vlsm)Dhhl+#FEp;1S;0L~ppvFR+Pi?o1ZU#*{Fc<&h{25YLC{Pm&1uth_TzBHy(+zy$X#F$37RKy|-t#C8PlbU3qp#-)=P z(;urk!kE5JiG{N{yL_1{B7&A=3F1qqEh|%XG##Nm^ zLjeq+Q4(b90A6{;b4CQMofBxjusksnczB^qJXodrghm1bFA$dTr2!Crcs>@ax1>|3 z0u4uka4dIMwYtBy#9gPbEITpY5Y5R%?W)W-W*^Vn#c$nrF2v{ZH-4(*rl$lZUYF7d zKC!HRod(vOlKs8u%y5`>$)mNDBCE`JAz*RCqGE0cbNj$53BopGbClbSu!?GtcRe*z zB`enFIHY00r-41cr^Hk_8M-!OS7V3VD&|LNec6$436f33O~2*KoD20jl0bmsWg!cN^Ks3T0R)`uVRkxx|fN@_?6E7Q_+ zmCcj^Zt}jE%%MK%diLcy12^PK2zf9$go^rC%SG7UxA;+dlP)9GwaTXaWJt_hoQPI3 z{+D7~ufg&Bf~oTa$yCEb3g|G4qG5!MY@BqsjwrKCBd03kpvwH~QbU2R&^?!8++)Fy zvf}pA(Dw6|7amgVS(*a&TQlFfy3wS0v$Yx@wf*5~Gb=^^;kuS=*8pvK>?jQdv(3|d zK2Xk7KJs_yydDnC`^Y`n+D*p4rQ!!P1CmrQ(PfjhpLj1^eoc&}Ny6}6=E@56z3r>G zEenBf^Z|f6YT{ZEGUrCXU&4xnxghRo@skwSGbNs+55k2|@ z0F80cOh#RKAUm@qR?KKdT5~@QQULLW8Yy&-Yg>U%zea!rs`}fVes?C41v(WW#I*DP za-iJBL%B&9b+AkocXC7_1#?PT&l4`Cdy?G@(oD@XL}4BQbydz=^7IcN64EC|M0d&O znlE!QoHK+sTF|67p7IsRkcmb8bRK41=aZPdXA(SGG6PhyPfx{=18WKa*zSPLJ0nPy z{5*17<~*kbZ80^1Ks`&kx_;~vx0UZn0%zGub*L27skX{vs531CwD%e(w8)N;4{>t$ z^7DF4-py-Fzb?}WI0?@Bk5HK)ae{3FFVII~s$;@FFK*iQL~#byl!IDkzUMN6)wjQi znH0%UX*5KZYv!u7qlw-9>v`dwflTq4&oYRW+P*MW9V(MO)$49t`4H%Hr5Wgg0`4Sw zHn5I$tknOva_&D%*b5I-zx&nlyVRIaCU)jTm|Pk@g(?SzkgqCeFsyNeFv~oe5uW5MC@x0GhxIY{rPHC%|<`ufjT4~Zxn%W2NN2t(kldpl+2cW#?VQO{~BoJ#*o51Zl7kTB;|8rPPZ3V7!$H#5lp}qTMUjA5sdu2 zP1E08%i_`hBN#>u1uDolPerGUIjy8`Hys0SSYaIarw|5EdhFWu%hDy~-Kvw;E^d?o zVZO1u2Y>2n{}CeWQ$jV`PjHQTvfO#hGv;QTpe(JsJ!sOhh~Wo{QEwp0;vk>Nn&?J1Kgw}U{cGl4z9r7css+MY8;FgHs6gwKc$+*3B!nv$ zFeXDj$r{%{5s&pMh;9_Cb<+Gtq^D;tQtI!tWu8u2|#aQaAxg^+f)U+COv)`bS+A+A>Fqe*7sPpWpJ9?rA4c zMD^j&ZEwDS&G#{IoY(6Y)Hu5LUH!`Le(k@EOkWNsgBB6$F;4A$aD7RjBRd@mE|$p^ zOHWUuTxG$teuUOmp!w%CN!V`rGwR36IqEHO5%7@=CCvbdW%$Lacj-S{gx{S+jPRPjL;x!WKk~l*4CjZ%?gWKJyi?Mu^%yI4tT?94W&@rKNOHiJ zkf8Jk!XraG=^6{wEA-rj^V@Tq-p^IxsFdd)K1H9LdNW%MfciA9~BXmZJXr4=qV81Gh%?K^`p|@@dJ` zLE3Qzf@{E-loG`wIND5BQSo}5f(1>4%N3(Q+z=yj*_wRr(@W9pS`nZn2Uom_O>@YC z?Q-Se223~X<+Ra~shLJ5|3Npg%_rrA^G~U1$n6|%XJ*04ae<2$|NL5%U^ik8i_du)XGB_ZKjwVl<3F3(X$2{8iO(a{F{EYo+ommZ= zIHv)d%J01!FSi6;5&?%EAt&v5io9FoJkoM8>=*w#{WKE z7JdmW%5bT^0+=}7fKW#*0CBVII?3M*Wyu%~0Eo%-neM&R;~jVN9m;^x2IgcKC40L{ z)U@2xx)#m7kgbfkTSjQ^Oa1zipBIi#^+bivUFkHnp_T3WYAoo0vJTLq$mld_${jFz z-d&%K(=K23LI=4kVi|&3#Zmb=n70ClJ|&j99o<_Sl|@LWE1xLjdfJz}8`yDUy!HQ*I&g81`-k zYWKE-|1omN7*HtpGBdkfqe|}c>3T;nZtB0UY`@2gxClac8y#fA0*deMkSv?)0gi0C zwu3hnG4(EsH1vS#>Qi9r?eWmQD~chk+IlE2Sxb^j<$MJJ@P3Qx-F<7F2a?#vktAz4 zJTQaMwK?XZ4F>+?b-TX|cRq#Yzi;k&bR#Az`o#1qt>boc91yiQPXaQDh3q%*mFc=+`W&U>h8RuW29gikmHOa#YoqS>SouvEpZvH?Gk!~2{#=4 z>_}Ne2K;tgR`yOlE#!j22`^JB`vvaNUs7rGY>>i#@|R{jP+|4o1O(*bzyKt~UE?x0 zdx|$v&ptf-3@C`|wUP7!y*cTN1L;3doPqRy*Lk9ecI~EeSW+I(+Uc!PvUjo>?cKHT=XvZczRV3E9yD zIV458=RnDU#A4688dwp#zw`TTeCE*|EtKv9z*x`c#sCqRSo2=>P-)*nIQg6`!13bT zzgb4KYbrLXkyt@-I-lIH2-xIyzIJX{WDiUX2f z;p?(rhRxu|R5ypeG`iF)oA0@1OP^Nw{Vah#mRi#W!rUS$7-P41-xo`3s!0DO!nLgy z`qqlqdeDul(m{3858JxPG9M~%c0QiX;rj6AADQAds2Ii6tvg}}jAaH00F`8~$KN|i z#ES$ion+Zg66cR@GDpT7)>VKDe3$kej?MJ|V3gMmKPq zPyqx89YycZ3u-4k01`y1@+DE2djtOioc&p`SbY)en6kwChfJP9pMAGJ)i12|#5hZ) z3WJVh5ht>u2sVD-s5p~|72dfl{O<3bP3VN;OwF+Nn*|n${s`TY%RH*Ke)y9E#)_l( zLRJX^b)9oOD@KuU0Ix$ zWf`J~Ffy%#7q;~mfRkH`o`wco5~CA;1*u)^?qM#s#g0S zcX8^avAUMS2II*HN{V>9rq`C73h&pYP=g zm(H==L`7{Pw2}<3UG^%9R^U4qUQgSe20k_KgK1J zI+QydGJ`(?j?svP(s*>KU4ZGRvO}v)QKN_?Qk~37wf@AZVCV^Iifq0yBuEFOM+R^S9K$CDYXKa;;!u}dtLJecmq-C4(_V-Og`Fv12nR1^7$}pwxmDLU?h9Y zq3m~z2W<>ma2EN#ccjKSx0ZULSuT{kMOX9kU$<8K2RP6R&Kl;Wc5#Dljb%-_NQ!2i zf^M6T1c}-1bGs{@mOKg-SP??KuOTsx!-&5@#7?+zfl&OtkO9!lLV@ymXc3@CC$?P1 zJC#F=h4+bKga93fua=roG6;l1h^VlHhy!IJ?>4|AQ<-=t8m8Yw2Q62kH;Ma-?GU?r z{{Q+kJY^1+?ip=Nza_o`C#{@N<$AJZ=5E7}Ep!HLLa&6YnQd)jwyXhw;O9e@JGW=j zfCMBN@YJDrr}F2dEl-Ah;unwG4MFT%cMXLG7dhPMWwaRO4r?@ z$?u$5JNT>p$wT?#gzq?CUR?51wA0ku09kDS$8Q$jn|>d^+spO$?_HXA*fW9vIkif1 za&kAITfP&euaMXR4bVUKQUa9fE}*-!>G^6>>yG5^P+`79QPJ9yFZuC#*l#QA_l=g0 zQ=!|kh$f>SXchA1bL>nP@0$YeuAy$ z2ikW!d={?vM7<|5ZSC}>iD@+kOLQiO;up_L{Q935&Y5cd1#;duWw>)-&2EV?u?0pX z*Y0=I0>G7&{XxOBNT8?L`-Y{NX^eHDN|zH=WfI$ph#AUV!-&@$87fyj@!Ss}>rW}) zyToF>pQRuGUjFn7(A=H|y|yT!*l~c|u9vxj9d}4yY=8D7{BU*P-QhUtb+BL`#BF9~ z{6vZu4#W~m@S>l~;KNWxp!!-XY5IOI)%>7gIS{Fg=XDVHHa~xfNxstE9cC29bWst> z?>{?~w|sZteK6Kc=#iZfLIFh$W5O?I%Qv!k3_vk}8jz=R;kX@mdQ#@}NXRDHR{ew6 z0wWoWaV#ILm)%&mhf$fQj0~v@Wuqv45zaDBw}Y0%v`uW2X29EAHvD{~zU|fm^f%#) zO53j#|I;1-Fd~vt_|v0IXU<`!3f}Ec*x61K5#fs!Lr?ONNRWtGM!K$!4K0&^ty`i$ zD-&pK$6-0kSQ0?~Jamf+8ME)Vw_o<~v%f!jM?h;A^{}9-!MNSyqZ#8KoFVIC8i=fG z8Fm5ZtER5v&VW&FjQKdKwvwX8;dm>-E zGp@q;CdT^Q)yXNg_1K`=5y%|scl7l1kd~^hNLNq|CS1jwuaXjd64@6j2cw;{Q~m@9 zz6;YB&gAbYhINWhgi0bi?H|~lKh?XdG6`SRat%KyKv+fS@Fif=o+%QqhoCyYqQrm0 z^%2-xT)PTEZbn$sd-0UNyHW@PaRC51+0S3eI#J7#NBj0lGIz7!T3z%t%tbtzo$^rq z;_v+lLy~=B)lxerDuhEj667Tf^SEXJGlL>(9zEn1w0KSeVz2z(jCil(}ZsSjnhx(Y;F$< z`N3ZodjW;-XD;miyA~iW$OQl@$ zD3$NLuuG#S)*N;88x#);L5E9%73E4k}I-N{$E;^pt|6lv=c z-8;ev^ioKP zr&0htZY26vlz?}-h51MdmC5O;8BZ&dR-ByJj?2{(s7!#W1&BNR!^WT=JFWF@V!Sd( z>*i&4s-Wo`AH5A_y~uS-XD|yyFc)$W5><>lLAOpvD;_Pd+%J#APWD+Zg0ynwM>?d7Y_C=3k$Rim1zVLbiGeSOYI#`5S0ALjXfPAFmCa+zsc`os!qpSKjsFNK5U{f1?i$<+_rG9ert^WVX5 z8%9JhxgpZjUmkYaJfjo8M}~W9;3HwWUu3^W%||+ni8?EMoI@)(#~OzsibZv%5TFow zVuEpAnenkFv#3m=Kuojz{k~LqBWwJpXZvou4}uAan%^}7T|;RAhb$c|L#FK?Q}CjZ zJ-MZzSBTKaW-r}s!tJv!&mc2ICw~2h=e&k0Gv7xhrV0B8ox5QJ07YEr+wQwfAlaeK z<@9OPwSeDV)yJM%T%4WFf?B_q;rIw74?D6*L&IX7G8oo=ik?!$2eS>_&7p?T&Bi@fIvm1T>dPSj<`>Zzp zFNu3odgyArY+BmdUtmLkb8|3N}oQ)aiv^G0wMe7E^TL1!>$|XWp@059E`-^F50C8va>~;y%GRG z?qvLHtMo4^vxNE=f2w2vENeK{Kmx@vs>(Xr7|r2^6kiZYY7#Lqo65Xc0bn%?m@8r`%Vn$n2e_xW zdgW}v?Vy0@mriQa&cd|e7jz|!U*udV4~fm*{Csi#UFSAvud1(qX!I_)kbqkD@;i~! zb&+O9H;{#i+~*6m#t(iOh}~Z|QHABl{S7}HDH^zWj02CO6(hBlx~X@b-*er7GZ$c% zbXHjrvudGP$pE9xmK=$=<&o5t!^+wv{m2U@r$k8?q`BfjX-nKiZzS0=Nhr~ zCJ(aa(#0T}I#WF*jpUmm%Hf!Q2^ib1{nOH?Xq&*K4D15UiNmk)6SQ7UiS* zW5hMO^*s+n$GbtA^&veOQXG~6L`Q6+GB-|-4{S+U9_wvv*Em!?KJnPJGWs;9VYx2R z9d4$OQulSI6NPT=QD)hLU>BDN`*)(ZD)iCGHC-1h6HllqL;kh&it zo8y?EQCUR5JR_B|$wokvQXgsw0^uS4Oi_&cG+Hn(;P{HdF;h_Pf=$+bc7 z%2Sx-2M!2_-zR#W4YJ?7rsxwh|MCQHD+CCfKGW|!<=c=jjH5E#%kf&-RP#yHbLdaT zk3vB+(PEGw^2@ot>L7)S4RCY>$f-yjMb7FeSlWyEncRj5+U zQEZ)j6ce@Y-M}kBMEf?HgPFlKA?sle*%$Y@+tm$B#?b3L7PHG%kukWPpwx zdh@0?bJKHfbdey#b0Dp)JX7n(R<7!mWSZ|ff_(!lW||=*CFL~PLS0>v0W{t4U0=)Y z6>`AfXy-3@?5=k2TKLHTmBa!6$r|H*O(KSroyfa-?IRAs!%HpK0VT8UZv4-~;jvs) z0B3m}c$LTk$3!w&ULfV>4m?A+wZM-3?6)2v0M*pf*R+JnWPBe-Vr!lYa-yL?ot$>; zPA7TXKgyPyN_T)89KZekk(YSGNO$NG9#Colmc(KsfKitgw?@u4xHecHrD|!VDHs){ zk+^RoZaNT-L6d_px7BZG^x}XV)5Dr|H4_0u0`$CQ?F&Um9`Sq|p?MtH0gnm?&ih4{ zfcS684smzU;48M|lRvWivOmxn?#bla=JC_E#nVZFU=g@!yH_(v3?567-q@5H8QOKYSiH>1fRuNQCzDCQFYgL*#C?4p%{!gpL-+cy( z0lYNhH8NMNz-?s7RMx;@&VMEx%<`N3yZ?EtATo7qT=m#9)2AG(+xBT!j9lh&%`<%@ ze8z+flWBwtt8srP7wHO#s`?LGqH?<>60*mVs8BHjOczoEXkJFI@=P`7oL9L-xPzjeR?Ddc?-Y^U=I!3h z+Vl&(1M!Y}1au))uyatcy}Q4YMa>mkBr>@;31eWEBc(KDEwc(X4Ex^CEr%c7Lm2Ap zjDa84B}$Bs`ebi#M*QvTw!L%_qR?+lRvR-aKB1WYVrXhmm%Gscv#kL=!;6=T884Q( zxz{ehZ%V2kX}1UY9$VS8%)P-VXB&ij5xi3D8t-Wiw4e9hchGN%hu5{s8mV+gWW84z z;OUuv-)9SQRJ|l-zUUAN86~RvDyf3fO-e9F%35f$FBv^eY8?a)or1XCR+gL1otw1G z{VdN9%6tdcz-hMKx8PWxf&X7$MhFYr5ftO^UKctq*OdTQxSqC$j-9vuCBvZhi;izR zhgV(~c?ln0opWu)&?&d(|8ZTO^hVYb%EjVwnLY#?O)Z=1Q{YL+D}X1lw}a4VQB9qx z&$m131AjcNn1o~#@R6R`@T&{ksVAg()$MYcs;AsdLUKet^Of$V)q|(Yfj>gcigRU$ zgrVo*B4GO%rTL%bzW?j>_T3v&InK?0laxojS6WRIcJzI+15!nz0d<@E_t?`ZRf z_0gf=2HtjZ_kdop1l`Xf(#Ix@%V_`>*sCCCZOHC<(1Z)coO`XkHi+((+s-@qvOZg^ zv*nfZ*F_3~>l%Gn&*%^~+FE-`6n=S;vg@>cvy*SOo_~DI8=p*HMG+7>csUD&)e`RO zoTBQhlOwNXN;4;ueZ#8p&`#)dbj?2JBeiFEW35=)-|=i zPqe-42y#3d{)t^;TP|%ZeY5re6*gwtuD3C-V`!cQGiUxH+~3v}Rg2o!Vq3mwRw{o& z)6=A;4lHJaG!bXit#*TmT!}LRP3H}a+Az3SonSb0B$yO+tIc^>NCF#6)U$JFYk4gE zjd|Qi%bUB2dKfTOUi5d4A9MT_;+Nru^akNioGSPhi9PrVT5p05Rz`pAoe0o&Fb+tH znjS41R%;_Ja-_$k-d}tr3#SARV2zMb*->wQz6Pc(we?wQ*;c^UV9wpcM>G>w>rS+S zuW<DNH${ILt!)jVc%x-3zseaI7P?MS5n#h+Z`q^#pdY(XS{j;os&dn-CDV*bRw?<2OkdWO#WtB~ zLrV|)^!6P7>iu>Ts%~5V+wliG>@I(0YbsCFw|t!(5~A_YHSdk`w{ z#OX$G@GTjK73ANsNa&MZ%Q+z+oY7wEa&=U@{mXnSd|c<$ssfW3)n823?$Fe-vvWdI zEMrU#=ED6GEcs82fxOb(hM=Z`d&LAHC%y zXYZ2VbqUPVxa<{$>bWbQgvBuR%uqkY&8`)c_jEfSTvS$Ns-t1;Wc`j(d*$=)dr0%x z0Epn~BfC>037<3R4pu0(R84vcjXC;{mEv2qS9JHtmyRcBOKQ?wdT)b888-}yts<^N z{9ZTWkfnPTQFT&vE)g*4E_cTZ1kB_30ZaX1`4Kp^PRMLJ@MpD(wopO+2<5)-^o9Iv z!W<%C|HJT>X?P2E+pNO%z|9wV#`qtrWFTWEA*Nq!WWn#=t)rwYb}?ek>e7XNE0RNPH8J@9p^!F?P{5$bf?K7DY`-{yE>503U|QjewvbEen&5 z+JnDOzBpJ7VMGpk{Rq2!1326Jy;sFU`G}0E8Q`qzyP;X0amG%PuHrhNdDtO`wQ>5% z(R}(#=f#`c{ZFMIBc@Cv9VQ+QouDUe`r=C7hB~vbEPbq}Ffi zLnuMyyX8ZiRo)6m+3;;$87DM)XMX%x6Q&jYD0?3Z*XLm9{RO(gG=G`@-^bhfO=;Hy zsE+U3PnL#s4*O#_axLr_(lvVg`6Q>n`Iln6PTR6C7ecre-dn8l*Xx*eVtJ0)npeBE zXAhhz-`sF($&YG6?30OVXh>g^f_xeJ?aC^JLR^XrS34ZalGzz^nxc3ncrNuBr+d1w zstX#iKcTxfV6@pofTMKM8!$;K2Xrbx=Kve0*>#fk_ZjfB1(>1vRaG+3Y~o)er*AMa zX+W@PpcVFMqd4$yFTh?UT;QoAX6WT2a#j2+-}`Mn($jp4wU8&18I4fH>+9;AD16mz z9YJ>z76uJtCk{_?4P#<2A$NP=lnHkX$gY!;-v$qK7g`x<&pxeAk@l+jd$qW%t*!52U7|7WC12%G#+iZ>2ouYiA?NqzJkt}NQPQ<& zc#<{O9d8rtdcB^TS;o;Le2O;E5Ls=>`)fkKOM$94|%CJ9Y&f`A8y*a^4@n%<3 zCX`O3!O+RhPJHQbUxhJ35t59iMB?p^i`~K2@bwPUmr)a<*O!w?POGqNE|~lKY#8Gf z@!F-Sltd9s<&yfS(cfM%b8whN`t2zzh+HWnNV_v=ZKCWoHp1=fbkkUGL0@AJ%UN4b zGT&s=bX~wPn8z-s3cGQ-o<}|*5$loaPUjT84G89bk0$lLT$QC_x)hbu+9tV-gxpTt zPrb3_Xb!{q)}yQ1a{u z*nAsXJ)P!Dq?cjujSgeFJ)k+5BCbd7{<(7I1wuQY zBwyu__KSTDaDv->Wtr5HvvY`+RVYdtWeV5mt5MDf6)x~vjj|Qw(YH&zcXrXjy6>j= ze|wMgVzXoLy(nMl5Qcf)`Iq~SMj+n22V_>4xJiCLoiV>d8+q%_DFO{s$W&5rG!AuJ@&YO z=F8<)*E?On54Tn{-|0rZP0QNHvQLh+cu;S2^S$-tuhA)me1;DK0u}c*=;u5gX7aPO zbkO$PAD5_3Xy$7&3t-OCbOKJr$E>tVJ)u&((6%#V7W-NHUA{2ye0h*&hhEG zCUt2)iPPS7OF26Sy-@0CY!AGf1}w>S@Q6C(4i=bE!`q+X?s`MBScvIZ4#E~pZ#`sy z?Y-g&RF7*Vuq49ws1)F+zPMW6?TQmI~Q^Re_+Y5AO@r=0dW#b+G02lL+^l#nw;o zaAKtCl2DTf-#ej~p|dbWh|ZL!jE?_&eVkOUeAsU zp)*fm?;zKOzYQ>dp+29m@7%tV|3j<#ee(9$j(-a@3pVz7Y~C2w5N-#NpQ!5kZd5Lv zL~&*?{+=F7Zqytt^rDQ}2}r{(>cEPPw4x#ko@NS)6r~3-LqzAsRXWiA0z%ORdNjPvX zo*lzEYvf&jyNNhC#V+@7{&@ldf(*x4|K5@m7dlNWLKJQ*Q)N7RiZhAoDKu4Gf5KTo8g8&Rd70;G+d#PPOT6;d>E4?jzzNLb| zRgZP7bx}wDphlJAMQSEa9lO`IJDzKU4(Ydn0&^Z!4D&WciJ83g+@wB#r~tobe{V96Y8mXu=TGtop&T^Cx!Tp<7R{K^xyD^q{xwpDkm{B5oxxm=h-VaAq1)bJNUl0&*Q0RoZFyf8R z1)s)!mqff^sEKeJTl6Eip{wj(D4sSppo(XdD)W3C8B z%`Q|cG5Lr6@vQg_=nz4jm8~;lvvQd0dJ`rbh}cG)F}`$d_tT&C^#&f>?R#9Ew|0)K zS6he-ZEb{^mEevI*;krM;3Z-=0e9~Z@dtM`U;B5J83;9hcwDT?T9xSr0n4A}7aDjh z`))QUseK#FcwPQ;?6#Fb;fy!v?nWQ6!V#!-fVkqndj)Grjt|UEY`FR=Yk^qL=I}YF zq#p#&HwVI4nU8CO+l4B$T=* zpPa2H>}=lztnGatw9-NWY@sVxNbrA`M*Hz{CTnQ}{b<>l2t<~D$wjn6(}ky6-!<=#{Db^b93Q&BKhU4}UC4+ron%AwnC?N!DypNZ81dfNe)8Dn zKfMvm<`@`Na2s*Ioge8N$@GzhB?mZXdh}mbx>(1L1(*c}{Hruzxz_Y3Bm@-2?cfMh z8-8Y*vPVjleNz_T>3~xmwg7a1q}?nK1m*KS8*jQdGRs5}Www?{q=axmyT0%Ar+c%& z@9?||rCa|dBq%4n1@9=q(l(GuXJW7WNw*9Mu;o_8XuGy1ZzuJ%P#k~y`s~rTnyn)+ z==yx+Z{80#YqGc*Q0ge=pAj)@NKMzSa4mANJ`oNgtL<9gnl{S(~#Z2HQ zVQ|Oq)Qp!!uj2AAoZS^&#ce)SR{JuKIQ_jX4mGOzK?r4H^0~i#nKe2UZHui|%6)=` zLEj6@XL705!4{__J$^}VZUdW2ySQ-I6Ic0F(iNy}XCM|{Yh$Y6Q7F55VhTiaiEoSc zlO?yJwX0tyIaM_AaaI9E2kZXnsyLu<{JzFunUO2&dE33YZh7-lklJGnC-A>;G^teL zv4?QD@Z^2`Q*La9#>H3hv$?3`3!&TS#)Zn$Nvsrjis$lP#(mC`kkVNlR`u!{!&LgG zGVDvLB^%L8)+p;S@x$(t&R|K$nhtwixXq;98nmi59)~YD-TP%Ql{>@tdr_qUH{0}2 z3{RSD@FxoF7i#T)$5r}qUTH7;I$pmg_u;ctmBML2cpIk{GMO-Jn~IgPoWzOGVk-LK z{G<{xOy@brlD!FYiJ-C6GF5Q9kD_cG?#yM}eq#FI|3l;q_=AUux$aNn6f2%>V%fk>nKU`~V+6rVG05kj z(LceP;hba3h%nOHleU$6YD>mq=CxYk%4NLB{4hKM3pDR1SMx8nySiHGEm@8@wz|Qs z7)4%&M!GU{tZ?7r|3P#v z$Qz!!_G*Gz@Gz}*ElG{SD_7!?!1A7GJePEJ#qtZY@yZj_i;B~5Xj9=kH|U5tuI=>q zvFK{V+~U*Hoo&e&50Cw|J3Qev>`FrrxBn~M-|6m2S@qY0iOfV7?CRvi`R(D5gfXQd z4AwsVqkww9FsrqBVp~Afxf6Jr^${pU_c}r4yGFYpb#9W>V>*eO!*|!fvge;FN=huu zz?@Ercdkz&>q8zyY|hpMnr(}V2aJjpG|vb9o3T~|7cO&h{?E?fMaP5zMtEAT9zo_7 zEfBl$a2whjncFlN{rR(u#V}x(o53U%#B#J~D7Z|y@xMKMJThHA0a_gRluhKx5SW8L ziF*Q@(958GRg?z7F4yWi*`HTL{@*UQv^}q%*-Al~p5}b>-aAzGIGo+hCIdeg>y2lu z@Y}>ESo-u z8kd1-C@WMx{XHD&Fh_GiIg(=#j*Bmxi+g?@N4DYsd!PM8yT62D8-D8fDRT6OPNu7F zIkg-BBFXoca{FiG_k6$m1wTo$8?q<9BggtF?>$QTT(-%*UIaNy> znJ~{8LO?CCh$;f->%P5t^DZ?L>$5kyB~b4LMHx4~W2ujSC>p2hLcj_=ihzm#0aZS$ z>D_u>PT%wDE0*(=Rz2F8Eo4uCV$mjB4%ERHuAIK3XhP=y8f!wB!CLs@V=XCfmL&4q zN54Dyf9SzWVd=JB`4i33Ki6&QM?XW6C={gQ^#z0G85?smC@2K>|HQgE@v{C&ZFu_S_uCgIOd4go`du$HUXLs_1l3zy~>Rd6zz zBcNTlrB1;0O-VEYDTT@Gvck*lF`&e~iv-*Fnxosbb^R?(eGw>BQa>8fs@8}FZ91&t zEEK(HvHmXJ?qY-h=%pAW1pNCOou`JmF{5H9RGU3Q0IpI~isXYqElrkNoCrg_wXyI^ zqH+}k)oql}CR#}7*3z0dy`+8Dch+R)A>ZL&$kb(q&Jk+f9|LftGGE@xqfz{mMSF^% zLG(CY9V5a<{>k2}o0DGP_{6@TH*R@AcyHnncpQdf-taMH2Iq0mpfoQzC`Us^fIwLd zsga;uT}VyB&TQs}#B257k@f;y?Nw zmtyoY^U!Jy_A-oMB~)~G2_9Q*GRocoC#9H&Zp*;-1%HO8&xYV}0b}W|Yriop`DSU4 z5pB1y^1K$?KK>_Mv6}K0cAuL8$?0un;PvQzZJ&k1GFmlY_|nuqxhUmb<#2B8AJhtb z8z57X#Doa@F8=6U%$*r}I(CHrul$^9HG$3!@xIITFudK`P}`+z=NCd<8FthSoH~-7 zKV2ynrBZE*k9zmi%SbP<(HB+TMt)b1CL~qPo8+@(O8FX0Oo6*Li*gVy{WZ=fHY9_a zjd@gkhDL7DH?0W1S>!A6WsosSS!IIF5Ub*D76jb60W6OP7FPa(JDD_Xx{YB zyagYhbqTv~u#a~jQXPBp0PRIujkWkB$DdJ|fFq3o$3KWcK6B5$1ml{p5OMP74Zpeg zZ?|u!JU+W9s3!!LF3IztFFF23x%st01X9OBB>{209l7_imlOm)iZ?c@6Bb$-6P?Fm z(6~;JP=bufQSo$9v56fIlZOFU^5ohqqC(zPYlhdT;`5&`7Sb_H7s2yDz?9`;F$}EWlbw*jsK^ci=9A_0C$_P zA4MEnReOVDOd%gKUYmT?T?6axB^#=ICIHnP&Fbv1mh zag9_F5Vk+nY^#`Ho+DSrSefrflQ7hi$sVVE{Ga4+Tr8Nea3OGF^% zMmG3&{naCh!EwME;|icdZHXy}7Dqy-JQ7zgPbyet`;Aa2e@XwN1lX*7jjY>R%n5@g zK~@VJ2#qHQ-%XebLmt+9EgR9jpA_Cyz&Zr770j=lMn>=AwQAMT5xXJ;U)uZ%K3_ z5#b$)R(-FCpz&55Pr{vcgzLtFelKBWN(nyYrTddF3tZQ3TqA)^xLn4GKv9>qQUt0D zI})71Wr2?gmCo3$o8jEy_8D{$U!pt*lW(78fx61<_boi#9O-LV6UBja%kgZ%(>QmS z!tWV?BRHdME&v6L719ySxI9wM?U!I$$p;FYc41E-cAu6YSt*h4cqJ}SAwK&3Mgap@ zMmS0BG*k$@Iu%w%ORv-2G zo~|kGGF$=e?MUl@b_&J(Db_3gNz)g~AF2F|e~z3Nb6;0ld=k)nYC3zxQ0RfcXCA}# zBQ&jr+@5t`AS z7mg#@dQS9@pjo^ z`PJ2V+UNU&Wyrtdx4W7b2l3~cesWQhah2VEtV(+Q76w5SpTwoh`o}cm4oWrv371g{ z89C$I`*;BD*DIE%MBQhhbA2XH$DhP;0-btp%rzwkP<4mP(rop9f6KOPB^7WdgXEm= z>63z3^XH-vD_NGpCwhQDRouuEac_ovs_F>eeC;5pQt3l88SsN<>muZSUvb&^&x?`^ z7_&o&9PGO#x^?cf9jkS!_HOk#PepXZ@>O*${op~-l_lk*r1Q`@5!Y;_b%IxHX? zYO`(nE_H$fOw**vWck(RSe0V{9Tpz^8D6}-axY4S2Q2A$`smOGbLBk(N!uGJ-PX$m z)L|4sqaf3REVDEYi2KFuw%$(vO*vusb(>!YzS`4zUZbg9+Sh!|d4bPSrq{AP>DLbK z7$LH0X`C7>&mdramv* zrD24OuDtXI^DJRS-|Ri@VbIWU0q&;mA)?@7Ye%pf@t9b}2gG|a@$YbmY`G|Qu*Aq6 zi?Yo@9%d|ne-9_>-L!RE^ItT}3!LeupcH#iBmef1GS8+``qc%|Vaz*t`aHaLpvoq*)Zpocv(T;`_^7BkmE zP>6$E%~9O(WHBk|&bXzfZm0Lo`9vD{gW~D+&FKN~LIpckR~8M}vKMmWOCT7<3@`XM zS;Ychw5;5RXhafN*&dFT)!V#!+e7MVLJxNw$?|lx(KO*YQ?ecfRf>n#e0>rXC|-Ma zVfLf7;A}7KNeOjegNH}X&dqv{uWx7qIvo z^$F5%RatnSvCw>#IN)Q?Iobd!HC5S^BX{ zeK~UfjcKe~QtR7HMM&n*yUU?{=E%NwikQ}vW~So_a6PNPjr58LMcGHC4^d+~tCzan z>)6Z)+fz2^Ip=+D`8)TN<*p6w=v?RcIaQ%HubN@o47Z zkrIy`7#Zw{IEhc{4Ednm9mVhSmvsAf>6u#QArRIHh#UxczxWSTr&%M)2tPDd^CcL+ zy{Jg-iRBr2pW(##o7MTJu@cfYD=}&oB@C88xy%{asZX3I!{x++cwX}_2K9h|l8D2Z z8@wI{Y=8vWslU+(nEZGn?BnU}#OgShS0xp_GU+4uSlCjS>fH%){e^I;!XDi-V@IyD z;!2r@tVEAjb*+j2vSwmMoFAssf;QK<@wcOkR>xP{+J&)Qa#aYICj0|-<-e1%npDL5 zNs6-L_A65*KDWjA<^`Xno;t3Q*cizZEcMU_&sgUXD@pUr)Ueq6T0f65)Fo)NKJ>F% z9ewtswxChlA%MavA`25-Z&{@Al(RzANYX?%cQK{Q;@q4@^@m@dAFM%HeiIFkioFsc zh=)v@P?}^(MM>#h5F7J=A|*c^?a@-WuAVbUvoEi`Rq-s|OQj>txKib$XYxl1Te+!!}wQHVTSCHifj^~Q6?gQ z8aC&!_I+(b_8(6F(cQ=Vws5-m><9T=icUPz+}LlHDGhh~K?iGYkYMlBwZ&Y8VVOB= zDy+>KR<3pfxx0;IT=tQfJ^B;JSzSONhq;z8uw~F#S*EY}A>Yk2S0Z7X01DZu2*6Es zf!*baBnmmD6991%wR9MmR9ipCUr%@cGZW0TWB0(#6JtDxZJ{WJR2!H)erPo%;3-ps z4k>^NUM)X{+8-j9Maggy1i>$gluO|kLc2|aMhDP$OhVY%S_k`lu4*dev!vB+MYtla zRtw)3BiymJ-kga#uK%8rvdA0%Q{z(LQnG}qzxH#dxA7e6JTROUKspVfSI7<%tkmIn z7?6{}Eyii*)m!T26w?iK3K()fq#cl+Tw{iS2rng@$k%F|tA5R7pRl7|s zr~a0)9pGd(H!8DnRIZ2@|2E=?sOHyJzZKAc{p1uUZvgn|8jO8ewAdKQ41c|n0C;f( zX<>qpRrOXxRglqG#-oNY3xDR%`4qwN390_k#E(4f<-S#;WNTkCl_5wiwd<@-HILTA zMmDE^Rd-TeK||@>N-VkNgdx4%;4plpsPl`F$#*&&(tFmGnbXf$y0{)5gGq&HHX0B+ z;%T9?QIJ1+5MJR&9hHlop#qETDZ)-w0^Few?KE=FHSc`+pZ<6XH#<)FdO67C8_W4}+d`nH~*erbyH zRq2xg;|KP6AyeRk(iZ}>)%O@Eh~OuXvZCr{Ml(xV2;HiFuX%;+=OD2)GFRb}G@-4w zlxh7-oIuzo4_pC6hL2?S@DtmkS+Z#d&WR z<+mV}+r;?{?D#(+T!@-cLz2gM0qrxd)Qbk(N*dqh{{C*NNF9uolQyHs@MKKDT^sq| z!E%H7)e@CU0!ZD+0e&0T;EDQt z_FnR~g4VbXDo2GzDV~X~`Khx&qWMf2I&4Nkz1Z=ZzXodwRJN}a+z^c37yPC0Da19i zzjh}U!6IN5Xh2u7!D?~f8%ja77lVA5>PUV4$}llY(AQy3KrME&hh%~M=iB{gBQd0% zR{K{gMW%DuQS!)qpR>6-)$jWzKA?bJeq(+s^f|{(iIlu;#A=X2H9&sq*WE7%0EDo| z05ePMg(_k5>hLc03H!8wV;ybmD>s0~bM9Nr1vThDCSuT2v+cM4`N1HV_&a7D50gGV+uLQmaDgWs~SaW{ztVIV~pl!tm zoOQ$~L}gD?MmGO9U@Vh3ffU=e@0N*!MSh|+SgL+!fCDcK=;7+V*V#7#UhTM2bvs>u zvzI(Duk*^(wN#ul^nWSkeX?VDHNBe?dSwncY)FhM=!;)2P>{J?*+_vnS3LW|ovW>H zt5TJ%i7&Xylx5Io{`12xCvonG68jtHTByyQmtfd!1z|E+6hH(9BU_oz7@_9-8PI@~ zsAXnqY5<7T#4}Se=KCQ1EAWFn((eM7xRz<>6 zVu07vgxT~MUCY34tb9uoKfLw;&r)MYvF-zn7%G;4xU0EXq?}tO^{{_>wt#ucl+KkQ z^Kf#3=jJQyT8FW+hr5zFRe9<1A=To3k5&P*=Pm8`fp@L3_$A~voG%yy@_6who0y+r7v6r=-7I^P7Awu5sLCNW(U&R-&exg)OA#ckQC1wvcocO8wER z6%?x#za=Kb+OLAK;&N5Sw;|6W10~?Xer3Z>ps)#-MglQ10{5N_U)6tmQ!}nAkK*aXZ0Y~vT zQ!(?WXR%%tEQ4Li&}oFbAQvzP6mopzbxQQYfEog-AMI29Ak2;Oi(#LCT8<}eqv@P(DMRW21@lh+T0^M;gpdVh+3Z|(V(2*g~EthbC`b)10vM{Jf1236~Y)&Wu_v zW^@XZ_1S4|SH2;?h|H|$B>`0*W!``Sp0jfTm*rwkX*1l*PA@8lL zRpgWZ#K(8)b-0!`&=f9es)FA3^7wdPkFkk)-D@oxx2?F7 znt(b|3iN^DDKW)#yD065k11Bq4{Fi}xdtNcwZ;ovvHt$IX&~Vr)a;CU=$oshgIa8S zag=@}^ly4J(=l;nGMD%EPayq2ZJ(l}AGpM%iMyRl?aX^K`H_aF7}}#*X@04fG}@Oh zy(~zHwkw!~>Gxj0q~}6$q^G>lqOD%}V05CC{^Kdi@g6|IhcS4P=Zp~anutgbnebEj zVhUIfl07X#yW1EGka;K9rX#L06J6GG1F@nQLp2JFx?QGWnqeu?%byvgtMa%9 zlF|9HYNuN-Md$$nsCCVBSc$A>NNMy}_0nm3R(=E;(z-!Ondx=I_5|J7gb|ozTjw2M0Z5VxW6ErXBtze{6<&W@IesMKW@TQ({yHe zXW3R=#SfhkRlsr#p+{5YKaH7Nhf&~^u1K^km-xOqbSV|%p`>}oDs35?J9H+0fWUmT zA{|>Y*Z&-o<IGI|@Z;slEoeML>JqfkWbG3fK0ottBLPH;i$qZbK>%b|Os?>D2P z(PYAdzQ2;AG%O{k30v!NWG8OVUq|$#^$E0D1wTAn9MD3gqSWwRZUT^BHP-S9Cs6(y6+7-K2%AIZI`AM5U$IhAi#;!Y~WW+M1LXsE_KZ`RO;Sp${@pE&mh#g>97z`=XHq1V(LqT zQqwk{A1lCkZlNjfM<@w$fT6YypjrRQYa2Ns4#-@N0>zHSjDKa{e|3>Iu1WdM(2t$T zbG=QL$DLW~*I|Dukh1YcNBt>qCN;>2{tL?)hx@;6ZG$~#!bYA5dTTkhvUwtqC;8@I zgu6tpjDu(@50a5d%J(90qsSgz%%Yj`Ywbfp+g_R!bXhqi5&~>%;_brFERgi9KhfFF~#dw~=+@0I2OsH%L zuF+%i3#@2ANo=bi>DnLc-Gn=xc5P=@vwP}`{GWPi@Is87na0RoGLycF>R=qo*;snw zJZ%-WZ6gz+?zl;F6-U;ioVEPzHRL6LF-w{qug`NH>dn)+|KMC-7taYg;V|zk{Updf z)p^elhcAH>!)j$Vn!67_%tGNLmO`&j6Q<=sf6LPA!@$SfUYbRK9<33R+iKVvAlUxO z%cT*#dc=D9B5|@9cYpPRRnEh-8kA2?==+!^CyKQ#ydzqJUf37-6)*0(NL>2{>ly0H zB;foH+&Az8f2J~Yo9#o-eNY%|gAn=Z{_JoSgxYg)Ut-*KkG@9yJ9-qh5_W3bQ@MBD zP_z^_EqxzBcgOEzzj?cvg!p=o_O#FYq`f&jg7^@2B-)NB5xh}2qkAq1bIviJVvq7@ z@4R;pJFF}<_Ca8uKl{ntxBU%TeWvLW8xh0krCF{N%YFFw5OzB&dcFPbAz~2qxRcd% zi)iZa#%+ttzp=7gm>+KE{o@+#*6)X#Q}gGtZDP5+O9Wj_==#a?0GIUH^Uj>eUS#Yr zaD^}A^=#jjADdt{pYv%I-Cr?2=`_1kY_E`kSXmxdZKoM?(~#bsNX*~qL%Cwei`@qq zDxmgyv}k%4sFqS!Qk$p${xQ)#b{`gLQVlwxf*mU+}{p7uU7^=1$7=!$u4H3 z+%%a#9~B3sTo|&E^fzvVT^%9Df+~~&vd`LKZRD&QTW0-;NA9GKD91%~DumC*+^3G1 zJe9xZfsY|wUKR&Jt05ADT{A73NgX29}xVu3B4btsv#l zzyKF2zq|V%?hm{{YqvsiyXerPS%hW;FYXhfP{lGYLS~cBf0?A-kRqKL(`@Tk(z7(S znu+fRGoe@Of7ZR!V1Kvu$2BV*U>mBG8&QKkdAP%bR!<;4?_SZ>FIA};|5R|t->bAw z6L1H}3n-i=f0SR0{rLvxzyep8NLgII^>3lgSDo|vEm=}iPUVdPnSS=Uj-#pkD5}%Q z)Ysin4}iphwi(gH|nr7){>{_|Gkd{`4o94!nT4!VUh_s!wkYw z^d&!~ZXkYSyu%T0s<63{4O8j7*}VZat6+-RwVT2G;G2D@V%Y7UH-5-fFtRAN99-fA z;HkW8;31&9o#mNGE5UQ-S^oF#ud#dc2UMtE z97kADrxIee^bV0Xbk>J*i%#}ti29Q}NDuAY{Qp!M!whf;E7M?~X1Q_PVnlCX2{{IEih zNet2Vjh~}!?TnBmOG)qKqfod>#q_^u3gX8%43%09y{8JYOiq&YxXKEBLLGh1aqIT; zIdGO~Y`??x||)vHVqi+xp|E&kW%N5#gxBUiB}Ip@CwoR`kUH+fP8!o6-Hc-ER~ z6l9Z58DbAje5>5SFD~PaLg-C4X!({TYgv4U{!vJotlwy%7kW%71Y>^8|E%!Dpuuv**<(5=UJmwW{MOM3 z{~<9}(Mi|b1^r_}d(7uB_ShuZV!;gm9mNuRO||&h`(C;G%}NT;?IOf@R7o|X47PWB z%sEXCwY#;#s$mH`a?#*XPLoj9=e<{N=!)`Bys8F75rkiQ@Yk_)Psd^?8fgjdAy zf0xs@yq)H_PS}xgpEDM^v~OX8<69i{{Y`re0XoKGav6ZUGYCx#J9jl5TR;Jq1)c1R z?99+`w7XO3j4y-v>eQH=z>9#|^_7;rCj=LKYWt6&`OMfU_1x27V13Z-YxDxvn-rnR z`*-%5re{ax2xm^+5S(2~frbaUDaA~~BD_{?ipzjD40`k_RDsWjyS7$&4?0tRqRnih zbfW@8!);_lVU1O(O5>l6-pUu>G|z^aQwi}BXB>V7O$OKP_-YMa<8PQvUBr|n2|Kfi zTJnm#``ovUyHvH==HjD*FG0qWY^dKr;1E?6K~{>7-w1%RF}hC3Vd)?IxX;{MqSi`d zON(v9mh`1(=o3~PiLj*C1l2+sUYPCj*PhFhE#7?@pgy18d-cMC5$>*HdAg>7ITdav zIMyuPg$Pj015I+qNpm_1ME_bQ+iXOt{?>lk!9X{PxdGoWJ!FFeN$O!JkKE1=?#E9h z{b}xY^zaapEF6h7%Nm?tK3cGg`Uc$Gx62DE06&4ce}pEDT4U}GB&V8)=Q^5S!$e|4 zeZA&(i(oqrWPSSKm`kda(9ZU|s<3k?##0;igW~*RUVdJRoCYw}wbPjxw8r6dt8#?d zWa2;U^HvSRk1&jsJz=l|mo$=XlxeBaz@bN^Z%=p>bCJs_4mFjQCoyI1{dd^|`t3Ik zN6Y$O#* z459*Hna0|gSW%3m0;ioSAl;ozg|>gmxT_F>!den$_lG|L^;eZ8GDQuwiXExrR!&5h zK4r4MY^K1sZ@~oI5|l7~V8)vD6o+@@%DX6s*pNHT`QoVg6F&wx3$rXKaLzCET^{9^ zhk`(iE;_r{`mz2_t!jnh3QB&~Q(;_#KhX<`A(S9tLTia23gp!*CZG*GgARLFShlNL zOaikJOxL-{jzWp{T|@eB5pEZ=C-KyCM*39GM&a2cP`zcvxUb!6PgU*N`GKnqglGUy zkn=M>kgda6ngV?=%kkxJ92|yGhv;i(QkT^-p|Mwt-^zNE72WhO3VsSk?7XB0YP+tW z-9`!l8gQEbSmh78Cflned^cTF?)l1Qa3Nk?qP3ay3 zFj3tmQ8F$Q@VVF8S|?B7vT^Z5_rZPNp53@kyNE8U)e?s)zON^{?S!fDk~lsf7LQ#~ zWWQrcXT_#svDR!|9Low(GQ;7gH_Po5dcb-qJdDS&NFnLL+J?8iQ4xL@#Fy)p{7qxO zly3f1A-I+q|@t|N0BzDHXKC$7a5?QJuYZAX1P#QSkL_q-0kTzoh< z2u-M>-VBfsMNzS}c6rFOTrsV~~jO+)c#`C;w>Y>q(a)U|?Q{_tR zq`AVTV90%N%VSFZGqX}87OhBAS#`;Uy36Uxf{IdCAX>^cO-gceeE0(f~C}(;$ z5jj(q+`(HLJIs^G`KKye_i;(6@p1e~K!x2EZOqDG)jPf78_;ahOR8YMv(`dLW|@V+LvGpYbgJI0@)*7XsxI#<<vXyy@YslI|9+PV&_M+{`^ z8TRdTvKOt{IUZ7t6(R^!{=#A0R>J_-1A=*;j#QAm8b_`?j8y1C>4Fv~P#V9T1mpxe z>ogzLTo5PR^lnjswfvzA?s)}V8;MZrAO%s0X4&RKmKs$&Fr=ri#}nEB64Ox?X7q3q zbt|e3-AETeJBdJvTSuQ0jG@Fy&Ie^^j~>2B=DEPADMEZX?`DdJ`xW*%rZpWhb)1h8 za=0E!YGpP8w02q}HWMhB_Dg4_Kc1ismH8?~y*<`ov8nszkrFrCQg{jOu8$({Z`vmB z60pNo7SB@I^lMb2w$NkAtcuAp=C>0}-BBk{1ljd*$p|AL%nTbeixGRI)sym#Fb24E zbJ=;~OO^^r5D_5)MbQ+Wj6+#_M~ujO5rFm+@?ES0Ug6o-om?ymT$Q@YzVz^q3l~_+ z2A#QQ*eW2Fz1cxq<8X&G$ZwHo%TGB)n8$g2bzeGq9a&h0I*35VUsD~J1q3j4JWd!Y zM3W&Wiyrb7rhxVq@-;GpNH;*BY!PxSd+>mC854I!4AXTQYyoqTZt2D~*nT1;mr_s? zAbBA@IQdz?f`Af~2N;km+g=s=)Vg;p;8nkfhf#EEaU^sU1hPtlxQll*BtsfH$KuOP z(Ub0&8;tpXaMF?M*J%Waw8r{>T;n9UMyQl7so}*z2utaT!~_jox_(;K#^*f6gFDb-Xf*H6686 z_X3z=vy8<@4m=q}$7!d=r4@oxn*HOtz!NE=Y_vkcfh3YO^z-scU|Q`!&Q@MjNdvh2 zhn0*FJWmtsQ5!(u+izGevV>kv#Z~X?a3g^Wc)Z~qG9dJIfk0M~G8(YhO@54EUf(_D zVJ3itF#JL5&5Ko*fVqKe$&iYxbuC3oeckG;UtepKeId0NxBt=IgZki!Mx>*WU0t{^m{0Nz+CztNV- zJGBd28o42I(P^PHT?yHG0Z6V0=c!?vSF$L0Tk%`3nst~IR zV^A0y+~ErsBDwg|8=K7+!sudJganhr`vpP$%y3?i5Jj4xreP^-yj*UG2iZ*U7}f1q>U>B=V24ghE%$e=}}ut-BO=Z ziD%Q(4U%Bc5Rv9s_zrsnn>7Th2dY?Sk%DH;)0yao^;2&gJIag19%ml|9Xs+1!ZrFN zzyxJC&gk49?~~^(+eR0nED{hg?qaia=pk!TMAtvg~W>f?b+@A|(sg!2b_ zYWC3il6iZfrj3R{`@5$ZjNiOa!8j=SC>6@aXz`XMR{D;-UseFjTb9kGkYPlJ0iOR> zHBt*KqfBvCYwN`ZE+*k;>*t4DeA9s?tqm|vzQ9-^adntdmk&B;gEG}Y0e3TCB2uC} zA4mmfJS63S9`xRAO=!5%643qG9#f%@6=>cfnD}G>p4j(Zgv`O$=lR1O^coD*>-5oJMjtIy>G)jw};R9nZ>W z0v5W_)97cKAAZUp9Ti83<`BO|a*m2K%|l{KVak}I!ud`{JU63#U~RVMV5HN$11%JM z$q*(NXKS(a7fJF7;(NJB4mY25$PLs|g0440TCO5IzB;5*jPtm+GG(s(@&WKoj0ZVK zx-I!UuPn~_ z=5e~EDzmQcKmadPkrDmRY8-|lxFmpq7$n&w+st>@4?dBjR)I~kgW!@!Chr(jU^+nc zHH~PNrBlhHVPK>K{fJaQcKSuOh29HC&Yscnu@4$YaGlabwUc0k7yMj3VesiICc4gJm^ z5?jLoq9A9UGRz!971S?RyMcuaW$|Qx>N@Ym)BdFoQ@Jiq+nvc?40-XKD7&iU;T&;a zL?KD}S|1-a@Py)J^rGk~eW;LUnpW#uM!)`FLWm|CHYdRjMc8DUG5A>9d%LBpmfC9h zT^+*nS4O%w16&?{w`QmU{ykZvnnCxtS`SuX0x1y^>-3wlE@hgML-zG^Sm0FyYqa@$ zVj}cG+9n;dq=ajPC^ylFaokoJ+zCL&qK38oIrBjVC|<9dDzicV+lB)p+EH# z?Y2l-cHEE@3b9o9^|cK#$at^TFxyAj4l;h(BEM1>Y77p|UV6+6G*gxV;r&$PZVo$9 z^`t#MMxKf(^dF0D0V(5f;lqr(;I{Sa=yU#u|!u?IpQaw$HF3 zM-P7o9B-2YaV~IdmXd@Kfx_(=nS(t@RY?XqUTV?dFA07`a1iK8AQZoSr{R9W9Ye3; zvsogsX+KCSkz>EN+!Xfn4^Hmuyy@}5h#beH~^HH-{*jC0&**i?d zQ7DGO8J*4%f0IGuC~ki2z&Q%uzYyovjtp;H5k)7~m=z!`=NYn^ps|@^0uhJicPV6oI95UccJd1>I&)d#RC#Et1|2T`KrsJ&pO zf7R?=8p#zz$v&3APse!4XB&s=*m9MT`6Gd7edMQYKiN(OjPt)~97W*uV-mliOH40| zZ%{3S=iCb-{K;a;Kk(ORtTw#qCJHDUvMzS``&j%juI#GSsj{hz<1VSA31ixSx|D^b z+|WM=7^A9${w;pL;tA=!xqMv3d!`9x>dy6H*x+{j$(>dyu5tdRynzrp5DT>@!QwQ8 z!IaaBxw&RJYBUabjRjc6yVkr>_J>?`)Po{UJ{c-2w8LoAJ1B<&`#^%MqEqiSLb%k` zU?H#nzb_8h{&aZJ=iJ8;Evr2V$?#`O&vRCqoP2vh)>_c}G{@xdSFRmRmAsUeJgJI` zgp#PCm4zuz3MZdJe|y_HSXo<-@>a%S5(op*%ZVMD&B^x)M2nV9eI zsyri1cw3bDN+GZ&9&oNgG}I)Q0 zxkxwc+2+tW6e@4d79-==&FD-|L< zcpImM5_VxDOX8tETjN^<;fM|G5q~PMut)d5i4gXyQ^DvC*7s-j%=o1>sz=TyOoe(Y zOzD!FVpxxt_U`Q#a2zCZ85a%HwA1k zP33=s+c0{%&nG?U(2WsU#pYW~ee%MQisiy%v;Na2t8N^T;xcjXle%)j8~wdmn?o z|F1GDQI6_0e5mKUFs_}lU_Qck3&je8K_I!-gVQvZb56mR^tkrJ|W&=Q3Opi z2YCO5mjLTaUu}k8c@{4hqebOYD#~ibL$=IN7$8p;yL0F9q!~C7KWIEfxq{p#<4K23 z%!-n~={@rW^$Lel8JCoL#!6SN-(9i?GkKZUuU;FM1!0mHf63AG*&j5((*10m4^76J z?C@>$Ka(cu<};J_)r>U*${SbOIA9;?cREG{5kyUQzVH-$Pz$Ex<@NFZDNKoWJzmuP zWPfdpx)$RRSJc0^?^?yMma#Ve!_{N^+70WTy}w;Z*%Hw9A9ZWY8jcLARG(AuU#gkn z!v6C&q=g0*xyrYbliVitxZ6;oU35@k4-|%^%PW7k=HpXJjyl59vv5OxT+Tc9*Kf6D zJNSR<3u5BEKpdC1Se=Eaiv@M~KQ52*cJleH+*(GBuH9szm=^Sv=TrTifX#-_07&{}|g71P;f` ztR_0oS9e<}Ob>i4+usScjc;~XQciZr7?)WfIYI?vOuhB~YV z^n0qYlf|K0EXlBz4*NY>n3%2N8z819{Pewdj#bMTQ$)nus73J>5R)G-q(tw)=};r| z$i96kMNgAp}@jsP>WRG4r??G^^*h%)6 zhSEqm9rg1`UV2=ZL9hFIj}1C(K=)i_bbqT&yNr=vD^)3f_+>P}df2Gz3a7`-_+kB; z3N!njd6i9D6y_A^)uzz2^Pj4*<FC2QP4!lN!Bp zJ_eon(F;}6{$L)s@Z_M4u0>o9gsLDbo`iY~%@eLuKku=Rb;U6?*{WiaW8|80xUBo8 z6^}G6i-OUxeHiszzdw!zP80BYa23g=@5}}>-f5mQXk)FH?A0Sf#$UwKxBc^A>eLi< z$y2nvPA&*ml9zAbEA3*%n{tY%d&`p-0<6QjmC0KjI6}Ty^M;44z%MDj>#=q9ao`nWZXI57y?`?)4t(3BoLSq#)jk9?+5?<=F=C|1g^Yr zm}ZxX0=m)&z(GR+`=nBxS)}sg` zf^d9uZt)>Bn*En`l>q8(BRVpzo-T!f3JxpD9lM~#0a0`9Gc+`0xiK_RMD0*r%yoP8 zCIKHp-VPJ8ucF2xolNp0J#bw^-A^?VGv@;%v3>g3>{rINh#B*-m_<#GkW8d(lRl*N zBMSK!NKc~h_sF1MViX;hWg`OxZXvel7W)@#Pnsl(B5~eRY3!~YS&M5G zuS@o&?HA{7F#-B|@qRR1Ib`@`wfXgGo6D52W&ik(;~$Es$X;y!6T?5xjvUSUHCcDA zf$cbu%@@;cn2?4y6~&*?$L3M7f@!+zm5SlJr)yv0`az-EQYm^*``tMFILe-Q;&`Hh zPfF3EUgc>=1xZ0|&4U_KjQqvsQuXmFD-zrEdsIlu_6ejtL@Px88${(&Domd5o{bjs zlUyhsx=Xr63j{KwHl4WZy)cJ+u_hHD;V#FOOxNS%J;VCazR7xKQ#s(^A4gZ?hjkpTK3p0VpmIsI!8kCw|^7z zs7lN|@MvN6nd#cCk_*ZCJ?~|wWXSl ze=$UP)1N~P;bhnmvL0!9Du0$Ei->a1lhH>(xBg!HgZcPk;h~{lIQ+pGbIVzHW#6od_F7C9r|l2r z{Ut-Jt)^MpPE8*$PM0C?%9l?|%V1!g{zqQ*)CFPx_5!WTef# z2NY+LEkmaAsnq83kqSkaFI<8kh$z6B;_2u9N9lV9`3ofN_7?9J+3+&IJPITz){?91 zFD_E;5RfV~bSDu)gFo_4KqUx}D4k3NP2e~3ZxuXHd_Bt+3rAFv6YotEQ!#8KQG$Z8 z*M8}3yg0LUsE4;0!r8stY-0RU0nY#!M{jfW;~<~>F&KkrLE;ZeW~O)CmvRreTqF^> zJs8$@f!RIvd9GszL9RyJKlMAn)tI$VXUh6(4%?w!cUDu6 zFJoV{kINBCIn7EtT)YS$5+`;?|2kgHiHrHDHJx@StlwKW8xK~7vL|AhRNg7dHk-XO z5cZJfoIe8x2FeS$vXUk#S8ObD>!pkd#a|m4P~}7334}W@8K{Qi;@cwM#r{oq-I*4g zCdN4&5b7|iL>LvQedx*-9P)SJSaM@(uzYc^y);AawzrhZ@nuM>tn7}=n2>`XA)&dF zpKuBNb^gXH-kALC2xo*&as~ykKx9c^h^OPLEY@WSb@b3qX7~ z<`hn(rfHr^pe@{BBem1QUaXRH5}2=^@GpXU1{r3Bmo;+Mvc1T7?FPIfRPQTgeP zznivEa;%*hMW~VynYoV3aS^r!C{^YHk&B76cQ8ujLZ~3J5CwU65`gUFU|hvSCt6x! z^aNkSvlEvXQ2vtJq=y;!+HZKjK96_v&3-7}FU5z86HtQ?diV#uyLmIb&YR+C>-FFy9!?3f^n^rh(bd0yLZ0yHk%D~(gdfjT{R5; zIr8-|9TjRdtsbQwS$q{}P?fT*ni^B@!rfFt%iS6!XMmTWs_a@p(DRq{y7?x_^qgqV zhHTwWd81O9OfFK-inlo0YH1i-GB{$!*XegJeaei=$ArJOnH@whgwUOe+Ey14ma`r1 zNIvf>B2_5TPaNX%O}!(aJzI`rC4n%3IXbq7x4J~{$S7%#B`<=cs8D5Et%6nQ zW^6%W7tAj=>?;YmKo}zmr>g38UKv7(Un0M+q2+B<`0*r~O=6-XAS3lYT`s^yKuJ^y zcg{8Wn|-^(@OZhw){@-_h&EL?QS58xin;IBM+U@&)P zu-zczlA6Q^{jmC}cOIyPk3+ls0pkFl`k?;B=(#EUc9dP|U1XVw@89#HVa^ZDybywP z^og@3R~W=$OmTiH;hZ07`*lyEBFaGSY5?Ko&%$5kG}fgn?Z$Hy7aH;{&ReBn=El6atOYF1axogQ)YpyRsY1EtmC>bzf-Bjbh551) zk3#G#T1_208R`Q8j0kuJscv_G&WNs&Gv*J+^NfhjXnKj-Pr&H)Z(~uR7OJUb!!n%$v2%n`j* zz5}fb8+nsBDolBCOUg@Z54)aG|9yfg+oGl4!~XHhANx3Y%1RS|`tCSp$_Bm8T)zk> zv6tEIwYwEo7n7o=jCauPeH}nqIxhO(`2+LEA|cA;M_?sO6df(4G<9;vrFGj?K7As9 zM7tv6K0QQcNMvYAwwKoM*7VDdeDHWGFcP?Z{e*IK7ny3@y|L??%*M0TfrcuX2uA@j zC1AuTl#51Rp8*{Gs@n|}7Sw{9oCN(YF4*;Y&0+Gx!S9w5F-{kzHIs?hn1XHkmsaE% zHqFk0vdWUQ2S=FQ$QHQGGhJc8Twdnn=%eJGYRGfd3;s)zg??My54`4EDeR0H_`Q{u zP!DnZ;K5NvFnS{+GFE-XTCm8o@z2{VDf~;sOrw$=y<1K!A?b^Z5+op63KoStZxyw~ zmojd=c8TPzagxzXLK%?b0`8K4Y{qc^9R95gduk<) zXYdze8dr5~L<`@uKAFRSijB`gwk$O!bvbFE$6(~IjQJR8_r(!vIzXee6hG0O zFiW*9BMy;-+xP1l)C9~4QGSfrB~*39&J<$cp%2J+CSX@I(|zJZO1+GqPD9LFpLj|l zPoqk%dGI&m*uy*e5)uBr>qs)qZ`B#Hp+Mk-pO`}qjNUg{YA23$sO&5FIzDcM<4!?O z9w^LN%w0Xh*zn6iby$hOJ+^G&NC()m2zBH?X`bA$JdY2|y?+Ri~C3Y9o$VSJ&S!Gain z&Cqu*f^pd;ip8jNBYz98xk=q&&p=Ps0l81?g@_;|G{IWvYa87`EIKG0@39%6f2XHu z%P&--R0;4=R--o?=(S)7w4->1(q)^!+x5_lZu*1i7lY{qd^Edi#z9$u<>F$_*Ftv@ zouWoN^l_*2hyCJ3h9Y=D<6%?XePx1yye|`Ig1D%cnXaa&d2L~#yf8y5a43R9^1GB9 zxWc?E5Fi41f=@5rBtnTTRfh_2m^@&7R1!5$RCTYlks!-5JGet}?k;RtVUwEp;1igA zA0vt~-|M^!cfF74&pWF72@LBJbp1H{RtW1z^5>nqHCyZaW%5U1!jKQJs@(9k5lT`< zO_flF2ib4qNs9|IlQGT9kHFx31Y}jmge3jNb6J}A@(_mf`zaI}C!F=9VkqViiCvZ^ z)*lj)RGP}D2IVRcb_oXq+Rp1^enBSwb?DQlk1)C+)@_29*b_RrmbD+hZSfIfHinIQ zehTzeYVf39yt0%3I9io_EnutTqeF@eX#f?Ge#6gJ@O9-xUEHXNGqw+`#U=8PKP|>9 z_1bI`4U6KmeD@XD>Q@)QUJQtBwni0nTZ_B3H!?T-9Ow(V-mL|?F|U*H4RWQ&C)dZ$_KVZ<-)=;?=3_kGqJIJD+$EFY0k1pUB>{ui6imUcHwV=x!w z7eNorrI?vLE1ihN$i$R~Yvhk1yrTuv&KIBP&0zgOCz$R>0T-d6HM}t3QmdZoU}18b z9?!*%UIF&sZ8Xz7^$o19&b*HoiA;TZHb4j``Lg7)deukij0EyKhS0 zM4)X>d+;TUe*DwtKs)U)NO)o{lYI5#lajwKt7uX+UwkeTERQ8GIadYE3&Z=r`LS5i z4II0obo<2xbP1t2NR8%O61ASASXYZ2Nu?$2Epi{maVY_o*3f2s^V2|W#^Es8s-~V< z6N*)yZjXR2Gf*9rM2jQ-c2psiZb;iTDhx%NqoqIW^~9GIM1+KC=FaqrVeh$Z1^qTyZ1`5B( zL2oJ5g{4a`z)YAMe048N$cn&3B11k7Y1^D(Az%tI07U;!+j|c6(K9eQ4ga|Qa}y{e z1hpAYJQ;T`>p#~N=&+U>l5$Ipb3;#Ot~XK08`%D(iT@oSi&L5QpcN8))vFLfHoi}J zpfdD1vNlX{vz+Q+u6Gxl%{UA z+4Z2PTh%S)6*r-oXbxv26VBY z0SXmu2q^> zH2s*{N>xuWkQ1FrHST$aQkk4v0}n*-{^*hMmj%_6dWL#G3H8x&#hha}L!o(;vg7Z; zXc)(b3{=mYk5*(4w)hP!l+Z4^c#Ki5UG?=i0w?|5KHX`ZtL5hUAd zbO|TFR1n4p$zs2i<|yFaXql%f@ApJ>XikY|17_3nXQvEm6e*eSiIyj?jk~PD+Zfnp zCJjUtr@34V^1<+#%FNY&>!gpy>dEU0OSJdoJ4hh&qB{8R#VU3IRusA}$R0L}2DK}s zc#xPOb_ejt_fXD1%gJjBToClc5BUN|;g+W!&G+r;8;X+?bK(`J!qkD$+_<}l{Fx)* z17~5x`pt-Cgz9hx1&wOPqIip{6+4;pT*@*RUOeL+`Va`3XHw92L5dKJo^dAZ9(dM> zsOxZtGQgq03WET4kuTW=q5E=`?HHgh!I5gS$S2miWZH;=S7uM3dk!cEnd%9EmQRu+ zyv!cXnB97wwQwVLA0zW)_%J_Y?45Gk@0L6(a z5;PV>pxREhVKDPyND@!k#Y#A_?r{(p$ppBJkwj4}2uD0ndz71!gn=nA9{(y2J&t-| zP=3O~=@A5gJiIr{E;n3vc4;mP9Lt_^qMMRR$i}H{V60pxZ5Kie2`Bum51->dRL^I zhMO~_#kWFsVIO%~*Y^6K5paNV$HExX2rZpekmc-Hjm>Z$kjNJBTt#N9Yf*%->F<4X zSZ-~Csg0jyMg)g;8Sl~Mf+C>l%+B+#iVopV#=Fxeo`WaEcOej9i;&2O4i9Kd&A0O{0Xa7W|O zCh|b=a}(VZB$pCmX${EUyp${37X&%$V-}H;Z@{2bzTuPvPATKW*;6x|>9qoA;$KU< zeW|(gkjI|e-WGjQIat$a#QFY0Jv^_ynzlCBPvYAEb_SpN@JaUQNAqUN`I8}fC7B;YLZ0MgH2C*BjHraHr{k;;)De!q0o?D{10k8QSD z%z-%C3J|`zVGw=NMvBGQ?Z>fcoPFcY8ldRHwx@2K{5P3}JXse|mMOs(4Bdkhz{mt zOP>;-Fe~t4)?6N}#$qmd^b!bU-Q~y^$?1BN>+A7}fMX3vU4tYGcO30?2_{Y*uut{$ z>;ZSRIrZV#BpE99Lb0DJhyH}6e6>sSez`F2uhnL^_a`(+_0SW1#@n+V4*O^$iB%%P zWSnxL;KVNu871@emVV?`Mxl;mINXPWHJ;NY=-XwlpdBQs|gd zWpc07d1LHGLeK=l9j4(Vki+AJu%$u53Q9L-%XKks2hhUdOLMSyyjSY1l#_uy3IW`v z<0Kta@QTjLK}|crJM}Rbih(MKpXAwLlSZWOYop&OQm9E0wAVKKecywM7L(h)sW9xl zhdWPPN>el73k1in5CzF zsi9=oMfbk-@;nCN1Z+Mg%$TI~=5?@$#~Z*fgd!<~_&W-} z17zb(ZEjuETqUyA4L{2bk|a@C|B zLj7tyZ=u>#GgiIW;6R8+`+MOgZOT^@W3uaJ_#;e~u zEnXJ7NJirKI6LB46X5MK~_KckdJ#TsTJ3l(FC*wCy`Yb>So4E@WKx<)#5?KtHBcT zX+=XtTtpOE&G+ALNxA4nPV4q4P@B^053pDxgo~N5J1 z<>j}ulS^A$Aj4G15;IW=`OXe#0&#}l-hSG@?Hu{N%y+c>7eTYt^k422HEZJN+k@fD z+>sCYtJcp}ax!69*hY#!rn?mp7S{YneT*4)n>npIJLrl|H~Hfh<&JkzmH%dYxd}rHnVxsn_E2T(r&@k{c}RRc+XXcGqr9>k zEbqlwPj}NdGBL?_&W=cPA9ZO!%r%YNOxJc1i}KCxHWN2?8-7F%d?4_ntsz;gukbR@ zi6o_$G@r97#W*(Fr9h*D66jw0qxd~6f9|-ne=XaWfQ3EAwfnyC`B}KaI;g#NU)kB~ zR?|s%mSQ*vDKiQz!qC_(FdtfTBw(QT%8IviOS~?S)cDbgv zSOFkFCVIWpFP2`3hE~C^wfk0nrzxrV}00O~9h99TNR z8}{qMQKaEx{Jtn`imDph@BgxuwLc}~93{Qbfd6^rs+6eu^>;RWPzXU!>}C`ZV#XNU znzkV*y$(C~el2|}q$Qk-H}<@5e7d`ZiGi2>^}6Rr6GBDoatIsHF@jfF-xb{Q#s>z< zs>hj~`BuvkQ2IWoQf~v+A{L=~3w1)?@Coan%`VaG+gi0$4Y)~BkQFjtANF|)_E*LQ z4srFYglp0T18gCuoy~K*o-~$t;Ra$slHFFG)GQG61B?}9R9j7{efs`R}v!z z#dT*xVRmK1a!I-z1w}S^|*fL_S z@VigQIa_L^s`SzOk9Y}Om@e^LT>#x42+JYfRcC#vtk@0z>s86Z48;wEX`$>Q4t zQlo^wII?ROGsxmrB89oVN0pHxZ+t8tqjNh zlYA+2-kxxip`}<#fCek2ekF(UzhDVD#1^AWp(FtPHzP~YaXJ-+tRm*ciD%?uMCIUy z&51lb^DPX<(&-<_ugk#OEA(L*!2Kp8cS6Q4<+vyNuRq`Wr2=3vv2>S$%+4wQajZR^ z1~SoJrgZ&;S?6-M|UZ)D9VT~``;M$`m+7t{N+#~Q99kv zt1D8HaS3W9=9oYN6~VBr9q7Wu-D`_J$9p?(d+x@XV8ff~9Vnsjg+GB2TA5=Y8#%2A zKAInJh`J45-V9&J@V_H}kbr%I{GTV0`ip(|h}PAv5`#6EsxdmsGfhq=bZtB!-n+ov z*%&yfpMtM2;C@d?MxQv9FM2W(DjK<~G35t>7~a_ld2fe_?KLUFCY+Kj(be$ve-loe zhuQ3-McLyV+nzabvmQx86_#SfTAEN4TyoZLL?HGxU&YG*%getj+oAphWJ5Bb3#=o3 zf8oYg(4q6c6k=l5|Nm3pX<0ixuUdt=z>CxDPQVaFrY5nNLn#??^h@S=HHF@lyS2B~ zIPP9I3VR0cJZfjm`-@AAH-+&ONluGP&cvxg-q7uqv5`W<_J$Ca8n%wxY&QwZ7Uj{9 z>S)gMW2yhHSZFtypvPjHwDM$cav=YnK}>VM{o-f#C2yuG`h=JgIjO&o{raXmAE|B1 z2$dre%gESPP^&x7w9FXes)?_(5#+T`#o?a})ssajmK8f&3uoi?J!0Ttfoilvz$uw2 zR9y)1!wJVN&ldJaGVyMV@v_oZ3nEny$@J8}?dnkHD@~P%(IGIh3klC`iOu580)_ZV=f0&)aPmKlG`HZ@wmGN$w8B&1K zzmP?6ZV2#H_aP9TdI+@#UWZ^c6QD7HBL0eCE%uF{Gs{%pwCgYqMPB}w+hSa&Ahja2 zf%G%=;f2G$LZ%o1<$ORq8_my()O+f(LqV-8eyNEZ;l?Dnhgn&tZ;3CULC-reGO&iK zB+0qZ!a?@Dc^Oj%vvRCv~jklH8 zC(7n{RlFvK2s24qg?mD)aC$+_8z`A>yQP&>Fkq?sIch$x@PAqis4JUxEnyH*@_6>4%W<1vYg4H&7$_PxzFgFmZKO} zpGXgLkQ;*^X}#YEU(S*CnRvfatBtNVo+n(t<+SnrsJAH@l8PCvJO}ZLxfJ(=4u^~^ z!|`#ctR?KUp>=SO_dgi6{NGj9Z+SgOi*Q|tnsi@1Wnp>SJO7k1T5gsauh|1W_ zlh@O&`MG%xK%4NOypDHzh_(Q`hemZdaG0s*Et(XjUKm1lYTeE{zj`ic+QX`o%(^@M zU(I5Na356iq2LzQPugi0$*wY~h8S8`_=snSqspYBN+EdfNr2a&K;~YIU>~UO%(y-! z!7ey$h^KtH3(q;9{16#F(h$h0bomWPkb8~}r$|sPV!^$m5WXmLA=_d@G0=zY>s1py z`V$uAO=@g-Ep9!Svitt%ol3Td@7`Qys|E23X(OLvpH!#^wnsTNAxyc)W5JOCpEiM% zhvmfVOM!ANfOkW20R*)5vs0hp#8{44>C{lKYo4r8cO(^~jQZX`2D672_z~jlt-AY7 zT&L`%3(cIJwVa>T{!bj&6AZ#prof(9%I;LcL9I)HAr`$V;m<$Z(I6qVW!RR&2=Ex` zjp{q=j@FXzRhQf13~!QWP|SLJ*Gj!5#X=Mn1t_I7T(_5{toPD zf|mvE9&1y2&a&MPy#Fh{sg*s_KHOg?LHu?@C#F?|W9d!dv^6*jgs5Ho&K!6K%J1c;W2X@C#>4y4)yitpXsOR5tL#o%j^WRPKVy}J}`L2Au z`zfCtlq^Kow~w;qPZwxMr$3Wzf&kvpRkT!F+Q_L^$c*KQb;;k%rXyormU7!BL3#0X zD#5+ki!Qyk&4VqfO?G2CYf7Q~F`Psi@!V7VsM|Cj`(h8It-4^%`e82PfE!e_D7X3M zf~=D`JN(FZ51fvSKbNxMXr^rCz}yFry-xt2f3GWjIm1lB6tpyaYeXB@0)Nfc+pN>v z<;RjpBlqfY)(OE_f6`DM`b5&jTn8>W$5jhg4yEfJ9_ESu@s;{6a2r-p|Lup4ZEGgX zP9S9#)1?F}a+SV66J+;c=-BOuJr#c)&j;zqpKqrrH&=%9;COoTIC(Un;jj)oM4wo@Eve#!mwQA`!)=a}cg z^mLY;dEn(hF2SCy4`07Ry697IDnuM0C@)E!xD($C`)9a}@N&*$!S=lb-2VM&iIPUr zd7!f*e+2j(COkrZJP&jspQEkxV5R=3C>BT10DZ3Bvl5uw&Cs365nkkW#OT!eb{dZe z%QJK0QeMwtN!?pk} zM5t3A{w#&TD(?jq_c|p{mYSAa^RWkjGwRBZjz8W^u8inttM?1EAUz1Mjyu!VvQ`t^ z2FLfdjdp)5w@08Vl*8G%K{pGqvenN=b`5l)_#2w*TtqC+^og(hKAYlT?z6YLY5tp@ zmN#QH^_KNZLat%W3BBcBHyfPj2}L-q@V&iU)!Wn*x@8syqyac-ysJ)o5SNKqHRd)< z-5&M?@oi!AAWI4v-V%UwptwH~Ma~>cuYK0?wT6Ot zSxwmoz?T`*1$1GtTZwdyMW*a&Y*)UvX&OT5wfph|)6KL**U7>=>BRuzUu!FLIdE|P za8N0ThBp!N?E|y>b8^J=5BV{ooTwy7efnjEM53)Wc`RQnywmKxrzmOqz8oKF)i^K_ zGlGLIMW=ju{)0j_5!snz9vqRD$^{L1LdaB$I{c-H)5f(_dvot#x97MZ!%XopF(Cv{K_lzh|+3VY*l2kp(KEI;EghCQG-l+q9+{^FE@44H2 zgH1R$To3tXKsi|(RL_ReVF0M;yH1O%M@8)1KlZG=8oE2j6#v$dfm(_K$+ZSxoEEmb zGfY&Yimt<=t1123XxMCrc{pqj%mrE#!|GA>we1&-1p}x{F~gj%a$Wra7w44S+U4df z@@{;)h~-3q3IzvKt2!_o#T4NkLBzR++;s^nq`;57?-tdhvz*Zvr^s5l&OU^a(eCi8 zBEiv6pzwSI!gg(8(acU@?)D<7nPl$`vUV|~BIETF>XPN6RB(cvPhrAu1?9+W({gyR zJ$bUszY$x|MJ8$=mAV5Rl|3u5jp2pLAi5THQJzTRa`DxOEG&ddG`EP9(_;+C9oNC6 z*1Hl@O=ty+9%hZ=gjq5`jSRIp7Za?N} z;3qB@2qfY%7KrH2%>UVR0Qkf_MQmRm`9H7Hq+#iDr9bkVys$#Xp>MX;>jFad#Uj$LNy-3$%=>nVy&w`r~S*{WvHoG`5 z-@4#$6@tckfFZC}RH`9ps%E{n^(@i&CnDA(ra9LEKUVx&@v z=%+X)KqL%Sy5nJ_|juSM2E}HjoJugGbI&NOgs-^aN2d-9d>mY_xjL)BNkSyhuyG z>iAMF`*2)ZTv-PFu}$8|8IqvF92bohCVKOJN?^M}5!QIjUc&sygfxLGgcnv*=sTt? zt+-t0tN6abe=kphJH`dx1Z56{hAi$9q6LE^!bT!sj^v0QMcxA&x&bc-qz;N;J(N+; zEz{N2b?aQIiCu%@ijS-cX=%LN0vNdC_qwSgF3)%1)W+fo5G5gFZZ0RJC}LO(W+*vE z^#rl>*8)Ty>pTo#RNuDoJ!{lzi!$_n9o%9j#OJgYA10nRYPuzeDPhuYcJB9aNeS-7p;z>ka|2TVK-$^+lbC1h=~4Cwb=YVemlp89lEMOvAtj$@-~ zlr&Qc>VabDAM40{x`QZ%AqcI;Z%B?La$m&Ps!QGk{4#?5klel>tljwy_>hLnYrZ20 zOhyC}Q*=v!qDvpo{zy82X}Cro83_Zk0Wn!ARv2G4>`*aH1#LE!``0fnDJNj85@;Wi2L=YyJK)qND3~n@UIu6Lt1z%a{bu zbD`;t#cj+_5?*33{VL&SRM<_!p#+k0N_MH(M?rHile_Hba&RvMh?hxV&DRb8hMzMB z^tCGK5t|G0=zaTQAp(S0fTSKvAp2Z<0kfqwBNqlg9*_GSpX&jueYoctygNOAFfVkg z_{2&)dS8)mFHP>#a9rY9kduMRWhYi~mf(X+xU5k;60}FmCQQMT6>p?rA%v4LM_uYW z;pe{zdBg*XAKV;2s;1(Iud$#tgGeuQ851XSw>w_)30`{n38RyJ`FYiGl?~%3T)|sR-)v!a|CMHsh+C8 zqS-No)+otlP@<~7MAI0mlhJwvaw7K6crUm6mi|3sF;|k<3g7tTCY$COPJGj0m-y4T;g!N||#LoKSBPzKQF+wW*aZGKevL1d+A3!Q*{XG zQOll7Hy|nzrbqW{jN5v@%Kl3UjiP9#k`Wz5kU)kl}})4!H55 zk8KeZIGvGdhNZlI^7)Tn2TObzvZ;Lc(602`9hj%bq4_I9A-4Ez^1jeTn?V9iswC_q zX6~6-w5Q%}vOB7n-A_M#OA)sS$o7sRGU?v8127@jkOs6#i{?+Gpv}rWsR_Q2SQQ-G z;5wL-=z2N|^Ob7LD!OT}8o7}6fD9Ee|Ms_d)U=zhoEK%(1B}06|8Y2J7X?{C@mVAa zva!g)t*I@Pkv)9}ZF4!hFd;6WYky%+h_@lXIs5$i}J0X=8my5pT-QhEM*~RZPTw4*pm<9vSIAS&ko8-@L_l7SVhZ??ynatB(&z6 zj>3v8;$#5gNWy)~Mu$rq!!zNpr;6j`6bWC`tHj&2XkVrz6*6-o!v45|;;k z42o;qj>2}7WsH)37M`d|Fk#N*G-utKOWi)tQHb=r@u<3C&aQd(WXx~GzRR|dufwW= zH*8+~@cMTmn%(8*Y$it*!o>rDXcQ0cZ4pxa!26$r+8KgZP!~g31W@hWIlc$ZZ@zL? z&AExt3A>W?wd25I_Ru-n70d%koJr6G2z{46@~rWF+A@4i&GwdN*$RZ>u18$r#=0N&khdb{X`pBOfsiT5_sdk|TDgvDmR;l;>Fz=Pn zYJVNtM_5P_pL-(gW|$$q^tJNSX%LNmcC8X-Jo);%GG-xxqYhEu?XTtTXg>VJ$XzOv zzZhmcjb)^()r&5zB+>k2X^wPz1pM$pUbZ?ZUn!8Z1;f+S+0mrvrCy( zw6!!S-g{;+=YD;nw0DookFFb3`{0yLPomGSt+b=+%sl&goM%3F$${ze;mqsoR%RkY zsCuj+j3bu!>&X|NH~4`K>+p~lF`m!3Y3)z{aQk@o#^0q`Q}vm(&-8N|6FpQf=EfMJ zWUPI%Dk}Z+JQKPH+iqqzZ0%s!fQ8=aFCSj(s>_h`8g1XcKL69Gd=^+kpEqf*Jn^UQ zCqN{+#SNcZN^MAVn<9+4ww|k|R@D^bA~^0JO0-%{0c7zmWxZ1^K^TEd5b^eiul0rG zb-~C5x+G1zwLtfs#!dMTA5Wj-4>20%P73dgH`Q&}?X@5G1IcL0tWtpFvgB&_6y+mW z`aQRpaUKj|)}pH92hnFcvn@a#=Vmh!#bOwUKejhMTPz*_%@tM?9M``_Zm zix{D{qE?KcsJ*wKwMR>9ucG#jy<=BxMa>kYYVS?$U90{kB{?aLSsy~ONu1j&oNAqpX}yaAjkxi_WR0L&+38* zy+7AIMZDWuOl#HScVT=KzYnq71_T?y;4{KiTt9(-(TY+BwGsEsXz0Zz=3RHyiMtnoP&dr=>LutHYm=bJIG^v_ObsoAJHE&ub;dkGXp=WY z!v@Vw+O=H-s^x}8_}(?1cAV?2IMsfbd&YO|T~Y`ho=TS^9oR&#=Lfw>Y%&GaBlFz$ zTWUowx>|hqbscJ`ZJwOpw-cr1u*u4XAKEqBi^eBSVzij2N$Zb&CWTZF7K;wLN$mdP|#3q1bPFND08sBjYo&=wXHBodDc+Sfj8eZzF zJyd2uwf?+4G#{uUJj`3cqJE-1PLEx?%Nrn-^r$Z+)lzx9$sVdbtiqKJ%u27&oyE+Q z{q*hR5Z~9W!gz4zyG$oA(RFz{pB!PxvyP_%(nSWNroo?3O~}6%i=nj5_1@fy0u4Hi z)^p9lF=2_%n2l5NiFeZiQwP|*}yBR`B$P|v-=5tGiDV%bstJZT0= zpuuJwE~P{XZ#4>6&yrn|=Jqoh7;ch=zgQ6clkL{Nn9Q2dMG9R`Qd*#^d%12?C6J&T z9rK+F)76+gdZM*5-lGC9v22SAy!GVo&%54O?$>U=TS~~KAC81jNjZ*c&j00D#Ql2hx7<54Pnh79u&;@So2br@)`6PpS_FHHC@Yhm z6e$FqzH7MR-jaft5Bl3^8Ag%KM1t0eq6S8O7S-j8*jCospSpf)j3IQQ$sJHn`>aG$ z^;n$#XEqAsuqWh+?N{ft8?#N*>|(52*x}!5&o@9`Rbx}!uqcZk)LG6P(}dIF0lNvb z2I%?O=|bkL&jr`(#jylt7T-X#{yo8sq{n`iurJTKif{XoF?2hb?vFCxuP|yUxLUko zS|WrYKX@ndG9lE;TLVWUf`}ErJS@&iEC0CRm_DxXqaj^_eMUn!6j76{9JVr*ZXgQQ zsF#pe?u7Uq?ggoTej9Km_I3KliWn)tr0<84PBOw&O4Hz=>t`b~E5$c@N04-U?Q^F3 zR0pk!%#(K1lsxypqu7;Zew#4KwPU~tb`4vybx;lXpq$xgS(>7fh3rlneIPYvC_TXI zu{I&0(Tg&KSCSX3Hpp>R%8)Vg_(N?p@?(>g=VRlx`*yJ^k%WL6%Y&|TtHifM@>95B zxo`+!S476T)kD6-&zOIQTF8GwO~w8RGnEm#ElQ#ygCU{hv)`-ROPOh-mXw<5ggg)} zN!tD4O*v5GGC6eHLSU;0fDMKMt&BC4gC3Wo-dQ53{)V=wH(G`HW^@*g+fJyAIs{C| zccu8c-5xF9E+e^?Z>H;UmmeRHw|&&jMV*pQ{~P4@sz=BoA;cVM3@!p)Q_Wj_LaCp# zgMEPbo(xCB=vtKqQ?#J_S+gkcRZGH?rhBtI&;Z4s>6_!;yy8;hFCLujd^XGEy!)yI z(nZ5LrHS?7pKmOg+pRV@Y=-T+Rc6m=wUNlcM`W~>WPd_9&YuU-dvT|rx^Jfq}mn6yL0wk)rzgnlOAnG>D! zf>dyEN#uP;A=?m!$}go(TC*5R_%0n5e%Cab3CKQMvE{?hDdydW?7%i$DgVrxW2D&Q zcagsCl?XDA#o5>9;=DQgp)iehG|0;;!BB?Q{`In`_3-%;X?u+S~Cz+)8*Ks~WcLHMbSZ ziFtZ@Qf&Xu8dAIs0o|EC{skZQQadGIW#3E{-YEQ8)MRu3-U!YZR$u)sfB4%IhDSk; zZ0M7$lIY4uceHL$82`UZm^_L#x4Yd8Xe!bEUy?fFVb7f(aeUW9BaWewnPapf5@rkJ zmWh8ram=s%%FMtc0lTdynp)2e$v&Jhh9tBn$EqcK?|6ArV;N@0kwIuqeI8bHvJDow zJ|NjBG99AY@l$i=)BmcxOND3fNRW72EqzN|L=%u}>Tqu9+tX^0uUdf=eNegXzN@g=4Q~a$jLI)C zo~X@iplyGE(HH_EpU&b=?n@qS?i4t*X0kag-4l_oL*|ddLK4=Cs+%yGEh$MxgE+kyJ@tXN_1pnlRskPR z$~;>+&dyMnJJ*wLR;f4r?1~4`#^<>`!T}q?pR&YpxLKY+15YRdE4`M_vMDwjueUNN zM!kQ#k&pTxx=|eYZHt@hHee%*+i!}a6{JoRqWh#DPI#X-Fu39~Pxu394>3Sjzt;1l zZ}K+h`urFBIHj&S2rt8vn^qq0S4{3! z2_IJ1i`)Jdlds$_t+aX#s2H!e(%@__{0kFe))^~F%a5i|w3oee0v>06)BZu&zN?}4 z`~!|yK65-*D0eh>vN>yWpRedC&Zrc8o2{YNmN>p-wnbk-YJgR`aJ zKcxWt3K+jD47_1I`o8mUu=3z=g1>T%pXXtwMG8Y|_9}*1*It%(uVyG_FE5C^>aN373w~AlM9#X>r$-D4?6I0fnp#{Gt?z#5UzEz9(@dLM`C_T~5U5S1;3`;{``cG(h&a zH-%WuT#abStMV}@=m@U9-QX*HA&XA%H!QRZfKfjU&U=y9%eEu$LO|-pPx)*DUZ7QP zVR_GXtqyPu7=ycMU6&=_o&T(+K<6#1R!okTN)WO>t`<>i@yWGe{5!MgWe@>(LNAZn zwm`c^N$F~$C?vQBI$l3tERR<-On>mZPBxwFR|=uKwA^!`-(h`YRX<9KM?DR3p^pVa zk!{G<`we3&B@XhqfW7*Fz4f{U$LMZK7IyG$3KV_D$a86(IL@nkeNyURa=o=s`hItv zqPgeS?<^!_7Sa$b+7f)@opk&bRGD&Fo0dE85PV-NaeMM{kbNX?u>OZa<-3A1!oxXWW6>62(O$%|MSB(#IIN4pu(xNZ)qh8%do1 zUX$aAA(7lsHjK2&X^`+g`>zCd`$m`P?yrM-J9}TjGS&ItaRyAtv%m;S-SN@W;by>V zJcI;EiE|?Ry(@Z0RCIt*!wbN4;<;LsuHVsA*OyG}w@<6d$JM(sLrk8hB)!l`3rm)D zGhD-q$P%zTc}cth^v1g0ck6FEFng7aU`46J=aDK3mM7e$mUB@cR5S!;=H=#glp0E` zD$Aa&(_oMUs#8*KfAO_t>AkTP+^H+OJ{Ced+lGt0v zPGUVH3*6nX^`|vsRwpKdgZs%vu76IP1G_XW7D$t@us&d;C%x{N&|c zJsb$BHo)gE#q#+oze%7k3H-PCa~i!)lX6-bE~X9m)g&!KIKuEoZtzU5WO>)V`R=Ni zP~CSt#dcIRHj{iZLqH?$@$x1tMDOV#6Nn)3|Kbvq#+-)wMWgyElT0e(F+b6F2j^qZ zoBZrK#1g_{hGh%Pc;bq_Z(cXhCVgHrr##lY=0Ff*plStYYY(QkpO!x;i1|a2c;Sv5(N`JcZ+cSE8px?S6CC~d5y82D8B&He zp3=r;o6=WuMJ^lC_o!A)wo8=za-Sqy?^E#Ru7_wWoz8cRDLiMyStgK%j1rgCZg=j_m>mDR@2)jB#7^e;3I3dKeI@xl27HR zzvxvp-3QCu;hm^$yJVyXIU)<%sMchCsKKpy(rsjpw8N*};kdsESx6al1eIVBa{S zAhu@Rgr-H_^bN2SA^5(GL7oZ~fwOyo(^me|>u%CWHwvrYnZ&n8sqJ^0Umx3?P0rG1 zUba2#wHw!2E@%AS z*cfCiiLi7lH_8`}(W*WBMp&ty=BK#vt+Z>>^j&~K0HZtW6bKJBFXYL5cfC}Ub44p8 zeiBe9qw2&{z63rHrziI-k{|m{!Ug14GqUzs$DoR9aY6eaOEUcG%AO0BU4tBpG}*sG zYK{cZo8|t&2RqY4blQbDZTL`Q4)UOz^N;wZ*Td;)k={xSWoksV>%q=@GSeDkr)byuN)&wEW{u?zH{plYEVkPuiF2s2BLMk$!tRi#n-(XUdvPcp6N z7Z{V})pavG{PtKW0`^)?5>(h9ucRm) z_?bzxUi@~%U)r|%XpgevT>Q$61?x`h{RN{CUx0rhJJrImr=FFqJylg3b2%8XR@`}y zmPAg(lgOLa$1^-4@KSj!j7*{WOMX~gfJ0ATtuQx3!ZKE5(ptim%3OsU-$f`<61Cje zB;tb~%G#OBB?y64#X0X5zb?y)-+d>B;g-fz^pr(WY5C;TtKu^ohT!u)b%~d@W8bq> z7004N5CN!;OoF6U<0fC@6CQc#?LYlfk8t3bF!%C#fiS1>{a+o0$K{v3>52BTVrwrU z^{mj&Qe3+dLuN)J*98*j{>uJXhg-$%_XAm7(8Wh_-wi565bCeoLKAeA6TB^0M z%H{one_xl7Pq@;7sOd=Dd&sb%VgY0M|4Wi(pP`AR*V*ccst%R@J~MF$5s%gia6^TZ&%ZdO5Fn319QS>H={gC~xDs zUShCDug_7STG*lKfC}$kr&-M|a+(6ZSz@e+3m%$T#jY5JEN2g^uiTHP+$Jr5-<9j% zp|ZX}!r&SC1n+Aujr`liI>5byldr*Dj9*wtiO1Ss769!>agV*%p~$$$t2h!6i_zFb z*V8R}(G`N;Uxdk5F}w%qmO1VRa|@UamnP6%l+zuaiyz*$@gnv;+ptPVRo?PdJ(Yk{ z*c{!B%j!I}Q_|yX$*%{OhXbK z6#n*FF{ls6I&+jbo@utC`0IfF>BaibTO+QT<9}6Ez$pMD4^bd%ObR2%k^v@(#GkZ+ zgX17k95LV*=6Rz>Oc9t=LGVY~IEFm>2A!OD&Mwxk-{YA@Zl!nY_MeBT`{h;Y@aRNs z<_Fm8RbUKVD!21;>KhsL-2@|TUgx{h*AE=wQJns5in6HAUY^T_YlRDV6>_syX*>`! z?>NtP9*0vqMp~4I#@)}xeR05Ct6&vX@&&%n6Ay<@m@UXt(+P}>QI_N4Ies?GRzx;B z0L2!!{g=0Ud~?o3bC@n4-j}K2h|!>cM_x4md%-ALM}kjh2c}Q+m-L%mcYV1BWY`xD zvYYg7_#90*0(@@P)HHEJ?%AeqG{-yLMBxol;MT&?Vqh9??x?nFfKp`By=2(?sm|@V z{A+`e0NjVjDhOSV*jsDgT80B%0@tSz zB7^e+&T}r!^oQ20mJXw8;KVBnED|*=TtKURdp(s@l1f?%oS(@lDJ0&4f?yU=9^WTh zTVouOz5NPgkCI;-#}bXBtpS#EA2e7g@hX-e`N?s*Y&YM-Oz}~pB){D)3xW2zE#_MwcwB)ctTk3vuKdnUvwC@ z+IgzqKx+RbY|O#{*(==kX8*@7MLLrEu9KULB)Ve9G*GScYOnoak0q3EH}tmF}Y*O>hj6U_`&7l#=2iwhTvclvijLS7{GhOIP)4qbI-w37bw=mXUJ=cSk z(R`42q$J=3^*ZeV5BIuXjmC}p%JtSC1PSaZE!X#!W0&;hDsi{)V-gkSgOmljfV_GG zXZc)y)TS+Ii}OZD;6uqQiR9%BT>4GEH_l0l*HuI!9Yi<~Ry{Ss{i8AO&;xQC?9Ym6 zY+6F`A*y5L$%FA0Dls0q7%^Y^p;BYX@-R9IVGp3s#NuB`l>$2f&G}A@_<@3A`LPXO zU!xCBIfSD*no>gz`E5(ig#nwCKWKk7Pj`Qpf(U0_4j8(9*vLwUGX)YfVI=e%f>1NE z=KH+d5TgstcJoDpm7D3(&Mf6JHJVehE!HdjVE=JD@zBSV`WDL*!pCF6Ie7%CPLcQi zx;(MYu&r|T&9u(r+@M0r?=hPj{zdxz>o{?mUf8tAqWRMgQ;t@Fvgsm80eAq(i#Zkz zA62&S*etoVE^|6axS@DPZ*rnx27OP~jZZ!`DOs%Axt!Z4eV6i(DSB zkzn=~Eeabmc8}#GZjF9Ye!4-6VA7FzlO^ZBK`xQKRG-v(-P!#&S9NmOHX~-t&HOei`KAus zk|wX0L<|x>T0j;s?&!1Z4w24(MvS@>J`=w#0*YZ{!~Z??{qWQt6j#TV&%(u`v5>na zCE!}Qg72S3==AcGhh#2+_X#%opzep8G) z8leH|fjJZh4*|qbRYZJ%c=(!Bq?y=AvpQV~`rbzg3KmuNKf{)ED2$S9cO?rwKmK$S z4GZCONSrsn#T^JclvAE&`zFn3!5LJbD7%OuUHne^J%lW(K$;;ZfpbVfS41ARoMUtI zG)k+pt99%Q!_*bFOfl`H`fAJ}f@7CBF-9(fr};T!JKcKz7N_!|9$LhA=>*5S6ajCP zlSI(z>4|sHp8Sua66G=Xaa(2T9OM_Yj}eR!wMK*i5u4GSiB6)l$$Ov_z|wTBlw#($ zvO(XivkYW2n;+}Q>quJv{cd`WVxZX3e+JgPiVhRDgGDE}=JRDFe73JAWyD)RdvfYx zXtad#A#}HDm$%!SNsT(ms=#gxeiFS!k3?nh7>W%%f!?JDfz1rUa&~oQQi?<#4waZI zhlZ2oQ!06FR(m#S=M|E3(Lro)XNlPkIbQ;Ak&p9&=u1BDt)5o3yc$-nQUlj;ZDbpD zB~G;Qf#xg|x-s1vRBO&`+!50-Vk1Bwy`E6c+5eh%OVUz^NxXZW7xm}S5@1VV(HoKm{mrmT5 z8H-4cYgxhZL7{o{|EW@n?RLZBoRE4dZtH}~29;YIlCg{XQ7`MXowFp5+4I|>0Pgmq zm2$iSBl~C#EEpXb)BBKYWFfoGh*VvqR+Fw^Ox)5*#o2ExnXk9bmI|>vZHag5Qh(K@ zI`+bKo(03r{TLv9_vX!z5Gh{Bj7P2wg~8P@LTQql>YNd>LMNp<-J>|xC66}RO-DmI za{O$mT^v(h>yX@CMY2QwV>b)kE7DY&d8K~U5u)#1vSZAX>?BaBy&Gh|IREt#&}Z>* z96HreK7wI5sx;=`XSoLSd9d+PY$IX_RNtY9ZBT^T!P*{{zRiZhHU6^*3*n7CsO?L} zgn!J+`B@qWTE4`wU?hSPajNi{vZvmx`S{PWJNG3gvr=&*Q0g@*@_xGS%mj9>)N=c` zYuAyx7&7PG-ImX{Pm-vlPZjD#GQ&TUGbAOeG~w{)=Oq0W#qsGiK?pdQWA*wa-Cn<@ zs6`>y)yXd_@L!Bp0v*_iptn6e5jxbXeb!8E(sS`}PtyHhd4t^KDWo!8DZQY0{4S2q zPtz3m9$W6-rUFDmZOq2$pzF*#$WG;x9ntwIu82j=N1wPn07ePB9zoLfnN z%Z+!my~A0pGO?6ab=XZNLT@V6_Z44WZC9o5yfXQ_pp5+4Nesp7&vHu`d83@;=-|cH zj4g^-&IH`&r<)|~S1zX@iskwx-`@WzeOfCq>v8DKI2+QOjo7Yf={(cDl$hi0v{;o$zD@c=nFA$x(Bo&&Wl;j(?E=R9DcnJ1QI%k>vj~t$#B$o%fbQHAZp}<`d*%hG` z;}L@v9rl}^`t_F~uNT-A3c^V=maH;P{>r}|!Q4vB3o%`vpa@*KuQZjcKniGZ;f#N> zB(^WLY~NFMDaROsbzglV-QJiEHy5bMVESW{`I=DFegO^m-M(budJTo2(x?ctu-E8g0``=2lRy=`PCu6`zzUdl z*jkmxh}#Z(*Y8v4`Cj6+vOo$iLjjWW@=XH1une{M;=1)iplB= z>BSA9w4?28?p1<|jK%=~7((M;n68ew1q6!L4=4Grb-{l5%XMHak<@)N9~Ps(=WVy) zgGnTC643!LQp{o4Icb&2Jyopd ze)54GmW`%=CR636^(BBuBlo~ZS2b@mJRikWK#SMkUq^@i)+m!2$vQ^5HXm7p)M9#E z2#Cv)ODNv|3Yz}2iqS7@`+GBs;457sH;d|gX;vc<0d40%z@|n9Ej({E`~F8gZ~GGB zBi?^ACSnXk`IgneQ#6RgzS+i58&hsYM3lf1rk)sk3l{&CiJ*ZS@|guyF(I()A4-$3 zsnIP&`#XFCK9_|3U>0tFLmtFQ{cD;Or0$>Z!#MaNMT4aedhETv`HSjPvZsEz)^mp( zY5&cU+c&Z53v*J33~-vxcPXw!{;o&}j!$UmaqkZk^p`|Xhaafi=PCuc`iecNbWXTT5(WDwYB2GxjSEk9+#Cb0HwGPTS{)X^oVLm6ipfMcSL#2sDWX8PxvHS!V&=>yA zrh6To#1W*yIK~@Tp)X?n`@~pc+z#<{ME$cSR;14Rla(JIxde9mKSr888pppO@v&XB zq|T9*FlVq4@lUeuQhGRZ=+`Ev-lxn+1ya9uAKra$Oy);_Hb0 zT$k_yf~8-!Fo2riALZYhf$p%fP$~dwW>d-C_j8{aO``4`1BXEpC@!&N%xinW&$+7$ zTfR30M{7LYeSP(nML+UH?u$&w)0}4WUFpm#B~E0@ss%*Nad>$@!5QmqOR-JRmXz&i zDtKwZz+JTf*X83$#Hp2}yk5U?4@&k?iP#ciJaD$^v{S~N@hf~tV*FHvH3x+S`4>2! z;2(Y~0jbBKe@J3h&9(<)jI4f$5YOQ$aS>N%+99ol<6p>lH$K7uNusOv% zKN2T$u?PldhCKNs(ed8s&M?^_{6m9!brV72X!tMY47|K0m3-CpBqhu^A*aV=Z8SR;#7DZPoHzxq7_kZ5aqWN; zXEkTR(}g_5RBxn;;dW>fsn1l#O0kyugu(abV<=AF__fDB)-}#)6>d+v55r9t@MLj2 zG31lt(wYAI0{1^by)uqKxizFi)CvY0;lRA#KS`;WWCxL6H zI<S=n#pfQLeG#K`(b{V`PeYVOuHn3YfH3P4yihOd ztBO!-Op+f|ZZQ<_1ODt+lR$G<^MSUwJ~^OKna%Gf2N&)>=lmL*RaAC&1waBNfm+lC zuI@=!ojh%XCyuBw$#`)Bw^{j{oK%A~?fG@~C5?0_eZT?mmkzdcFtrwoxtXQ;LrA{) z5|(9+328w*n)SaxV&eY>iOqI^7{n_!Fi*$~#~v$kdI-3~?-J;*gKXvcR|m=!+ZiJE zg)sPGRA7NQg2HofE`B_{e$}hfLW^kc4;lkhue)7G9C28g+<(T@^6@+*(o{rzyc4{H zsJU3*2oQYf8Jxjxci4`X7+5Cs9u&C`6$&>gGn5&8JQDj&W2r@Q8ocN+N#Yb~sQE?+6lhli>GDE@tNmL?LLo#Ocyig`q3PGIT z?DSl81lo+ETEtla$!ZQw4}TfI3vOa;DxT~~)n%T`8+Sj$+3GxL2Pzx$vlME`HxC%{7twzF-zvL947Wyy zYdhy`JDQru6aQfD08~z3BnkZX;z{_GjVOv>y*{?Z2<1TCP8t0*ErC5{*&fQ1iRl2^ zH5%!peo=KINji119!beJDEUXo;n;Y7*s}M1}jIT2rLz`UU(+|HhF1 zf8~L=w2`5s4@2~Ki<-rS!9Oi<*?2#FV)sHP;3&%2YkxX{H?S z8L$YkJCFfBw3CyVNw3HzIl19Gwd{9M?&kUaz_o|I-Xd(FH`r7DQir?zjGNPjal4B;THA@PuHp8u zS<=&{tWoYjbK4(9-+27O8vu;PJBTULpQhNnKi59?PdPw&r*`XT$_`@|)N?Og>MiHY zaDdjHVLf;Q{ZG|{+fJXe+i_kAs036VkPTWBT;Kd1rE&57Tq;K{PEQ0z41|JN542F6 zDLs`I<_BjVwmXX$XSO8k-;h`g8!WwAz*6`^j1!GEOZwHz0kQpdIjN|pq(smH%(^Xb z2K_31I@m|=`+}CMYH8C|h~yR4GnB|3+PaEpvY*MY{mQG@$>8oBEom9YLp~IJT9k;@ z@{L2II>KwB)cQ8nJv6}jw38c8sD%nOgZa^Gs3gjYsg$>(@Xrb#J>^V%`I>9g1eGM3 z;#1x$biEAcN?C2L_R>=LexPT!^RN}RF@>w9YeCwZ^*?)UykNFaTR3;CKN5-mu~c_^ zZurH3W|QYsg%aJq=?Q~k%*)P2HL$ZSj4OXo4ItxlbkHwa7Ul;Dp<*SqJ!Is|I6iNB zyN!Fm5h=<-G$fIz)OG54*Hpm_Q84>1u%*9MJo@7+AQ)<(7K<>V?^jkVC2!596;b3J z5M{*`a_orG6dLl&y^c<60<=@59P!*A1@O?#uTSQOkv7ZaUwdDFSuR40s*gXa-IxPlL&D#Q?3|7SJM0a|YD*EF$$VBKM+p~CWt%i& zmtHyiWKP{q*Zht@s*%=(q9*b(^;H-B8@Vv&a-GL%@Tu~h^%t^yQ+HsiQ$iy&QD50W9|sf zl;h97HVOA+zgB2TXT{~B`g2!&c1;@enP;1UH!i6F{)(FvYH*klvS7nlLeajDl=%0b zHLfZ-LlFtX&JWPER9EYRRS`=;hk>K`z3nTmPbT6Vl>)+lapc{(Nu43LWt6f*g5Czq zHt}m1Gw*xq3z@MH?*SnI?Zca10SC0J&2Ax|J&HAJyPKf znr+lu66a8R+jSJq4Vvx62RgX|m}xhE*er_ydhUd;Cs>CTFZzbo0yWS;5s#8&;$RIS zx$hN)EIS>B%!i*p)MlPEKrf0}&3|6Y2?Vw`YMFplu3dP`D5gG@%h4 zqM>+{sIC82mBfBiYffOKA46r#Y-=LMoaWH7UnstAe#*5Xpj2qa_HlzzjrBI|G(p7D zSK&|gc2Wu=A~MTzmL&0LmhaTX9$igG%9O|cdg3j3XV(a{>;-VRBQd!-E;sN)EpSe0 z3Q)Q*-{p)HvX1Ja*7_GpI}@|^Gl45B?`+zO6D~N-sF%)L)IoIwE)w|Eq)&|W3PMIa zpKqsZLPVdW#4-1^mDs{I(fTRLPG3cylESG1)Sw?1cu+3Z+>NJ zBhh^wA418Yf?|_WFpm`hZ?d_XTHfOL@Hr_}p?JTcSZW7KOBZ3M%>1cM|I`@?5pGWN_vk0jJ#z`CY5A2>B5~wKt|7Yh83Pt&3ByN8Y)@GFWig@hsjg99fW?51D%{ zgI8%oZ3M5|r)Y^yxtl!p zb=E@?pNisem6)xiRN6?)wt%Sdx`VIAQ=wl-E^aEN;;&o_xr}DqAc#u4_JYuye8m7~ zWCj0@`NeUk;(JyW)Noav^2DUplQ#)CsvHW%?We3SA(d!|U%l*Hek3#X`O5m9;267% zRD}leIGUi49N`Xjs-CO|J=slGViuz8P8HTAmjv}dB9OX$o=gRk(<=4g?Yt+97l zMo{9tM~Igj@PC*gsLsH9unUgxloOF^`=;m}-`V99#nfw%16R>kVn@B-m|$DWCQkGz zXRm|5_6F{gE~BmmA}qljV~i23aNnZJBARJOqhW`9w1t65k=YuT30cva*s&=lY*z89 zmgr0DwHRxf;-GW5R25Batc1Myh-m>B!>-Wn$4od*_x+E)fc9-(R8#h1DUS}BuTD*g zce$4$u}X(Sfb*kFcyRR56r|IP8r$9XU-%@}8#$Nr%@`<5Vn=E8GBK0u?7OPyWwm%c zYISIk{Rm4w2u$5@21_PN7fF{zWk&4GMpiX$M@4WS1}|y(M6E()B>3Lw_C{BAPy|ey zF!YCc+QJUPz+^7-G1NSV3?|Cn>!>V?|4_1>bh()DEr6B{vc9MmL7w~jvs$zDm`Xp6 zj|3$St#(%T00G-;Qe!bhpOBbTS77|c`JuyBmRJ?r%adu}WWD-4FmO2;zHJ&(ZJ}M; zRwrxsQ0>xR?X1Q(7?vw_SGfw)=ug7&f!#X{VTDlDUgvrfB1?04!OtIvA1kS$I^-Vx z%Q@I98#6fR_fNe&Uo?5c-Fs{g$KTA>-16A2Yna?qWDlX5<8Q7%Hrkdo(5%PpXq)Qr ze^`$>oEI>_nH53!{E+7+lOljqW_@Tp-DKR%Z&vYMv?~9D3$ueatGHMA*CoAPTCX%I zUPCBuJns4^IY|p(ezLe4)1%QO;QQH3diHGXDD3C?ez-?d3ajFUE@SzEZ|Y9J-!35w z!wy>tlfU#D#*DbO>3|~u;)gvRRk~@A==Jhss=R+rhiHqAcB3dO)Cck`e zGI*07pgSnD*+tdbh+>8%h1PXO!Tc^{0w`5N4sR!OiO)G#fj&`-1u#infDsP{M&sPRiz7OAH{6`-u>>7qJ?O&9TIh7Aa7o5NWC|#hV}Mujj_6YdEVFAbMN(W zoE!2A)I{lgivBHg1+eEaPj<{9ee$KZAVeoo3LkQGo6n{n4ga$pEQ=ismvbb6eqC$k zCS2$Y|MK_O0||6h`e8fO%W=}sEfpo$b$Q{ZOydqC=+|dS#I4q%IP1O1lC`rxGw!7~ zw+(qjDI^4z>KJFoL4iK+$?13QeriTBm7+&^MZwK8Ope-xz$TsGUJ_^qD~kIGTgLHe zkg^8Qx~>|f6skqX**&XcK=J#B9(ENX;UI%8J*M3eT5?vz9dsh`ckt`6t`CaoZU7ip z0Upn`GafN3^G=}vAxqF%4D1-|HF;#!1M;^+#ui3 zKqs7^#L(g6IMlj6;Q0BdNy{U~2uo|@&}7F}=0{Gq`m8+lv2h(`y3jTghvqNvjcnE( zErixnEu5QI<{@RLU+l-Oha&{(qTtm|eS!1v+e&%$WT9x?=CwbV>!~$I37@eKgAZPC z?62367dpRqD+V8%hxfTZSyl9H#J?dUZIlv#wP8v?wmaj7HD1o5TqlI}!0cNz&!k)! zS{%rud|-ztF7}-PObVJ{&3GBVK`>wtzJ2fb$+3xm0Fv5}aX}e9b^?3>X-fo!c7k`n^OucrIgjHn3h8%#;;&@FJ9TW3Ng^nN;u+xNl@P`-p+-jk~2z z+-s0~4Bnkl!9?cMU+@xjmIEEE#qcTSCjG~bYVBBYDK?tWrWbrRtLY-L;X@uU`d$`pm*$>66`5>NWs2^)6i3B)9U@ z)IqBGr946oS7OjP-KA{!@~YULztbXM$a6WZz)V4{j2uc4R3=ez(%7L!O}$= zTyLRCc*rl5@;pgQy(_$6mcJ>3SfBW|mn*3I%Mmg}0x( zWsUU9KdpSh=*vxFdFJ>XH@!1G7T$+McgaV|?tiJjkFCCbnYI1h>_q~BFFZoSVL?W$ zi$2;KX5J;8!`__GHK^gL%%=u^+#|Ny8EV4H5X0B1kN<4e5X@*tWYv(NN#li3eHQM0>_!`X35U*cY}T3WZH1o@Yi6P7NJ`9N?OfN4oPwN zG$$BmSW0NRiANy}JHvRFjStr}k%->paX-aE=>@;pxE9kEViC2)yWjT_yg!N5)u0xG zh%~8yUwt566{7)Qlfuknhm5^en3do-g~skO`#v>9fk{Q4TE3U7LJ`b!Ll{jc^O+N( zQJm(mIcqFMA0`NbU>7m76&!fd$b^76C{E?2 z;S_iFVVZ;~Nqh1Q+?EcB7gJl3QYu|J{bcqp^rp`zW9(>d20wDaV(Y%ilU`iDHO$|? zM~ftNPX<$g|J=@zO-R2~y1b`z?ptpgs(`fJPQ0+JE-638{(6#=Gn$u^EimW3KLMD1x$s)uj?D+GK^pecP{M zoJ+T;0OcDR6F+UV&r+cIlFOyv)?_JhqSqK>!F}F&f0Rfut+z!giC}a!({)K-9`o4Q z)x1cGK7HhRN^u!{85Am6yU7ZPXe1bVi+KL#bulVSLlnlsL^Kq~PCz!WtxPa&(t$QM zDI`KV?Z-EKt&Qwh*zc~rUO@tXOMVK7g2>gW+<{=tPD5&K#Ae_?;dj+HzI6R9gxq+( zrnhEB*fPUmUO9LMvGdUIy3fA!{l|r1$$tAY)Ra)ptjqikymM<4VvKG9XPJ}EczTJa zu-O74m<&g%g`1f^zrNK9Iq)gF+l?+f3h3(SNaLeY9h+mbM4eK+v&WxdOn_08598Yk zCiTma;EJPlMLp(iq;Vpec!L6r1!Ls$6pgJ^i|G73`ydGN8ggtJp`Nj4k)3dg@$j7q z=8aZI{8V17G`%dyx~~*K3x4Y2O437+0S-YJvf?fp+Zcv{{jw)4!qTDf$Fsx{<(yYB zE$FzhZv3T7FE9_v7f-Ee;?Erlq)R6-^+cx7q(n<$qo7-!Qn5 zlNURzBXzCYlA~xj=`Vz>``&FPq4Gs-;r*|)D`WyZf(M?}Cln2uleI6KSyRXosm6`H+dRJUnq5Y}gZQKx0>zpEE72a>=GKP2_Z49Fm zG$SR3h6aa&qvePLg5sU+)vU;2JF8`Q&11Wo-v9`%}g|Usq)c1&lm0*in=MjuyEe~n> zKKZD7^2jx>&V$Ft!sR6;y!$h2x^f3GA~jGAazSUt_kQrJw!**QsZl4mm>KJ7BY}o8 zWBBS`73o7?(%{L{;whwJG$(orhOF@~ec}!6l2kTVFp-nWnBE08-K&N{<0%c!o{_Z6 z50Z+G{vVdkI;_d}ZQ~#<4GPjVK%~1yjF1k&phtIii~*xXVFJ=6Ac(XylF}heKsuyH zH>3CN`#awMcRYXGyPoI1ulqXB&ndQ$+bj{AruSo~#9rZ3PW{uO!M%NlcHjf*nfmf@ zFCeq^8=cn8ZAKKis3$MLK0c7x5Fo0z7x)p>zd=49VO zvO^fjn~h1_+RKWfTl=jZ%(^QUI%K@t&Cj|7 z{Z&RZr?v2j09r@@n$fxbnO`(u`|v`YQ=yW8(&$&I$pxqE~*j&$e4SbpH@2 zkhg@fQ+u+)5urn|k@%^2tNG&plMe))V?<0H8`OTYk9G2SoDQy7h_sBPw9?0y5m^NM zL6;gplH-OoaO?!1zJF$S-fy9n%y?Jz4!-qY)4twVufjJ72=!URADF^C`1aos77M3Q zYP&usr@4+%h5C%=v;*J9NOKYLMkk0nJCnsTYh3H~=nr>E~PNH4hU!AgyrShNX& znbFtWhc8FKZ-UCOmu(goa?L(sDZ%XuL^lSOAt-eoQ9fJR3zSGk` zX~zu}I49M&n{bJ@l&r`a9P|Xy@QT)d^%vIRk+ZQ{Kld8(`T5N^x>Lt$Bzk8=BT&-^ zQu>3W{NJRuadO2{fb%cv(>HbYbL*iOUW5mep1_0*UrBJfN!M&eQLAv}TG$_re`Wit0m@KpMl*}dWe9m-DP#^E5u8qPtVmeS zm*}npR%A(42204j{oK<%IrB@3=4Z(XH@?3;rweP%58<(?i+>2f2*L(6o=2mBua)}1p%dU9GzjhmYx(;5j}R=IjBbN z_c4+0)hBAf!Svd%t00!9g5@k9q{4?a;zU4yBD1``^DI&J+&i_aS64tPN>zP$~fmIfEH@%cu;%>5Ca+ z#mz$w$2>LHb75sft*U$@o4A|1b+kRNHnoh??4o;eGg##Hn(%(%3~>q1+@A<%PHJ$@ zQ;t)1iC)ulQfD2+OLy0s{)%-m?>Q051zH=le*llYmhu5{d1Ih)h5V37v6upqku#Co&4%PG?|l=nZ|vHGN`PjnLfySWb~<>c5b-phZ)_1}~L$ASItjSNCREpwR}I zQr(xUZg_t%6l*f$0?(R*-f3ftk)1lNLiarsyq+BI7ErH6;RW|jK63#f(q~X1th*J- z7d%6?zge!CHd-$qXlZBo?O0j!!t!$kt4i zfAodF$SITYQj2&Lv zRPAh|)u)JYb2l-ASK=&vIBrUuOK97h-}S{+F)2YkqK(x+zSum-Q5*F$zh$GH8twX_F|9YD|F=y;$5< zV(WN-Jlk@2YIpGX;v94Y{YBmXRzY?j0L#JmVEaf-?Hd)_J(A*1k$7iV&53Zvw;>2- z(xw8jyw|?fHL$XckD5J<9n-)Y)p!J=?L=ssP^Vzy#9eAOBagsl>SUi;Ee7`-2POZb`7x2CXo6}E>~Dwe-qU3^HaI@&xnZ)?I3oEp z-Z5~>wbQ$y6Mr2owVJqRT6{nKr=V0Oi;F-#U0gCb-gYXnG#&^oa#df$*8Sl!i&Uo3 ze&Xmtwl)I9&}HwA)nFw5n#et<&o7j@nG5E<2c!KXTSuDNj^L*r|EZobXuLi-#^UN~ zBT_B}XYx`k|4DZT*kbh;u#8%HpC$ihbt0E&8>-(_^78DFOLmqu*5?nNnGR%1W&OIu zoVtkQzEY>x=1)l`lVipWNx>cW)^e+9Ub*z!TA%BsTQQ$Lv_AO#etj?)R2dBM*8wzV=Fcp6Pa*PDB z)6Pa10!XoBC|tV8R#uPAcot|~y&8<>w8fE_Xb%}l282J4;(z+F;Z-Z>>ApA!AS3$^&T+<^^5;Y~pY|Wy;-RMYuIr7rtrHmIm;L?&EKjoQfB{2N@DUpoW z8sUr{gfA$jA`MU^zaVm=@{@+*p;oS!l(YjDB+6f}mo9f}w}hWc^4&2mWx&w_Ib2=v znm*x)#m!pOW%z}{tUOH##CnW&M&)Zq371iS(ESwfQ-e5mQ0OYm=2E2 zx`SCSB2_m!mHdg`>0c&qWGl^H(l8dw^pj@Hv`;IH<44qwKdoX9#2XUuA9H;YfAs?` zMsM17zFfflaIdkx##KqSa=KrB8s@IvHdI>ce&_$Zmc2y#)|bLT5jvRt?lypX_VqZYfYysR>OLC&xWj^Wi4#l zp(oq8_ZCWF3MeEpT}GFNEFJfEO*2h%8s~(ylOcsBe~E`X#1{wj%;WR8`Y3E*H>s#D z7swZrV(pInQI+{e9GbE5F=OiUZPq+O%II?2kB?-J(?~ChaJW0#k%d*cBTt)PknN&} z(ifPmjDJZmNc?Ief^l3MvHtGe)E`fyFYs54$+6z^#6M4>;~UMgAz4)LXge{7HkH?m z{AbF0Rk~*V(;q`QkgWBOIp>pFXOj%|y*Zp$U9?h6g#d0p5gJgrplZ|w1H59(AL**xNm!fc= zdIAl@XM?9Anz-hj@;gf4u4=d0Cr0Gms_7kMse=-ElVnUaw{@s?LmUI{%TEPGcj>SAOW4E^3NqDCEIcq7;^lk#bUQ^po5 zQryUJhLcieB;W%YQKQj4`ii%btfzJQc{h=Xwlr3IEh=t#9wUzo-$_NY_Ob3-dGFlo zVrP#F>DVvURw*XxTL8*y1>RgeY9vd=qb9Hz2g|o&$D0XqC&TSr?-$izRmF`eh`Qlz z8OLh{PEhQQPB)lnIuS|@%ad97$5PA-H>dDjn0@A6-zUi=4wZyK>j{G=@)m+)#{^mS zcfh;S(EL}{f|%qVnER!C`URW%Bt1+3;Q9!b*#3KywUDzg=+Q8s>sBpfYk31}P$r%* zFC8NYis}So0*D3YPnT*!XczbfPmRTi4La?W-aIXX8Zhh*iKxpGyq(^mx9dKiy$MGP z>-{RmLaCM%ys4gh`L4T^QUqRb>tG1G7U1F+)_eVv{CDy1lqrjnZ@khJNvR`04(Bfq z?sf3Ci1*5#?;uLmL}d7%5j-SZQ;YvC=~V5|Psr<(1Yd99hRQo$JIZ`Lh$4DjAsHQ@ zIV%aPlC1^6s-E(5&sId`75H#tQqs*I3u0=Cyl0{nc`>(k zZzcN6`cWgiF%lTj-cZJ}yz-<5kQ5X(1b9ed3(scXl75Ix>Z@*+iTK<`WUpbYz=z)E zWl`|TAZ}zHzHqu9?)q{1a9fjHz^$!@sEs5hYEA0l;(AaUfGQHo=!$HH+=37XqHX0H zKt}i1m`^swOT$mhhJ@QF!KyBFU$G;-57yW~Gv;QCAyv9=#0zAeas;4lS^#DQ3T;@% zWJTmg1R0|$@I!OoG9M|uouxxjo|lCmqaq8m=Ab$_X4iUI+4z2f2`&FguU8mB`Jq|elf z5xKs_pWeRh1>fU~SEtX{l_r(;tn_nWNl%+Glx)J*w7E9i+-r6>8Qt^k$lt_f9Gwuf z2=47m1SMBbe^`6|K==Crmuo@X`{_y|8Qu@GpEQitiDXbprF<2|$_;XF-QW6BnCFnZ zM@ul3b%oMXwz7E~+0w2NCH@NLwM2c6KmF6^$@b#ZCLKI+h z1$bxr^&wvlEwL_AWTrT7@uH5dj{6If3ev@pSYwUPxBm9l91yR&n|zzUkbMf!lfgAV zD)rA3Yr?FCl)WrWp4JtTWuL2f9W46PI`1e3ukaHDACEmSiw$}G(Wst9aLLleXlANYzP;sp{S9LEJn^jqRG29~ zXOZpbrr9~tP6;M(f)C3Zc9FTd5talsIi1f~i@Xx^(%9VWVIa+X_MNE}?cL+zzqrQY z7yK^33zH7lLEWiN;-sJqr}iD(*Kui#9&-m!S$i6=vEWcHMHax zY^&qS@sCCIbIYy!p_T;x(Gu8U45NJhMxWzv_S>j9)DR=y)db5aWEAs9ia#_EZXMrN zghJ9^7TPXR*%1{{sOjkE-E$$^-ol`_c8Unz&7`ThVj{f@c@Jd^iN=;5b;PF`0P^ZR zh}{ac+HkZgjZaJA+ZK5`uYwtA4FxUQ|DhVCc1G>*VT)bO$Q}E4H=vHt^bOxV=!v5m zSa6vTH{W8j^v}{Ud(_{5Gs(v39@&b&B%&hdowXkQDGe_O5NmpRI?$iLlmcwwJQ$=r zRXKH(@XqPk8Zeu;rkhccWt+n>qA}3B#JGP<^)R8;WGA9}TYL;UqJfxsj`x_kS!xIX zCIHUV^^u*3?ZZd_SA}CCIn9huU}UPE^R&=Fxj9J~Gr=AuuAKkasl~ipU&n|G+MUH9 zkd;5aASBEj0MnJ87&2aGJId|9v>w9GjTK_8Qjt&}oeXPpY1l?2-yFL{?(k(cyJivC zSgv=8%iuKJwrhFLV&2u4Q3XeT{2P-BT;%?Q-ZoSf-3P)BS5}xOF}CQcK=Mf3COTER zb4p()(JxE?n4HHWQpqtggswxUG3D5OA3cOuWij(@so-X;G>FaYM7A&zOc2#jjmy+~ z_2KZOGPZK@J2*&b3DdF-gewKwuv%q9QiX8XN=u%J=%}hEI1!_wATf7w-ni7{f+$8y zbGwefmeLr`4Z!AA{@mAWNG3;^HKT6iA!d8;cObff4pQFMP6o4*$G39sKjcod&&Pqa z(;1yRF%pd6;mCphsd3Cf&{~sfjkTp=JoM3~MXc?{hJ3rEnf!?dVen%Q@#0Cs#^XS8 z`q2Gd5eX>q6ZSzoA{mAIDpC(xoCIUm$08f|ukp{21sEmCv{?oLqc&{$qLatq_SMAR z9+RwYe{?XQcecLeoNPr4Y>RPSSy{jmu_JJq=+Divdo}o1{*vXG1f?8HqtS zR5Tj)e^z{?Arda{X1lX&bafv1Z={M7IK?>f^XW*~bJi62hdf~BqsDek^wV9UZs5u3 zXr@AH+1qx;$6&CL{4Ec`Nbht?GT+ybh6t%*9X0R7TPtAJ7wDCg>vdpfd5s5ddwp!+ z#G^FgS5!Oz<=>jN`Im*pL!Q?FZMFJ)+b_8Bb&C6LszwChH~{iH-b}Q+<})*^pWP<9 zqQn3?KoLH+3Bgtn2Xbi-V}*BC&=n#^h%GfG&jQpG2Y)?N%Z*Q=GOcu zya!OzLP$q+9_@$g!N|g<^0;+SHJ3jhBka9uxgR(dsk)Oz7T%yS!tjESkok^1w}=`{ zd$Sj%AI9x_Uj^+T(paR`O6da4P|60Qj+={MP5|{PuUX zZY0=taTz?&GKH^qnY<5hi-$%#>*~6IoKZs#URsVlMv*HiO>dI!4oY9bESmT&`gVh! zb=frS5la?(^_G>@Q;m$5b>BoPxhec%*d(>)s6mDdX918m2MGK?yJkn9pM(?C7r+i0 zsOX>o>j;fAfQ~G_Nc zE(=ujs;$%*Y2UZ1`dwcH{kbwaOUueX`h(98Qjj(?oY{zeJ63L}(x z@bS)AW0(wck2H|3YoGN`k*ii0I_&`WTv%;){tObAY=ap7+E!RU zi7Ek=G2YCNL4v$%R=N3eiE) z7qrBB6<4Ms&$VxY@1|5-tBq&b$dwfO{hoYnlm1}zd7Lk{p zu*Mm`=#?(JJ^GB}^yD{$6^WurcGsQnITPwSqvvhH$X|%HO7ZMuo@2HvDmfWkCjalQ zrHf#*c}-VplhO(3y_M-phh_F7mCWg;+*L#kCP!si`%!N;fC(@+uPoxB58glIzsmi^ zu<|Kby#r%p)wpSp+{G3%2LITfY{npWl|uxGJ6{Y6;l??161M8HKHXv7)sn}bz5AXi zC_j3&a$sL9@-^@$_4@-fQlDKZ^pM2J?^NJ!GE zpx~nHi*KJUEns<<^qt>gvwL-6O#S|r4i#LyP9wcziBCXvBqDkXJ{KXN!3xyg(0%Z8 zP= zxmyN}OgJVO)v4w)K@FAbG8@?O%#pq0#f#K^WA`O7ElSI-V}VlwEf3Oy2;ehA|IH>vP;=h}2IVI`7J96)t|!@-suu zByCZh`)M9#ZO9p@%LxYA8g_z_bcb!XyPfRLbXc88dOY0rjiCSOVYt!?A|Wx&`KZax zW~u1f>SOnw&3Fr#yEw9tEfL3^2}Y*!i+5hm#zM)v4Hw@B{`e-oIAi|TbFD17#1uiu zFm61d?aOtd60C^hIKEe8f7kYQzO1{A&pZO$ySgXnKB^GuYUASGKIO~H5WmOLTeA~K zp6OCN8AM@@F1ULfjk6peK_`SuxYG{+BW@1BXitDExr}rK*EjO>Mm8%)o1^K=$T6bc zRK+|`Y{&WFS8kJkD>y%$@%UR=wyR{s^`!WQcyk|&CFRG^ye}bH653P#ObMZ(sY8VFob!6VCdBh&#PRely(woT)M;iBXrkVNdDda)&7ZIaLHn9$P&22%lD4F zb!YECn(|N1JMM>nNe<(ObsH-Gv3yCwXbB2V=8}2uHqn78i(z{U4?!oL_eU!WL_b$g zdmqD?X%zBak-AT`{$&74J>Tk;%FH~@k~$m=%D4aFJI--UG#@Z%J*caJe`~s_?^c{N zj%Fq%ITQk8?Tq>RokYpq;cMth9G{UK-~F&kddH1P8jExX!Q3?vW*E=c?;CYenYiHi zf(1dg1z3vKQpw@8`mk`2wtDZZZhdLgG)4;T=ba!U5{sW31iMoUQnb;Hb0X&IvR|=G zdv(zI>og))dQCe4^tU3~ulR`iMtGD$@~H1cz`@!ecGc%_>c zd@O%>)C7(O0E14R(lX0sO~K#_s8Xoa$qgqP6xG?mO=Z+f4n-;_NTQca zX$0@C!Q~~!4__RB3JL44lI}aMNW8)=Cqowk;SHZkN#X`91fsU`Q=R0E=4BX7qpc=LHqE|46o&Oz%8VW>;67gQ>c1TYm#GkLL!# z-oJlptMul)oE*b&T{5zP5%-XikJ`nv)IVw!P1G+HU2SVk!#Q^f+e?h&DGH@&sdkF- zW1Wyr;j9)52RI7&X+|2Mgti}IjcuW7(;wNMM{5M#SqyWAJuDj)LrUn3OnSoOi!xlX zF<-Ya8u>OS#p`+j9}&~xpFCZG))u%|Z3Wtf!%xq;*oJM^@(dE?XU~t+?nq;)Su1T8 zdC_-%3B=%wSzRg4yJOL?;FCO5+YG@g-))cElCfJGuQT(N*1(n96_%bmJ;9qWS!muh zwE}HE`uEC1&sr;k``#4~wtc0??Lb)Kk^JF_-bkLpg9ch&{uc9t$gx}UmMfiv&fxXz zA{MEPp2dd-wAz42&t{|rO!tiHTA|zF1lXnm=jf(zD{#i;As}eD!4)DGB+Fx!6soNq zl;~(WN=k9!XOsFm9{o+Zj@7bxpXq) zd5w{NpeV;om;!aTNInS_mum%{VuOFp>E}&^T(<*~IK$;1%Jk`{CXZ@py0%tEH||)w zsn%LfyOPa%c?Nf#gSS>HK`zD~($?Or-`E#pJ#y_H)x_lH14fq~8K-{r@qQGZM4LCh| zc&N-&kXq!ejK}1pD{0G61XIik{=9hA3UxuPCNQv^4r2K33)DsJ%^BlQHW(gfeCWNx zaM}C5szKET$WP=}&=pA9!<}&`Xdr1I?0!k%{X_m)7`k-qOd+_heBB2ggkD@5QaA-> ze(P%6K2Kl%8?@|sZAl8+wv^{*`y0WQ>40SYf=)>*(^|W(!pyUZyN7fiY3>~U?39Mk zAIq*J@~3NP(=!g8T8-kR<)%C6j%wV+kzJof`Q{Jfz(t5eE}Wel6nVllgT%#SQll?K zd(jlVo+QGk@#!U(X5jHXIaq|3uCxC-E*Mw(X;b|8rcGbvz+^(!3rjGMOIgOe}m z4mkyH+Y?u~ftv#&3N^$iC|}Lq`%X#x8qTJ}fqe8ClLB++!^FzR2jK|#kZCuIRl`F~ zDSBxoL^TFO8(&%T;ZwXAms>ua4l`+6tzJ%D>H%~K2)IdI8G#V*zTkrnVm_s`sr33O z>h;c&--)|cYuG<~`?BlNt1S;I<`6-0k9Nkd5pxf^82FRMi_-ib-|x;y4tbW`PxVKW zcC6NOILI}x1yM{x?;l=_6#Gp~6X&CsLJS9mN#4FhyV6%^axc6q%c~0ZAenABy85ZO zybXGhPz5~uJZXhZBRvswrL3hC)0gDXX)S9V`0PKn1-d${2kda9$l-QB+n4IuKog!8 z-uy+lv;pS?D4u0WAa7`2pyMqKXKmy4u9> ze8dtw@mDyP)f?8h)B%_1${dYymI?M9q95dO3Pbr5&O;W7ImPAZ^N!D-t*)*l2sA7b z*`RAy47DHrhL!CD_W^?sDBEVi3fGBWXn_j9nv0=ro+H!F>z`$Z$D#QpZkCf=ox~TQ zJex|TMjYxrcgayNMaIye@0(*{+kSJKrXDge-P@I&NB7*{?#{K0jGYT}^vo6H!zB8? zH&W(T3J)Axok!0Ugp(fY-JJ{eMn}!Q*-*>7K=W03B}@x)hu=LMj-YKZcfQA^u;>U$0DOI zlF6jw?pKk_!%evA*J);^Lj`nty5If?cE;I1UTvajHo?2IFX3(>*-i|zU$C`Fl2*JlDv+R`8zJ4O&f_M}Cux7qAQZnd^oG^~?sTAB?s_P{i= zmy_h+2+^(!b$S5Hd^l^W9GcLcC!yr9dRk!BcZzS>uD3u1bx_L0w9J@H$iyJX(X zDed^Cur*?K@-?_0y_XZ_;R<2iGt=n*a`d@oEC?NYEyrjXnJa;;$&Oh{Jo0|zPkVh- zeaj-uk2p^w6edG{iPRX#1*48O9xx=^uHJr`pLHWP=y8_eylw!l#J2ng9c+fnd{K#Q zPxRganmuaa);u=*@Gh+neFsJ-M_=?y-XE;(F^fW~y))_&AD3EdS5N!psm{gp4RQK= z!hHp|J>QpgqgR%*UoIHD-YIPJI%+P$T_`x#QWZ*=fgWX*+Id`Ac6qjuZ+3*BgKC=rDZ`8yvogQasui% zhuR`nT{FjI#e`Wn!1X!wXJpyBGw1t&vOS*`|Am7VdlU1#I&fOd7%kIV>VHA8V+q2i zeI=1+0^ak!H^u2aZ9Oq7w0R5pgfgDLcJ#DIf$sONyG0BVsCg9}Vu>J+S1>Z22Ko9- z;=2&^{&uJOp|auq`F4jo*As~<`S6m5d&w~Nc!q3r*T*lU3r_>z{?y;_I#WZxlHmTa zDqhq4nLy$tZ07^1VJ+I)4mZ~}Es{%8q?X~4xCCZCDwLdeH7DObtzF0B*hnd9kX$*z zL))mGe(r1XYNUfi%IH1$!Om^tHKxw_0{MHvviYL@0SFUt2) z#&1cfzx&s3a^BJo!fs$oKx|iQB~5qWJ1Ns+}<< zD)gldm#KfA(42@4OZJmhG}{-2glll?(*AgOJn?=Jikq5tO?;+PltPu!L1E6rPT^p@ z22YNB*1U(4E#EY?AkxuAG+naoAav+{K;a^&u&jxoezI9|98yj?{`19B95Q4D{b^s@ zx#yy$dqm}tg-4*}>b3@Xh_~Rr{HE7ODc(z_iVp?cI5)ls{|!kh z%S!2)Y#LBG6h6U7u8QDTjZeXEk4QEfCvjArSl;d6ikoJfKdte3KE}kxrQvb7X6#R4 zC+|6+UqbF(RsE?V)?CMQ!|`WcB$sprFYCL-c;VPeRFLQF*H?pHRxpq?kwLUMtH!9^ zw*Cc}+7JF~idRo`xkFv72bv<-^e25m?up-d0gnbjbKSS7PeiZ9P)1L9y8Zo@|9);R za=JQwYMZbPsAviFnOT63=uOkAjp`-04#_ZhdADYNt8&SdqQzdpJ9-~H61=nf!vBd} z-})4f@p7F|nPVh0De|z!XhiyYRnHwL zO78i0P-+#$cu3lL_t%5}Bf~QKm2hs*L&HpTJi*%7T#*Z8Z((J!`tPk`=Is9#J+)x6 zqVJRC{;Y-@81VMlzXGqG0&=QxPiy>0JWnbCOrB5kIQP&QK7Fs=$Cl|WLk=gRU={=H zjbD4%Ogpno%H$`qD#LY#*XnRecfw@dGx}ujWUoK(VcM1hf5p|7d5FV(I z$W>+IVt2{Qss3552iUG6Wjbn@{Zy}9o>()nCE|4%!QaEGRVe;0$}E!M6J6am3cVyj zp^qY`euw&h3``B&32&Z!6a6OZMjfLX(M-0udxJOibb5x+%m4@ zW`8JFf?;h@sYLfGHmR%_nzjG3#W-*&0!ZKu50@qfm;~-N3$_!|7JQ-ICdn zXwABy*KFW_)ebLU*=y4xTrHW4lK`LerZ;}yBiw87oIF;NI-Y~F!gQ6G)nBs8YSvXx zymtzSj3WP$Emg9ED30dh^AcH`cydZ|>+}Zh zY>2js$(=nsgiWtm)Pr@`izzm(i5JwgscfwOj$L|S!%OcQZ(B8yjG|7kXCN|EqpV~JA6=S#JxvQ6Y<5k-(D72Q}>Ou2-}cse?HUaUGu1W zR*hP$f){D%zj9c6K5@cO%*POB`OC6}*P16?FI9|e=keV%aZ)%KJo-8!KU4X-DJj7Y z@inT=(raZBgc-I&|5N>}!8kFeXH6qk#{0v=IivjLu1L(D@o0{z%| zOwA1HIrgQU(rRQ>vD^z&SO#ab1Unh>`ILp-$B&#lj>#;FOqFo(sIjlbwoYHhk;TR8 zNjf78jc+$de?9JI3tD)zxz&_G8_6V0_*IJ|RQq)Xwg%kFXTM~iJrT+k*%$`8`C z`AJJl$4F^G$OfL?L8lRAXNae6+(0TX`c1FS&M-F!!_OBiZK-MTU zI$iX)PHve!bVc&vax3`2baTG0I#rM&by5?@DOlft)=SXxi)n*005PgxYw zBDhQaZu!{+jh}h~zN?M~z6%5E4O8fHExL^LM&UA*2rVtQ;;Z&HaMZaj5%CvR`TMIw4?Y ze{E)J866#gv9SI(5m>wH?JQT09AuW1FfyN~jvs@%qS-rPsx1{ZarSZ(4-4qyScNfH zLvybr0wAzAUbW&&$8#l3%ZXp>Mfq2*990sIO`m5o9B1yAtk=&{(E-|0$9+1TXMN*A zR@Q5Es1IihUg@wYq9y;SBeTroc_TOVk?>&ag;Rv*5#6;*7WvXZljRzWU}xQQaP-L! zV|bV_N`cFsW0&}v@kr;5x={V`36Rsz&0fIirSEvSL~-U$d=vM!9bxOC7ZM$l*to7j&r?0{Hx`|~APxp0m+&{q4u-_-ev;N690oG`y zc^zuE8ABh z!81CV##{sZOun0wq#qyTnyGEJ<6nyO2TCW(Uw#;>hTb1VV3~v*4ajY_d4G>|P`Jt}Pc1ceq|Q__ zDi0!_%6Nz+9X?`1p4O0DUEv-Q=%867{!qPiQq=M_QlfYQO3LH3r}h=#`yd7QIKD@eN^yhjt1PxByUdS6O)% z&{oY0+5O`R%1yxnf zLS7IRmvVari_reEy=k8yJZfLkp+cSrq_fm{hTD2RYi43Y6RYfs`vS&f3~#xT@x=eV zx7|w|c;M2fQ$O6f)R)wZ-SP?T){W>j7$aasp0>@XBri@>sL*_~e?e{MgE9kV6(neA zaZUuRH>}{I*~F(0r|X$8U#2 z3m;Z!J>;WNRjS7UC7HM}Fn3)fQp5+n$3Ab?1bxTx`MMhwbi z@$5OV4nnwQ_Q(FmgQ~+#iSi#_dn*CbGB@l4L8i@BU82@uRfMh6r*ps!X?zs2$h>Ja z+4CN|!Azu}$BvgPH?lZg36+1v?{ui2kX9OQYWnImXQ&ZMru7MMh*HnI`$LKm6p~E< zjkcdNs}dWK5`b*|^+tbjFi$mWVm9OhDV!0F(~$Gewh_rzt)ZU2>kK|!OcQ?j zb(!pVyPSJ1{CT^<1CqWt@M~lMxFbF3&%j+rm=NqP`L=a)J>RL#25gA~VD7v=IN~xr z3jJ>Qo#`{0K0dk*xO_ZaLaa&L{ASAJIKAVB|DzDrqS&q=-Obgx8#wQhX(Ws%BWaeV zxbnGD712*-j)S@p*7TL;{u|8}qk#IguTjx`Kz;cgS@>lkuDOhcUcWe7YFt~4E_5x4f|^`1-Otx3;^k0>j2Py0fs#Myz@E* z>7JwGBXluENCDrd2zNL!IxEF=;IbY1VmmI z=(sf>i=|nm1X99{T@Zc_wa=+wRii!gM8U4IH!b$f^^k=w--@@(n_k%@5E@)2O)S9# zcBF(W17AyToqLo1&012VebxBH=KCR2Cz!6cZa_iubWSUG3Fk5%`+nEKz5Ks|xFscx zvD}vTdxEZ4Dgv37D%B!xSv8yR%L~A6A-{X3RGJEeWkx`Sx>fXYr_j~NC*d+0a#gca z*Aw9u#@t5TI%JntGRTsVi->ifIsnww^{M$*$D*Q--c@T!vR30U;l z^wgR^+5e&6M zzgi9N^mU;+%g{5d%t+E33f$Z(#ZYOfOjqVjqF@2N)(!GKHc4)8ua`Ox6}oht(eUn3 zJdje%2vy$r<66(a~isrh_o_{!5mT1~2GY>{~vHGA!vHp%xTF@WFV;qKnmY~`-iwlpZN;z>ks;_^LZ>mf|bcK?qmH{#5Imv zQ)5Vs$LUfChJa~#mF)&%%49kSb17Mg zwAji{2~3$vV1#K)4MUM*l_|b2CtVQwrm$tkyIV~1PZHBt=oc@dV7QaZZl4_qj4b(2 z+6udA;JcOQKoECJ)6NCn!9U7;NG-yN>*)8Lzeu4lykMkX!|=3ktV6HIlFk{%Jalju z$QtYVWFuddk#Dlft~kO$3`9=vt1M6V7Tko}&TEw&&s47vV9)vc)X7Pf4(5JV?D9}x zh69R~0QS-nwcZ^mBHR|u3sz`%x+Z?+T@L7k^-)$!)SK%xW-^K2&i+cn*un)8+N-IBruYb*OtqO!b@NV4TTmmIr z-8{$x-(F8Mp@A(YB}qC}p?wT}Q5$hTOHu$ zbX3J%a@)-?fe?D3)~nW=es62B57RCPi{DDmEqI2*xfEtgRTe2x1;$_oSb?Nmts_+_ zI106CtcSoEl9c6cB<&CFK9AG}Y;h8{?jXH%MV}^{4xv3enqHWaDkT1Jt+GBAKC zwePae1t>P)NML(X^SSq;a!}Z->nVu81VWgg(ogT8jtd!&-yWUn2sqjs(aatVpPD-^ zTov|Omy{tm*^1Z>=}=}U#5Z?gd?Ou*44|O{y~oJ;MFMivH6YfJd0F3$=)B?EKoF)^ zVIR5M=By}FGl^u5_pdVV|38+#IF(|vpmZoXy1Rb!^ZlLw_Q$=CyK~QZ-Pd`&o)OUHSznnnm7fMiInhEn2{aSy zo3*?7CmM&PD^F35L`^*92Kc;GUvQ24&8?ZA<54RtR1CA8e(7C6cpLOra9aev21q|= z$y#v(vO3{_yE#8*c-lX!Fh=v5$;Fg!dI-u|wM2l)b-eph@i?<+*Y8>*@wH~ z^#`T#z$C+{?VCu2I<4GXa=*pM@w4i&X6Q*&buMo>{jv=bcdIwymM?gJfp#WTCFeGF zeHfF3)C5g+TRhG_9QGu2NG~?KL^3^sGyeM%qL*5yl$pk*OKI1edsV}wxP}5@hSXM7JW{IPNy)Sp2kr7Ytacr4Y|G z*;*!V8cSWZrBcN#l$5QLYmq?()CUV@@FdFOl#$KJ;gbMk2c!o;`qIQF70tdR zR7Xy^-ekMwu%bq7Gmh6m$4PeOQHb3tBVh=KJ0Os;@e@7B;ybX(4jHHIaUYOrG||IG zepaQrG_p#&6Ha=jkoA9>M-?{_Vp8Lb(2KE{O!%P%t`Cse|qdQq24Q3=EE+%;dsryJFd_1 zNLLK-+Q_B$Dyc;$LGf7^(x7v(BTCE}8P8p64esL(Fvk4Z{$*fA;+v(*Z_YUa+4=$7 z4&Jl<>mg1L*xC}!%-w>CO~pbR2TcyT4XL*`BE*n{ndfNMMxZnPY><^XEMC?Q3ei2N z1(5$Zxis!_R1AwzETyKtzlPWr1_^Ad-dFQ?&Y_z@qpx&e%K>9DKQ)ce=k7j*_8pPd z^?DJvxr4weE|_!fZn6rPC4U}GMd zNT<1;QyVGGhNc{j`O{qQ@$ZEKA|q0&Ik!@Mk7(Bi@H>@F+Vqsk+%;Q5hPj%4|*g{;R=T3!Mpw(E02E@dKYoM zcG@4inG`dF`n}sY*xC7yE5{A)en;DCmica<{}t6RlH2qB!-e5RC4Jb%1!FhaVW$dX z-}TPY1OKvg^g0{6T;{I&({Y>oa#S?)LJ%LWq=f8#);z6++&|Poa3^z^h**%FCR_iR zjLst6AV0NuxKeG1`^^7Zfnr~hr6uc1HqGHzQ-O%INPZi}^*9Ce`_h~3qqde34%FFd z{>$#dYQpq~Bfye$5wMAy+=)}esNUByw|4vt2mSjjOC2fh!@%kp?M!7KR<^bemz9%u z_D{qq_y(+HM;V)K^i*dYP2ny@R32jp4nB8Ul_zqhEhbKC_Ra3;1WdiGcm zmcv_^`Kc4=S7Gf{@FF{_w+-urL=)+;;S^0}^$D#GHI2T<;pacS=&-eLYY=s3Lab{_ z{pA}_FBt3i+*8e8hlcU9>l(uZZ%eA?)3>ihG&UwO_<*L|B%9dVAL-62r-H^(YX^fL z`0xP9x6|H%pu`1qfBRAJdgAd|Hu=`&df}49SQ&^NpSbDlperu9$op&Tw$-znC*m zjDJZ$0iC^j^D|-`J3i|$skCQbS;^f5fhCc=07gk1EH=KyCso!)o>vE&v=CTitdT-( z3+0oSYK(u6&$ZAbBc?|44?={Z*Faoh^C|o%2Bc@ISkyg!J9w=>fml{q-;ZujW@a%T<+9z@$bmb6p9Y-55kCnU>dx^ z{n5u$Eja7?LtZE#t>qCC{Alcba{0B5V@ve0w-e4fjdr8=?@PFiv|euJDmd_i@wWd? zqo%79(zg{B*mU{%lnyJacn8WEvxfP`zihW;H!RX#B?ZSi|J3RmDO7sGC1O+oUqUeD zA@=%*b`9|F6k}Hk8w%V!_G_b{;oUVM9dyt&@ricwr4y{I`$qolIu+^Ew>}R6zB88_ zMryQ&z4m6K%%47$U*YN*FY)~n)ieds;q0wr^P4wCQWrfr4^w&Bj^ST1GjW&SMAapK z^0iQFluVNKiTHsnUk@nM&r79KI6N@*;1w z6$;E`8~>}4{ik;8fWfN50oeY$xKV2e9}s95@qGwEaAYF*;N;<;Bo~1>z)bOzzqoE| z4l&CZs3CelMA&wjH&rQ{_~)CK@IWc+@xg=b%KDwkM(=NzY{H&D;{{S)H`Fd+(Hf&j@sgl&DDvZ|#YaFC&7|BEEd;$+8L@ z32=LDnUKbH;Vt6Gh)3VDX(J?Gg;47+vQ?}Zl}y(jP%$N+Cr*5$;+3c5tvF_F@zkV< zLU4rwN^U(-$TjCEF&wg;poD}Ns(wB;Wq%d7B}nPCbj9f6TSgsjCz%{C#&cY?p(wP6 zN;?|}5u=_6w@mzU^f1S3s1a9ia1B}H)vt9E+4*^>2tg}bE7{q<+&))o#+6`OFf7D{ zxcC;5kVeK&O~XaN1#P4dTS*XFM%BR9>pH7fAY; zNEHn{6J2_>9D3J|6e*@zP(8S_A(yp1@Q?}XWOu4BI9uj`sY!a1pv#qfLO963U3PV~ zT~)E46@{^q`$RBsDT0T{n3~5&8t8J6rdSqe__f&1nLw6M2gbP4daJ*?$n6{);yXa0 zYL)PvdFZtB-5jt4qdiP(BVDOchspzbiH!O)nPGh)JtO7Vm1{~*LuTBu00JF^c(yHBuwVtirl3Ogd(|Dr-NzI~ zdzf?W33uA8$=yrvmabgp$0-{pjLh|nTT+d8uB;JCdk(x^zzg8QQ2#og-cz7fx7mvgvv{p43S3wZvwY<8w9 zMBDsB;nP`LaG-bSo$1p&N0?EMg_9)>>Wl}I?msE+Tw?DQA8&l``}68$cR=PFGz*yW9FzakiL>20y(R|I2$YQ!Vh_hWu{Q%+Cr zJ+_-1Zr^0*JTTm*3*LD$&YI|ZPl;r?$ci^8<#UDLb6H*f;-rxpCEG8((G6$yN~*Y3 z7H1eZ0IY6NN9kA=as8+1AYez7kXmnlJN6hz$DxX~!uPhoUWVR1!On2CCPaqeyY*)P zjaR0YL!d2lzqKmviLkIo=w_Oi(Sh`S$x|XE8I*7@{#TLj+R79>fw$unBq`+4xDd#= zM}<)*^lAVgt~$~?((8cvJ*jwWliADXKsHCkNa)?**Pj&uvu+`OUQ>l{6$%f_w|Q~$ z)YK{TD|6M5{bDF4c3J!Il$?)U)`8b~AaBX&Q`O})^WS>b*y`-9w3l=)vR79#^b>L5 zrLQH3(u&f`gH>_TD;||@z{b;SriJ;FL%dInf^KTGyv7uH&vF1IR)B$;?0OFpmq!`t`{QJh4CcINBrPx%) zJqEEq&7s32I~JKF&DW?PK^5eAhbTdSY&QEzN5;r)v}8?bRFa^9KHL!vqlZVPrzCp_Rwa@Zdk0hzgFY3TvPr`J~+bqVTCsyJH4@=ZX)%75fLjYkX@y!+q$P;sHRbxnx; z$i4g<=Jsb`C0&hD-@6pWW>24BV7Lj<0Vk(EV}=>(tk-5)gP4i~CBUEwMVtQ0tDom& zWU-b%qbkrBiU3J5Oy%N5GZ~)whZIPTHgL;_5-xmGej7C*BwOr8z2_7&;&lYBI$zvN{aV||5R^bFIX66Hgr>Jf$Eo1ewVeUbAD5CAU0Zy zHMDYAqVD@mTmy_0X+v+=a0FMfRy8F-#Kd|)k`&O578dVd7!w2-DGe}A*$TMs&cd2K1#jLyC22qVb}i=D1pm)Y@d@6FQ%C~$!#eY#y+wTEVl_u7Yw5yh)S9BI=S)nrNPl+r>uG)GS8VzPWd3k? z$U0qBxwvVkP&L^fg6_Hvyo;mQ$9xvBihUo%2NsDvk(HC;698PF8<qh67ue9M!UJKQ_mdZbSIuF2~VGbs-m_(t823p zgVs#qeF4jTq9k=o1HvmdWc4B*%&%3}@?YDHweDY!x$i57Q7CIps{P7VSMnZff;q&j z_j05BnyvK~f27PqHRsQTdD(xvz5O?0BfpSAi4_GAe!6Ml1-DM0vUgRqhm;l$M%2YR z;Lmn_)Ct`=A(w6$(cuM@UG^}Um6WhBMqD>_;&cupSDGab37Ccj@#J3AsY(6M^x2p~ z0x{}OCM8@CAp)33A@t6xf1G}GKf)C^eQD?0uw~Q)homM<;Go-6);npp;3Q50a;{-{`jN1uPMr2u8(t%_o_bzTt2yqKRG5;5?U`025f_ zN3oNfn6d-MY)mwq-ii&G4|>tLhvl?IWH;1sP?EZR99n|CzRdL6Ilg|VTNW2<4nD~6 zZqdLHQp1C2t2MK3<=6i186@S`lNmkR3>{| zIwl%`?C`s8_zJfJO3zII6okqNh^+<@#lVD_!eLuK7Xn_ZXD9md-0o>eMA0F$eTbFs z2akyZcuKbjQ{A?MOqKx9`~l@4A{~=>7(pLpl;bmD1ks=h_vtK~hRZ4`yIXw&fO)x5 zVQR@7rio#6DvCPv8xz-WaLdX#(CxapA|{Yv4`z z!V=HDZPw-jEPPm!aILjmJ7;QbC6@cd+yV1Z%-PXKU)m7nNDfYp%`;Ql@O zz53jG8Ba`I&3xAgLGz)xB?a{MT;Zrfa)(OWiFl9~8Rp)z%{9M*r);)6{@A7~BRTo0&kJd;|5-0kNbE-ovc73Bz3 z{s+}v=lFa~kWR#uqYMAf+I;N=rHc}J^gbkLY9fU*uz%3?2w%ry2fZ4LeC8u8;4QM` zrt_!8dmUdD{*)V=I!$X~GmN-jsGrrY@p8l%jh3p(I4{0lL`I$0mvXz$yvpMleV0EM zAr!$|)aF#BJ^T6JxTna>L2Th_H{!Yoobm81f5fU8EdVhF2-?rG;2`!^M*D6}ve@aKooHYv1BVEsE3qJDJ^p3&4u4Q**{9w@V?fH_- z{8nTHTTkA@pB#<+jPaCk&6%zUCUS0yruJXF{E;Gq7SezHV?HPyW>o*heVH=udh+k3 zz1^dmtpO|%56M>`C?kSB4kH;{o4qAwcfs39u2l=%+Nw^f5!ArMP0FX$PS_J=zq|eRhXX0Z)LQOBRPDAKI9rP_9Z!ydPH$ybjSNW{G#|wEo6Y zKgFrmwBQJyuSuPK9tALnN}Vy&CJ*8Te$@l-aH(|8KzN#Hr)er)Q-O<#TtGQ`lZ zJ(K$F^40aMv8_5OG~{}OsP?tWYA~MWuj>ul*^~dJ9<$W0a>!&G=Z}e2DJ*n9`d(zX z9o4f&y`gjJ<|e40KnlPW?pOpeYD0V!6oR(0o$Hb5Z7T+E@lV6Lc#EpmIK9MvpHL?z z9R>~Xikf2-3-k+ztcx*I<4k(FZaZKRTPO&AnWw}45e{)c?pJ^BsKQ{ulwtOliKW+~%PnNujZz}VbwUmc zIaE#H#4>qmczrzw)34Jj8f$!*&N2Y9dpERg*oC_ROh&W>kH@;XzcLf3iVli-w5eTopFJ zviUJ$dS;%XSw~sEjx0P}Rd*|<9*ASG5 zP((?c)=>tL=+macFYs!>=0>&V)B_`}Wzh`D@M(1(zfId*&E6p|%4t=weDxyK*~q>n zKI_DLO4{&cSsT&~?h1Dt_8s=uv2pmx+kStqBZyUnBrOz1=j?_xq1Y7Ap0%3up=skm z(o??F+?Su2)nwr%;vw z1u|%MZX*=Rpq6ub@&(R$0gNIb__D-e6B$+nHrOdi((5|t=(4CsT}K)FQ%~oAP-Uu& zDCYugPShxk{O0d`=cGIz$#Ej&oh0$?M!C;^%7Cg-C_;C2;o<6z|6_$wx-|;;#BnMxKncHT zW)rxFA;^G?WJe)?!5ww|$1vp&ZXe4C&Ao$TPx-&8O6Nvfzq+OxXYz^#Z;sGuSlOB6 zQ%AFpTd#h=&3cwNTUpYF79Nta3IaUyP8Ye$M`jC{4f@iyaXQez7#F_vY-^UYt*h3eG6qO6KWObaI5seootSCO*WK;i zTX#xyR;9n=aBKf{^>Vj$uhrN0E#pCqz?kPlET4*EBIJv1SxH|aA4gXyrm}&OhdKSdysHkf8;CIss9+~XQyfA*g z`hl1_Ot)wvao~+sdXge_V6;@Sa~3y?SG-!mAZ^s+%X4e`3a8S(o<5i$D#(%YMAg`S5H7F* zO6OIpY?XcLZa>#zHw<5_ovhffJR~7ns^`;0vXT1)giz_L7M&t7>DHB4O#pdh@~l5b zH4DSOcrEE!V6d$(6&})4MdFsaw77Kozd1d?mpC0Rti+!bV)(nN_~iPf5Trgvp|Myb z;!U;9W3PhM86ca{rYB$^GR-?l%qD5r*-T|{1tbLSwM2)1Y{~KCTt$c90O|EhIY4&e zjUE1tlI4?nBeyTIWEM682Lz1Mr!~e08aRV^CiaUyxrL zdi`34u1#w7_7x*SLn`7wIBip)JxwAtH{DSF!eEP6Bca$Ph-XMn{ET{t*%}1RsTmXi zU&Tvk&*q~1@~wg6vCO&J{>5(P1kg58bl)pnDIc_jF;HQb2$^ZG zWC_0p)W7z)SKNAoW9a`a#-WCp>YvkLlCSaLY3p5`NqTL+A1n^P`inK5Xvc==)|5LCc~7x^%;MmsfPQR zDQIgUed|BxVkq2CA`G!rRtrh%HkWLOrdlk>LE#DnGnHw3GI*JK8sX6bn@%e%rUoE0 z9K-6IUC3hF|Nnh^G7ubqj0c9rTZ>DY_?u+yU z5lV@WjnI3B-2>OE%67AP9n(=O=YccfbP=^iwtkTS-}H+=Q$!Si$A#(NiIH(vPr8f7 z-OaNZeg^oOazTZUd1SyU%s?!mLflgt^Q2PjT>IgeQZ$_#E>A ziC*QsJnuTdsMYa+t!1l9Tk0qq_ZvqLDv@G_U{D1W!RH(PS(G25vKa1@VE^{0*xYT# zd==&whEu``+4@6my{m#myU3hzhrcE|LBqP??msDr^Mo%g620b<`qBYi#7 zy;HW%tK$Pf^LeUUElsR_4}}o+nB?V*1zpI^1(`KS6{aA0q8fFKFPOv!(k%V@dxq2* z<%gvz`39^%%tA+ClfwxNYW%gYyag;b&>51M>C>Y%#5I=fdrl{R%1D0Rc6cC!dX{1~ zzseVp_BDAd457Bt_vzcvay7=Rc+;8DoqVcwg)1e-U;1syA6F4lQNvDh>-*#WWKh2P zL=Tr$m=A6-Mw~Ze5`ti^Pmj=(Pef3U3A&-8^s9`-F8L z9PP;`1o;{6XENyHyHIO`S29FB20%Z?RCHT4#?q4F%r`@L8NT%tQcs6-{z)$uj^(myPG?{pqw8aeS?!Z+8mXKm_n-N zycV8MVVX@%Rn1mZCz3+P#(uDoEX=cpc76Q`42~5ghc02rA$u1iGE(IY?_PhUgdEBM z2hi9XhX_;pIC+L7ggR{m0=sGCjIfdX4E@pL%xrBJdjYA(544Ive9+9&4DpzRsa{5rh#}jI%=$9Nx1)#x-qTu}qJuN?0$2FM7K>62cgFx1Ir{W3L_i zKO-nuGMT?yZji{$IX3gmL@b#9-;e`RlM8Y!u>q=M8deg}Zm*-*Yw();XesA31F!;w3=bS1PHD%Bkt zuQh2Cz#W|QeIiK5SA-5yRM$}HWsdfrz9*I#o1;Cu`r>c5e&SCF$aZ?AVSoj>yqu^H zZFYPLGeO2INU1h|CzrR1x%lHY!az*lf4<%mF#(W1OG=jQt~ZsNoae)rP<=sd{vRQf z7TP|i;xi}+knZvh04*&fkjW3wfJ8O0?5Q#p`Ak>R92L}HT6|)ouEXDs+rA(;Lb$&m zwmLs~vn|ARKR}QhjYRTD^T#$Cpags3K~uTtG6a%hc}fZuzXTZ1aA_GjN+~d>t^7n| zl9`-X6_9US@}t$~Bsy!vO*Vi{Brzi40YE@W3AebaL?YWS3O(6l|F=XEyM-*PcP{1q zTM^?-A-+}%r7}!UM5K9Qh$IQ{Kx?WxKN@Y=B}@77ZxR`-{)4DwfIUI`;0dpv*EUl3 zp$*}Qdaz}=*p)`J>PyeB+d^ApXF4bLps`oTh1U@nqLm+E2e$-|biR&2iQ#Y8j^X~q znNNC^@_ps#%CE$_N9$I72>Dd9LTCuLqG*fa83#yo)G5)KQ^5ZgE^9C+MYR-)j z&hDyd|9hskKmI-q@!=DEJ(TpXkT!@wxc<9_lr8fY`*VPCpVWG&sO`l0^rsj=>suO3 zoz22*77~4tys;Y*87j;TeYzX*_`#f~3r7mD!}4Ke5v$V~C}UFe7&$|$qV-}1f9Ie(BpD2s5F*MB2?dWGnMdwA;)5G>w>aCcp ze?6uOUewJ~M?io;a;x$c4)gJ<#|w5=`N>Rn8rmDic0r^R`~1f=I0-&O_zP4XuqlzA zSu*~p!N-y#-CqUzI_vck%_dg!$wKMESUo@$8U2m|>X-@;uaGR@p}6(e|2GkxUR4U; zUozzxYp6AnLMQTzt{Hbq?YS~Eup4ih@U!Md)C3{$K^X1&hV1$*@$H-X6>MCua8Jp^ zgKS6p?J)Ie%f3%_f(K*SWRtc&$qHES|0BGxw(IIWCh>ij9B|+FE-Cra&v9-9m5qePzHQm)dDVcY>)(_358K)_9fgI)~sxR)d|Zi4fY&m!Wq3 zN|hodLAa0ErB+~6T(8zA1Rfg91AxT`kna1+*W4HMLNN?wm@mMnLHTm?H$XqS2$r$B zc#bZM#_sRFGsG-8>pHJKJ@eEp+K!p~3Cg zFp8VWB&^+(2chM9abdpq5+H5Fw}?SjfBO+lG`rH444`{2%UUSA%-ttFGkV+2QUS0e zgvD!nfl)?#gW6*{xH1e$nnIyk6w0HLxtwg{Qg30-5G-jb2orVmU|4xVaWHsZha_Rr zLEel#ua2+Z!(q%>I=zp(5pC<}Ihz-j#p>LS(ypbkJ=s|QtB486X6Lp1xbf=pM_|WE zMa5qcI(@$P1|oE1bZoLBFv1??K?*b0!75of{2QD7r{Wden|$+sik9_f-xz*?I5lf6 z+~>*2R&_7|78dbhU^cep5Zq5>bVG&-FvZ556A(YFUu;_Kpzf-oQ&p8&m|$pOZc-q>PVnHgU zvuj4du4GA(Y=6$*h(9bkJd@nK)Gfh{_t80uz|c*=L)@=?AY6iWzQYi_g$DIyJJUD? zd>8mBJ<_E2#F5Jn!Nj`s`5wErtc0riBzU^@bI@!JEmWO;38t;x0Qp5BNShmNo;iP7 z_3)SVxx(`zGJFZSNzIqk6RLuy0n4tKaALfI=KmVeEx-cuv%cz$31wvDk8eM57uZLY ztSq(%y~lWHM-356sr*-1Pis!BPoDqImyF8wNJraH8S0Mu-^SRr#;%p=IkRn7mBmHX+|Pj49FIyteITLFrIYCQv7E*I>5cHuY|@pqhdP(bZkcq~|-uKbslvMh6QGINrqbH%Y});3h)bn^6<~-Hl$o&lXg4 z;{^qkMn1btx9Z>r8f=;&pLo9w~^CVJ@X8CNBPIm+@wF%9=@KwyH#iQHBUM z52rj`*EsGb)nZz^TH1e#Q9k z-JYy|4KMGF(f_qmKB3zwYCSx2T1%Q^@-N49kYP|~q*rEHK^fE9y}5Ps=@KYm#f}ZG zr{H5>sk#^UlHxgC5aF!gj$e7cl)F=3sAtW7(7#(L_3l)n1>})nR1k(}!MKGT`C=NP zXguu;kLEXV@k-yRlp?8g;^A_G__eSNtKrX=?pu`I2m_{luzRgF^1b%|+E6b-G_{eZ z1@1DP#%wfM!WE(`*1iQ+9Bg9MRy5?|Fw5YC&@6h&pOI{+lICzC(|_HRiFk$S_)L1w z6+F>0J;F%z^`Z26l&iIyRI%WQkKI6f7BLGNh%zZZpy*fRBBT9=U^Ws=~BJ7zlk92e5SoOC6v zNB8uYRuuo0+3E|-KDHAr4%}HNZsyZ%HH9C7bM^dVilq#dW1rPh%dws7&FW8`n%4F! zRM|ZomW_Yx)rSOa0_rcZ7F;`T8sC_&yg9L^Xt`_)VJ)&8DRQ};Tt79v^TVzWJQ#+f z5$k^k-|_h_tuGcCiTHNp^xJ$PRrvdl6(UETdr{&*d)b`-R?714O7Q6d$mKxnkw#Cg zLH3S0870$Sb}L(w#Exk_o7R3)3YI$AYl$h_izFydrw@9&Lq$6)!N7{XYUNYIoACoL zl76-j)4LPXcSA7@=@_%nmB-G61+(7xR@ldFUy zY`Y5VAG2O9teYUpkNsolEKI4Q(bS_S3tuwoMiWk2r)aW{k3KT@%#gd^XPV2cH%7zb zeu5Km*#U-iuaJ5K>QSDp3}J{}KPs!^K*9mBondnr+*MJ@eprN9@eC|#Rj!O)6zArM30`9n z;sXX@f%^3p^0VK}q=&+?XrGf;iL2;F3?f<_5#7j+VjuITk3Y&u6M9Dun>frh1+mPbC}JQ zIULO1Zzj?j;s0ZBIJ)Ya?RIDmfFb;9EMMB&J`_r*iKHL`t*00M#g7-Y#x$TlSt}Fw zRYhhV^8iRaWZz)sVIJd(&5HJ_6{_W{58|XYz&Te z;}s5^x^*=UPWN!Y3O{`O)2}8*-;i2;$@A&|R!1n{o+tSB!F9hucbw?Z40xj=dpxI1 zPqlKj=?&(_FKfl?2;WW!zSC6=!=w5vbGN5nkW+)n{!%pfj0wIa*N}h_JXoOAvg5K$ z*0q*>*mz48idZ?z(-!G!KeC>QJo0jnD#w!ABs=qGx-IStTva!{jtK8m>|37lxzBXL zKYj-@!8g>^oT8jay!CH9Ith+sEt>!JeI0`<6GL-D;_SJ~a}d+sd$m+_|E3dn32s)v zocot8SPN<+tvXC`Cb#&~jR(7pPk72ZJNb;Yupq6F3r%3hBx4LWqJY}XZv;U;sHKot zVPM+Qj!urdHxI?vof`N zo_u3^bacP7Pe^kBLRJopq{+)0AF`R_@N%GD4MAz!&y0XKGK9of&*W;AvrG(EdA~Yi zB!m@B6*4`~d3&KjJdf#A#erl4Hk~BPuTrn;%Lci^%BEN$B#f5pO{P6)-wZZ9{Q?|| zZ%!knbrXe=wyIfbDM_|$!;z*YAi5JJZM*+)1J4ud2wY@}xq<}yFAAKH-HI=Ly{`T8 zIbv1mJVl&j$G{G(q-XC~(qtR(#2@hnSV@yWq9M@oG zSfuC8?#5Wqz#ir~qwn|jO}_^G+0@orKD(gf$i0hPpzuGeZ#%_SGaPTZW3#C0!Tww6 z`iwwS_!u`-Wf9cv!UmG>jCNF-!n>VX~!xF6w=l8kQN4~f=h zL*4S9Vf^Pb{)1sUT(4Vtw`Sp>krOi$Yx$jHgS8xNxkV-(cd5ysWXonf2DkZG>7^ZEchS0)I#4d@yQkjuxK(JdHqWj$!t6OLPL|8|qWZ zp&H=Dd(3H9tJgaWTo%dIX&3rQ5Gkzj3=r3)C3UjcJvWAN^W^v3;4T6(?u=C3O z^{W6?*z{!k!`c|wc}vQ>$)TV#wzzVsX1!NPY@4gy^4To?a0GS+|0*)a_@5FG-2YdT zVTWj~O#G6+v%4w9?v~N@39dm6UX-Pl|A!Zh7=@I6p=Jx1r3d-T&NqH;699C=)}mLY zfIYId5_FE%Uh7b4(As+NAIs2s5yWiHpEsoWq5^SdUGD1;#pNBMap))4ysM0*IM0}# zzRjP3U=nBO|9kwYLk|-zqxRSH%Ii=8sm_GvB(VnJUE>5pIq>5u(p?>;o!#*V4do3p zlfD2p#C*0)Xx^$*y#5$PR$t9}30U%RwpRe{%nRAz)u4Vno{(3Zi1C<#d#mJXK|fHR zLWNL1Of8}^_ub`UfiYpfW-FZJeC|s|`zOtAivfY>$osb=k4qo_7PF#$zMw1k36y9h zn{#C&J!RR9+_QB3#c0c0e=w68?kQw_TQ0TegQW{8;1ewA>@D5BT8LNdmp00b1fg`hJorxlEf`YFr-T^pMI=s} zRbr6fETgo`+``i6S{bvdP@MnT#~Wx`R(JG7C%;lB2v2pmT??0|HiaDP?eq(2AKW8U zw1aM&R~8WY;&^+Qap!0{?>FXgsS#Ebwbbs%Bn}m%aLKhQG4f;gTQ-$30rRdd4F^l- zgz=z)}v-g3ftgOF1fl3StrVaJn5L**{3!)G9j`AMiOw{}`M)#9593sC!KGTdX zJd7D0e~in5)M&@~3Xd1hbBY>(SsaFDY zCo9tf+!t79hCCi?2l28?8oO=~m`WNnU)skBVb0og_uw56dRWooo#1cEIpMYR1e{Fe zQj2EspNC2+?KMOXvsWG;NXXmb5DMQOKk)@Bc+s()_24vj@A$g}+{XRQnL^OP zf=)TQ0t2iY?*7xWJ}!TxqJ}K^)B3$E(j60%WnRzAH4aC3Bo(k1CDm9`eU~hp{XrCc zqyy@hYM`<5k$ZEb5&X=K=tLjPoX2Z&1HjWe~hJ%>cDI1V0C z!Gf;el^=U@NI*M+Ng;t!yS{vyla^1byI>^Vga&QO5l9)GNx}lvlRIoBcIyTIKO_6rH zPP0pl)tqWLql56`!B>Vu4c zPlAPX=%na(=JA(E^(jZ($cyda=Z!f7Z()f3JGn)n(uv(jS7V3Y*Nghstfuyr@EVTr z78&SGDkAr|LEVVa7be3MXeZ!n=EhQ<2123Qg)1M5TmtpjeJX?cTSEKEO0NT!GFRXQ z@V?XRK4ocoj)SJ%0&vbu`$hx;JSo$hcQ1W2t5UZ~L^bh|?kFbL!-;i-TOSq=bq5${ zUZKiq+HZch6XZ=J`?qaPo)fsAzr2v62aV6PocS3L4zCGpNq-n(ugT^tlDZer&NOt!Y2o~dX|BNs$lM|lTzaj$Toj;|a5c9E_IJh;N z?C>X`IOD9OlwTW`qu6cto$|NQdM1P!R`+(rp$G2ED0i>AdOcf&4VeLdnOU&HJcFsK zYi;FGKyrs(!HDUg>BdJP5k~!yKwxXx9a`fX-ka7y%rcB!cQu>%BuC$>vh2Tw!+^t4 z=0%0ErsCs+$--iuoaOG*qtw%g`ZjO(+pYE&^G*(HG6{$+N900C%dA)K6f9O8R(9XC zpcyZe%7Lmpv~HXIw@gjKvnhCM$j($9->V}%@tU~7)jbzFXsCAH=ndHel`7JtTjO!gfZBW`j(3d)f9y+pac( zpIlbH$Jl-9j+A-WmyWA%-7O?7dj?6PIG*jA)3^&S0!l~<(sjNzGXx{}e?oAweq-E# ztWud#yr)fs7+(+sFCZjl+5e%FgeZn@&a!m>ucoh#YU+Lbr$ZRADYel!P#OdTsSyIw z28c8$A)}F$8YPV=U859z(gs_dIvcb9eXLSLUEo z9C|8fN1uRv%<(VUjbAj$!x1TXS;UX+5mR-aN4eZcSYLUv;Z~Q@XK(X+S-6P--AR-N z8~Le`(XtHzQ-Z1qiy>wy@_fRc0W| z9fDoJ(qIx6RCb=` zjVv@KGwOzj&8voC<Q>;s$Ca99I~ zXMbj|UBq&9nqG z$hkB8LfEglAkvM{Xl*}){hEhv7N#rn_6ft0RbO<@VaMZC$+q_>a*g|QMu0t*d%f3| z&D*x_*;s~H&99D}%}6HX12dGbbORgg4lgj zE)2A`Y}kkbg`*G0&wGY`8DgFz{6qgQ68?e)xoh<(#+e zB%Zg#_BaVEh_mV9gd5NQ?UNJeuO5a>Em6hEJ29}AuT?BaCq8d-229~?-I)>NEP>9i zoDL-gD-}=8FGmE0x+>Nzb=_p&3Lc%61dhtC8P8vvakl__ls>C}jUog(<_yBon~~AA z?ZcL7s(Fq+RymFsLY}4vM+w83N|G7>Oi*6J7z+3s zz4GGsLlcga*EBP?mF3r1g_FWE?H$(v?!Qlfdqtv>%}ntF4~a?vi` zt3c)~NZ}-O8C%8R`eqUuV*MqFc=|rM*T%%9&&0JCGf;ym`-*|;qDgf_HBZKIOrJjk zWDZV7d&!}HX0Ci&nlfw>rKA;$G?lQl8mZmWow;yyUkN#>?PTfsFTR!Bb2YtSpP;>_ zQUFSeU5I2fwGZhNN)GhHkdr=~Acs4ic(-Ao2LmX{3m+f=Ep&6QWXL+1j5SE$oM5AR zV}N)d6d$ZXI5NgFOZ17C%|+9Gi3d;g3MRrV1H_DUuCkFMe=^sWl&%BeR}*lf$$!WC z^D8J2^M2iDZiA~4{x)nx2;0{(Rm|!vYSH5s4}n-tVTKX*Aaq$*8M!}&?~=5_WTUO1 z=t~k{g2<6xDjBaE`4>pf7R0@>XddRJVMF;TC6(wrdhb!0d|fkL7K{cTInO^vil6%e zIL*cA{)I?h!dBB4$)eX4StEt zI_|2f4ebfuo;g~eXfXGa41(b=trp+qbNWl0M(MRn zY=^in{kXq0a_t~M_T~55I<>yKYD^Atfe0}%qqQ-yUoKo z>vRvBS@9txySarLnO=Xo7W-*M>OKoQ$Lb`KBDBkwa)FTO^vHRJlLW-#db<3DRUBEk z-xQnD=0EAXW^!tYBAy5o5kEW3VNE0P=~#R!8{O1;lU)V&^D6M)#?B{9sw}m+bGUxZ zc13Xe_oqaw?J`ASx-o0+OCV%_>p_jPO{BaCvd2gjL>5_C9%6`% zJ!6-2;(79cT`zYnv%fzeTBvAd`vA1ZhF)vuX@+k8IY}tD_t2gQn4LAZ#3HHNXqc|+ z-Fd*T_W%fM)9NI5w)6M^$EU36v^yNovhimh2*PIMqDl;n3~!#72%wu%%e=z(g?z2F zH{W&~uFCXgw~^<~yjJ;D56K{O-M61UlnKx>*?KoweHg^w(<-wdT1hzeOeg?R`RSfk zF&)58L}a-H;X0aD0iY6IA4eRpDB7n2V^Y{xCE1}l>M(=lTn{jIIm8E?Y|iZo{q&tb zJz>viI%5r#^4UJxyQ_wEGf;Vt90Q z^Mrhl-#g2IQ1UA*f1l-ibhVzA`SxuV2zZ#aCl$up(!)5Q1SJxWmK!=nJ#vPv0CYsM!Ra0bdRV#`ae_gyJ z*_bu}J~?$iQE*iro#mzwrL{{sB2$)CsfrWEPCo@wBeRVk(1+PT)8A^g?EgywVNG3} zBwpn)Y(9PWtBsmHlGZEIM+%JpK*piowa-wP+5RtL5weFK@eHz19xc{(UnMkW zPY|NmU&=n1>Ar$xyDS_cN8H-{;v~bQFZxo*+y={N4hftk9`3=)`~f_2qA!$qgOlA6 z7M%yiJNgL9kPU`RWXUxo9Nb%;q5VDuv!M_)5o%H75%D{ja#xhy))L7Yt1^9q3l zixIT16mRA{>M0hr6KTNI;8MA-A~D`heRaqT)zU^Q0~qeNF{mNiWx%&Kk9rm2Aypr@ zEE^6bZ_Ik6u;FlA=N>3LaSYJ+-NZY`DW4RsCT=7W)KdlR+|IUd4sX7nkT=~_+?=#M zP9xw*#B&f=LG3TQ)Kx!-(wQ8Eb1elwnoq$K3Tw z_mSkxTAfVdIQl$CIz2~~(mmMl{Aouf;;`^rzB4F0`WXbjKIL*hegsTB zlw5W8UPGigB#BjU=%~5S(q^8gzFdKv@W+$~*wbz*V%*@z$AC;_`R4s66_-WpF(%nn zS9ar)t3lg{pkzb<2HvzTlN=Rw%@KLoecLey?sA=1QQj!Kra%l}gtNeW2{B66f2 z8ds5Mo+D315n`E&eNy3(noqi~Itw`v`B8}oC^MLq)wE3H~l5jMwIr*_FZs^ga zLqyyUKF zmc65h5i`xS9@^`IJ|<)v%N)%@H%S>9z0bt9OVKf>@ud0EyE4&Wk)LyizW`j9B1;EK z^&%}Gp}in_zW%b9FZ#heD?MY;!N`%af-ScAuj4+?3i%w>61>nG>eWmZ6jYk?Tax4{ zoiLnI)GV7vEpbuem{LU4JK=Cv0D4E7eSLA%=R#x|u9mLu$r*7RK{FBU89nrb84Bor zR`8UvcVl_EWA+vxuhzb5)oSfpkMbZ(Z)^CTtG;#xq~9GKx;+1;+&Sx5F#Y1j`xB<| zqxHYVzTf@$?mCoVD0%wKlAPHS3aKBAnB6*&OSmI$7Lxu z!$>rmV#{uGOmI5%M6jw#Aja3QVEjy#B}#12`E6g#I7|$Y9|x3V^K2+`Y$T!MNhShl zGRN<{-=^#|X~EYWbMwg2;j2rfmi})!X@TW@dD@kn_l@jqmcIZsr2zK$)jmpx7bD@m zE5ISmBV}oYesHc*k7s3`NZ6c?&w$lq?Y|t+dUJ@07b5AG*CePm6Q8 zzxp)=YUrW5&i-^Cy*4qQT0$4Z18PWVml)7Fn`K1x;nebx?rkyZ1I-f29U~~d#YU?B z1b4hMmY_BW)FdUDk5#kFXUPVkm!$9IWLG{HK+QfSS04x~!gO{8eq5}XG`s)5(I+1Q zci#{qp14rGPueCBr8?r(yE@zyQ2d?=C#d9Z(SctNrBz_|O#W)-D-DxFC&rZEyA|mK z&S_wNyMepTOg8sZ;6sHJ4YED4j^mlL^|hjmV7r_nArXB85ubK(olgOkm$eHK?W^!i zOMMCWr_@Pbt{r%iem)zeOnLp`e;2vc(r*H~NM%4(NdBX9fRip$j5u}t!xxKmnx9Lm zmmop6!+K7-dDfP$kG#G7nr0E<+Pdmw3jf+R*ToR4P30fIR|lP&c%#^mxQXO{xRYl_ zkzo*mx5L}+eTUPthT-Zs-K5Ackg-0S#@RP_Z|B@dCT$aMU9Hqg|EGcL-U%)5N|^tA zzH!{8C9hdGK9}G%=W@=KO(o6x!ba7tKCPrXf;_)0F?Emo)w8n9QdUaminNq}fT1Qf zm3qJtAq2UI0nIZ?&gm(;7WUdkW) zGJ1o~oebOd>`N|Rt%q6a0{C&i*g#|zl z>Ok2j8^*(jeVr5B5TkoZ@E^Q#@6h{>oSLiBXTRK4rJNMQ zo&ALG>e*>JXUjTHq^KG<9Q%Gd|4DA}MV<~M!xsL7c+y6uZny(UlK7$Nr8GG8b>8=_ zqV>i6Nk3DHoFY-zJ+YXiK8|%Rs-VjtS~rEX3d@L4!I>wxXX@$PbRKHe`99Hbuc|q~ zdAgN!BVCmmRjJ;uo8^ESs<~b7QrNDO?jppVA32_2zZN7<4Yvx?dhqoiX10u;kGLNt z`;l}b%|%%$1+>iB@h(n3s+fjSFnULLlJVD`a(jQSbo?>!eJ{d%O!d)I=&&9IB(Vkpk0>7XI(Rz(+49p=dqL?NRPYTjM0seShURss zc6y~50;alcmpJNzj`mE44zb>z>luuWZ<&FsNWhnJG&Cnp2I3>Zo9R7)o$v* zp%*ha+=`XPeyTM(nVT1jQ+$;$2PH7ml8`)t3hvB^-LS((iO0ysIyf?dDoH znC=S*ZA<9`taPt`c26+&`JKsU**l<0%dja*_`7e{W^(B$eZLE)I80~NYbF?GFL;ai ziOt+G;2x?bV7>t2j(*e$-nfJl=g#*9?Y4G=MDIezew(DMzt6!#Vt3-^e~8)8I77ho^41kDqzmG=siURVB>`!wV4r{ne|RdCSCwyWjF_8DeX4=sXgv3h&JiQ9FW9QWvnaXd3&k{2NSu?#z?2h@Qay zk(hr3tYm10Br9i2<`T;9`~$|{PF(f#)(soAAd#3K>ZW^hmBP%wy%H`6*5M!PwkeyG|NZAs#qU{h8T>Ca@gNOcuhIB zf$?uxc(Y?kW;5!x_A=%tX=Y@5fK+_0X`$8&dwA9@tFs0q76qsRYRi?0{8==LbQGv7 z(`?T7Oyor#!aWioBo*X$%=f_h=-hV1CqyQu;D%I_bZjny}`=$SH-Z6+yEB z;@vKsl||k7lb&B7-?}WW^QHNk^r3B9i+rc^qDA9!XkS2=Euj9!o|qq^4?ACR#!Q6U zT7;3vNd@0pxHT3lq`rx{+;ntd@z}kmdTYW^=(T{vX2mD`9kd=PH7E*bL;7$V;RWSm zg)$Vog>dm=%FB1}QKd9RVqYf3mGAPCHW30U42g9NodoOw$z}!}3NIdHIUNN2tgrXl2WeRnC{*XA_2KYO-J=AftT>2)_B>r2f zdB<`9R3{csn327VA>&$~8tCemA>$dOr)a2h-POhI7fZT+Gc#GWJZqQ%d~%^^<+vC0 z9zUDzi7T1K7x;VYK?XGllN3KYudC*{FHoNI*$)yNat}tk(jUAh^#RXKF9(mY>L$C& zlhIe(ipmVf|~ZKX{A)d zA%_0a838MEs{uB>E!b?k|m@TfC#~Ce}5=aR)ARZ7%u_s(X&X`@_GRYljQN& pTn(@nDG_1{K|>@Rfyj{Sz?jmHM^lCeqy)fALsdtmRLKJM{{WNHMj-$I literal 0 HcmV?d00001 diff --git a/static/imgs/v3/mesh/istio.jpg b/static/imgs/v3/mesh/istio.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc2cf4eaaf5890384ecdd3e42d45e433b299ce34 GIT binary patch literal 44471 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≺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 Q-Jqj7y@RFun%{>17Z=Y*nE(I) literal 0 HcmV?d00001 diff --git a/static/imgs/v3/mesh/mix-meshj.png b/static/imgs/v3/mesh/mix-meshj.png new file mode 100644 index 0000000000000000000000000000000000000000..be0f5ab75c8768d3a1776dd3ff5a536cb27650f5 GIT binary patch literal 126151 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