├── project ├── build.properties ├── plugins.sbt └── project-info.conf ├── version.sbt ├── discoveryx-server └── src │ ├── universal │ ├── conf │ │ ├── application.ini │ │ ├── logback.xml │ │ └── application.conf │ └── share │ │ ├── jdbc │ │ └── schema │ │ │ ├── mysql │ │ │ └── mysql-schema.sql │ │ │ ├── postgres │ │ │ └── postgres-schema.sql │ │ │ ├── sqlserver │ │ │ └── sqlserver-schema.sql │ │ │ ├── h2 │ │ │ └── h2-schema.sql │ │ │ └── oracle │ │ │ └── oracle-schema.sql │ │ └── cassandra │ │ └── schema │ │ └── snapshot-schema.cql │ ├── main │ ├── resources │ │ ├── application.conf │ │ ├── sql │ │ │ └── schemas │ │ │ │ └── h2.sql │ │ ├── logback.xml │ │ └── reference.conf │ └── scala │ │ └── fusion │ │ └── discoveryx │ │ └── server │ │ ├── FusionDiscoveryXMain.scala │ │ ├── package.scala │ │ ├── route │ │ └── package.scala │ │ ├── config │ │ ├── ConfigSettings.scala │ │ ├── ConfigManager.scala │ │ └── service │ │ │ └── ConfigManagerServiceImpl.scala │ │ ├── ManagementSettings.scala │ │ ├── naming │ │ ├── NamingSettings.scala │ │ ├── NamingManager.scala │ │ └── service │ │ │ └── NamingServiceHelper.scala │ │ ├── util │ │ ├── ProtobufJson4s.scala │ │ └── SessionUtils.scala │ │ ├── DiscoveryPersistenceQuery.scala │ │ ├── BaseSettings.scala │ │ └── namespace │ │ ├── NamespaceRef.scala │ │ └── service │ │ └── NamespaceManagerServiceImpl.scala │ └── test │ ├── resources │ ├── application-helloscala.conf │ ├── logback-test.xml │ ├── application-test.conf │ └── persistence-postgres.conf │ └── scala │ ├── fusion │ └── discoveryx │ │ └── server │ │ ├── util │ │ ├── SessionUtilsTest.scala │ │ └── ActorTest.scala │ │ ├── naming │ │ └── internal │ │ │ └── SniffUtilsTest.scala │ │ ├── user │ │ ├── UserServiceClientTest.scala │ │ └── service │ │ │ └── UserServiceTest.scala │ │ ├── config │ │ └── ConfigManagerServiceTest.scala │ │ └── route │ │ └── FusionRouteTest.scala │ └── akka │ └── fusion │ └── testkit │ └── FusionActorTestKit.scala ├── discoveryx-docs ├── src │ └── main │ │ └── paradox │ │ ├── _template │ │ ├── lbHeader.st │ │ ├── source.st │ │ └── copyright.st │ │ ├── releases.md │ │ ├── design │ │ ├── config.md │ │ ├── naming.md │ │ ├── architecture.md │ │ ├── index.md │ │ └── concept.md │ │ ├── use │ │ ├── spring.md │ │ ├── index.md │ │ ├── akka.md │ │ ├── quick-start.md │ │ └── sdk-scala.md │ │ ├── api │ │ ├── open │ │ │ ├── grpc.md │ │ │ ├── index.md │ │ │ └── config.md │ │ ├── management │ │ │ ├── grpc.md │ │ │ ├── index.md │ │ │ └── config.md │ │ ├── index.md │ │ └── json.md │ │ ├── deploy │ │ ├── docker.md │ │ ├── single.md │ │ ├── persistence-cassandra.md │ │ ├── index.md │ │ ├── package.md │ │ └── other-persistence.md │ │ ├── intro.md │ │ └── index.md └── docs │ ├── tmp.md │ └── Fusion DiscoveryX.drawio ├── web-console ├── static │ ├── html │ │ ├── 404 │ │ │ └── 404.png │ │ ├── loading │ │ │ ├── loading.js │ │ │ ├── loading.html │ │ │ ├── loading.css │ │ │ └── webpack.loading.config.js │ │ └── loveOrHate │ │ │ └── index.html │ └── assets │ │ └── images │ │ ├── aiwrap.png │ │ ├── qrcode.png │ │ └── logo.svg ├── .eslintignore ├── src │ ├── components │ │ ├── Pagination │ │ │ ├── index.less │ │ │ └── index.js │ │ ├── Breadcrumb │ │ │ ├── index.less │ │ │ └── index.js │ │ ├── Loadable │ │ │ ├── index.less │ │ │ └── index.js │ │ ├── Clamp │ │ │ └── index.less │ │ ├── Mismatch │ │ │ ├── index.js │ │ │ └── index.less │ │ ├── SearchTable │ │ │ └── index.less │ │ ├── Highlight │ │ │ └── index.js │ │ ├── PhotoSwipe │ │ │ └── index.less │ │ ├── NamespaceChoose │ │ │ └── index.js │ │ └── Chart │ │ │ └── index.js │ ├── global.less │ ├── pages │ │ ├── config │ │ │ ├── constants.js │ │ │ └── management │ │ │ │ └── Detail │ │ │ │ └── index.js │ │ └── App │ │ │ └── index.less │ ├── router │ │ ├── constants.js │ │ ├── feature.js │ │ └── index.js │ ├── stores │ │ ├── index.js │ │ ├── GlobalStore.js │ │ └── ConfigStore.js │ ├── utils │ │ └── constants.js │ └── index.js ├── .prettierignore ├── .gitignore ├── .prettierrc ├── index.html ├── deploy.sh ├── .eslintrc ├── babel.config.js ├── README.md └── config │ └── index.js ├── docs ├── paradox.json ├── css │ ├── fonts │ │ ├── icons.eot │ │ ├── icons.ttf │ │ ├── icons.woff │ │ ├── icons.woff2 │ │ ├── proxima-nova-bold.eot │ │ ├── proxima-nova-bold.ttf │ │ ├── proxima-nova-bold.woff │ │ ├── proxima-nova-regular.eot │ │ ├── proxima-nova-regular.ttf │ │ ├── proxima-nova-regular.woff │ │ ├── source-code-pro-regular.eot │ │ ├── source-code-pro-regular.ttf │ │ ├── source-code-pro-regular.woff │ │ └── icons.svg │ └── icons.css ├── images │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── mstile-150x150.png │ ├── apple-touch-icon.png │ ├── footer-background.jpg │ ├── header-background.jpg │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── akka-icon.svg │ ├── akka-icon-reverse.svg │ ├── manifest.json │ └── akka-logo-reverse.svg ├── lib │ └── prettify │ │ ├── prettify.css │ │ └── lang-scala.js └── js │ ├── metadata-toggle.js │ ├── magellan.js │ ├── page.js │ └── scrollsneak.js ├── scripts ├── travis-test-scala13.sh ├── travis-paradox.sh ├── travis-test-scala12.sh ├── dockers │ ├── fusion-discoveryx │ │ └── Dockerfile │ ├── postgres │ │ ├── Dockerfile │ │ └── postgres-schema.sql │ └── cassandra │ │ ├── Dockerfile │ │ └── docker-entrypoint.sh ├── publish-docs.sh └── publish-dist.sh ├── .travis-jvmopts ├── discoveryx-functest ├── README.md └── src │ └── multi-jvm │ ├── resources │ ├── application.conf │ └── logback-test.xml │ └── scala │ ├── akka │ └── remote │ │ └── testkit │ │ └── STMultiNodeSpec.scala │ └── fusion │ └── discoveryx │ └── functest │ └── TestUtils.scala ├── discoveryx-client-play-ws └── src │ ├── main │ ├── resources │ │ └── reference.conf │ └── scala │ │ └── fusion │ │ └── discoveryx │ │ └── client │ │ └── play │ │ ├── javadsl │ │ ├── DiscoveryXPlay.java │ │ ├── DiscoveryXPlayWSClient.scala │ │ ├── DiscoveryXStandaloneWSClient.scala │ │ ├── module │ │ │ └── DiscoveryXWSModule.java │ │ └── DiscoveryXWSClient.java │ │ └── scaladsl │ │ ├── DiscoveryXPlay.java │ │ └── module │ │ └── DiscoveryXWSModule.scala │ └── test │ ├── resources │ └── application-test.conf │ └── scala │ └── fusion │ └── discoveryx │ └── client │ └── play │ └── scaladsl │ └── DiscoveryXWSClientTest.scala ├── release.sh ├── discoveryx-common └── src │ └── main │ ├── resources │ └── reference.conf │ ├── protobuf │ └── fusion │ │ └── define.proto │ └── scala │ └── fusion │ └── discoveryx │ ├── DiscoveryXSettings.scala │ └── common │ └── Constants.scala ├── discoveryx-client └── src │ ├── test │ ├── resources │ │ ├── application-test.conf │ │ ├── application-local.conf │ │ ├── application-helloscala.conf │ │ ├── application-local-2.conf │ │ ├── application-helloscala-2.conf │ │ └── logback-test.xml │ └── scala │ │ └── fusion │ │ └── discoveryx │ │ └── client │ │ ├── NamingClientDemo.scala │ │ └── DefaultNamingClientTest.scala │ └── main │ ├── resources │ ├── logback.xml │ └── reference.conf │ └── scala │ └── fusion │ └── discoveryx │ └── client │ ├── DefaultNamingClient.scala │ ├── ConfigClientSettings.scala │ ├── impl │ └── ConfigClientImpl.scala │ └── HttpUtils.scala ├── docker-compose.yml ├── .scalafmt.conf ├── .travis.yml ├── README.md └── .gitignore /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.3.7 2 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | ThisBuild / version := "0.1.0" 2 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/conf/application.ini: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/_template/lbHeader.st: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web-console/static/html/loading/loading.js: -------------------------------------------------------------------------------- 1 | // TODO 2 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/releases.md: -------------------------------------------------------------------------------- 1 | # 发布 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/design/config.md: -------------------------------------------------------------------------------- 1 | # 配置管理设计 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/use/spring.md: -------------------------------------------------------------------------------- 1 | # 在 Spring 中使用 2 | 3 | TODO 4 | -------------------------------------------------------------------------------- /docs/paradox.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "discoveryx-docs", 3 | "version" : "0.1.0" 4 | } -------------------------------------------------------------------------------- /web-console/.eslintignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | dist/ 4 | static/ -------------------------------------------------------------------------------- /docs/css/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/icons.eot -------------------------------------------------------------------------------- /docs/css/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/icons.ttf -------------------------------------------------------------------------------- /docs/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/favicon.ico -------------------------------------------------------------------------------- /docs/css/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/icons.woff -------------------------------------------------------------------------------- /docs/css/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/icons.woff2 -------------------------------------------------------------------------------- /docs/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/favicon-16x16.png -------------------------------------------------------------------------------- /docs/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/favicon-32x32.png -------------------------------------------------------------------------------- /docs/images/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/mstile-150x150.png -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/design/naming.md: -------------------------------------------------------------------------------- 1 | # 服务注册、发现设计 2 | 3 | `NamingManager` -> `NamingService` -> `NamingInstance` 4 | -------------------------------------------------------------------------------- /docs/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/images/footer-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/footer-background.jpg -------------------------------------------------------------------------------- /docs/images/header-background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/header-background.jpg -------------------------------------------------------------------------------- /docs/css/fonts/proxima-nova-bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/proxima-nova-bold.eot -------------------------------------------------------------------------------- /docs/css/fonts/proxima-nova-bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/proxima-nova-bold.ttf -------------------------------------------------------------------------------- /web-console/static/html/404/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/web-console/static/html/404/404.png -------------------------------------------------------------------------------- /docs/css/fonts/proxima-nova-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/proxima-nova-bold.woff -------------------------------------------------------------------------------- /docs/css/fonts/proxima-nova-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/proxima-nova-regular.eot -------------------------------------------------------------------------------- /docs/css/fonts/proxima-nova-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/proxima-nova-regular.ttf -------------------------------------------------------------------------------- /docs/images/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/images/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/images/android-chrome-512x512.png -------------------------------------------------------------------------------- /docs/css/fonts/proxima-nova-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/proxima-nova-regular.woff -------------------------------------------------------------------------------- /docs/css/fonts/source-code-pro-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/source-code-pro-regular.eot -------------------------------------------------------------------------------- /docs/css/fonts/source-code-pro-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/source-code-pro-regular.ttf -------------------------------------------------------------------------------- /docs/css/fonts/source-code-pro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/docs/css/fonts/source-code-pro-regular.woff -------------------------------------------------------------------------------- /web-console/static/assets/images/aiwrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/web-console/static/assets/images/aiwrap.png -------------------------------------------------------------------------------- /web-console/static/assets/images/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/akka-fusion/fusion-discoveryx/HEAD/web-console/static/assets/images/qrcode.png -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/design/architecture.md: -------------------------------------------------------------------------------- 1 | # 架构 2 | 3 | ## 基本架构及概念 4 | 5 | TODO 总图 6 | 7 | 详细概念请阅读 @ref[Fusion DiscoveryX 概念](concept.md) 。 8 | 9 | -------------------------------------------------------------------------------- /web-console/src/components/Pagination/index.less: -------------------------------------------------------------------------------- 1 | .rs-pagination { 2 | margin: 16px 0; 3 | float: right; 4 | 5 | &:before, 6 | &:after { 7 | clear: both; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /scripts/travis-test-scala13.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$BASEDIR" 4 | 5 | start "RUNNING TESTS FOR SCALA 2.13" 6 | 7 | runSbt test:compile 8 | 9 | end "ALL TESTS PASSED" 10 | -------------------------------------------------------------------------------- /.travis-jvmopts: -------------------------------------------------------------------------------- 1 | # This is used to configure the sbt instance that Travis launches 2 | 3 | -Xms2G 4 | -Xmx2G 5 | -Xss2M 6 | -XX:MaxInlineLevel=18 7 | -XX:MaxMetaspaceSize=1G 8 | -Dfile.encoding=UTF-8 9 | -------------------------------------------------------------------------------- /scripts/travis-paradox.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$BASEDIR" 4 | 5 | start test "RUNNING Generate paradox" 6 | 7 | runSbt "discoveryx-docs/paradox" 8 | 9 | end test "ALL TESTS PASSED" 10 | -------------------------------------------------------------------------------- /scripts/travis-test-scala12.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$BASEDIR" 4 | 5 | start "RUNNING TESTS FOR SCALA 2.12" 6 | 7 | runSbt "++2.12.10 test:compile" 8 | 9 | end "ALL TESTS PASSED" 10 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/open/grpc.md: -------------------------------------------------------------------------------- 1 | # gRPC Service Descriptor 2 | 3 | @@snip [discoveryx.proto](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/grpc/discoveryx.proto) 4 | -------------------------------------------------------------------------------- /scripts/dockers/fusion-discoveryx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:11 2 | 3 | COPY ../../../discoveryx-server/target/universal/stage /fusion-discoveryx 4 | CMD ["/fusion-discoveryx/bin/discoveryx-server > /dev/null"] 5 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/management/grpc.md: -------------------------------------------------------------------------------- 1 | # gRPC Service Descriptor 2 | 3 | @@snip [config.proto](../../../../../../discoveryx-server/src/main/protobuf/fusion/discoveryx/server/grpc/server.proto) 4 | -------------------------------------------------------------------------------- /discoveryx-functest/README.md: -------------------------------------------------------------------------------- 1 | # discoveryx-functest 2 | 3 | 功能测试 4 | 5 | **多节点功能集成测试** 6 | 7 | ```sbtshell 8 | > discoveryx-functest/multi-jvm:testOnly fusion.discoveryx.functest.DiscoveryXMultiTest 9 | ``` 10 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/_template/source.st: -------------------------------------------------------------------------------- 1 | $if(page.source_url)$ 2 |
3 | 在此文档中发现错误?该页面的源代码可以在 这里 找到。欢迎随时编辑并提交Pull Request。 4 |
5 | $endif$ 6 | -------------------------------------------------------------------------------- /scripts/dockers/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:latest 2 | 3 | RUN localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 4 | 5 | ENV LANG zh_CN.utf8 6 | 7 | COPY postgres-schema.sql /docker-entrypoint-initdb.d/ 8 | -------------------------------------------------------------------------------- /scripts/dockers/cassandra/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM cassandra:latest 2 | 3 | #RUN localedef -i zh_CN -c -f UTF-8 -A /usr/share/locale/locale.alias zh_CN.UTF-8 4 | 5 | ENV LANG zh_CN.utf8 6 | 7 | COPY ./cassandra-schema.cql /docker-entrypoint-initdb.d/ 8 | -------------------------------------------------------------------------------- /scripts/publish-docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | directory=../docs/ 4 | 5 | if [ ! -d $directory ]; then 6 | mkdir -p $directory 7 | fi 8 | 9 | rm -rf ${directory}/* 10 | cp -r ../discoveryx-docs/target/paradox/site/main/* ${directory}/ 11 | -------------------------------------------------------------------------------- /web-console/.prettierignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | /dist 4 | /static 5 | /scripts 6 | /.idea 7 | 8 | .eslintignore 9 | .gitignore 10 | .prettierignore 11 | .DS_Store 12 | 13 | yarn.lock 14 | deploy.sh 15 | -------------------------------------------------------------------------------- /scripts/publish-dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | directory=../discoveryx-server/src/main/resources/dist 4 | 5 | if [ ! -d $directory ]; then 6 | mkdir -p $directory 7 | fi 8 | 9 | rm -rf ${directory}/* 10 | cp -r ../web-console/dist/* ${directory}/ 11 | -------------------------------------------------------------------------------- /web-console/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | 16 | *.tar.gz -------------------------------------------------------------------------------- /web-console/src/components/Breadcrumb/index.less: -------------------------------------------------------------------------------- 1 | .fd-breadcrumb { 2 | background-color: #fff; 3 | padding: 16px 24px; 4 | margin-bottom: 16px; 5 | 6 | .ant-page-header-heading { 7 | display: flex; 8 | align-items: center; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | play { 2 | modules { 3 | enabled += "fusion.discoveryx.client.play.scaladsl.module.DiscoveryXWSModule" 4 | enabled += "fusion.discoveryx.client.play.javadsl.module.DiscoveryXWSModule" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/use/index.md: -------------------------------------------------------------------------------- 1 | # 使用 2 | 3 | @@toc { depth=2 } 4 | 5 | @@@ index 6 | 7 | - [quick-start](quick-start.md) 8 | - [sdk-scala](sdk-scala.md) 9 | - [akka](akka.md) 10 | - [play](play.md) 11 | - [spring](spring.md) 12 | 13 | @@@ 14 | 15 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sbt "project discoveryx-docs" paradox 4 | 5 | pushd web-console/ 6 | yarn 7 | yarn build 8 | popd 9 | 10 | pushd scripts/ 11 | sh publish-docs.sh 12 | sh publish-dist.sh 13 | popd 14 | 15 | sbt "project discoveryx-server" dist 16 | -------------------------------------------------------------------------------- /web-console/src/components/Loadable/index.less: -------------------------------------------------------------------------------- 1 | .loadable-box { 2 | height: 100vh; 3 | width: 100%; 4 | display: flex; 5 | justify-content: center; 6 | align-items: center; 7 | 8 | p { 9 | font-size: 14px; 10 | font-weight: 400; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/open/index.md: -------------------------------------------------------------------------------- 1 | # 开放API 2 | 3 | Fusion DiscoveryX 开放 API 提供了配置发布、获取、管理和服务注册、负载均衡等API,提供了 gRPC 和 REST 两种实现。 4 | 5 | @@toc { depth=3 } 6 | 7 | @@@ index 8 | 9 | - [config](config.md) 10 | - [naming](naming.md) 11 | - [grpc](grpc.md) 12 | 13 | @@@ 14 | -------------------------------------------------------------------------------- /web-console/src/components/Clamp/index.less: -------------------------------------------------------------------------------- 1 | .clamp-container { 2 | overflow: hidden; 3 | 4 | .clamp-container-text { 5 | word-break: break-all; 6 | } 7 | 8 | .clamp-container-btn { 9 | margin: 0; 10 | padding: 0; 11 | border: 0; 12 | outline: none; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/index.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | Fusion DiscoveryX 提供两大类API。开放 API 提供客户端访问接口,管理 API 提供了附带Web管理控制台所需要使用的接口。所有 API 均同时提供 gRPC 和 RESTful 。 4 | 5 | @@toc { depth=3 } 6 | 7 | @@@ index 8 | 9 | - [open](open/index.md) 10 | - [management](management/index.md) 11 | - [json](json.md) 12 | 13 | @@@ 14 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/deploy/docker.md: -------------------------------------------------------------------------------- 1 | # Docker 2 | 3 | ## 单节点 4 | 5 | Fusion DiscoveryX 提供了 Docker 构建脚本: 6 | 7 | ``` 8 | cd fusion-discoveryx/ 9 | sbt "project discoveryx-server" dist 10 | scripts/dockers/fusion-discoveryx 11 | docker build -t fusion-discoveryx . 12 | ``` 13 | 14 | ## 集群 15 | 16 | TODO 17 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/management/index.md: -------------------------------------------------------------------------------- 1 | # 管理API 2 | 3 | 管理 API 为 Fusion DiscoveryX 提供了 Web 控制台使用的访问接口,提供了命名空间管理、配置管理、名称服务管理、用户管理等接口。 4 | 5 | @@toc { depth=3 } 6 | 7 | @@@ index 8 | 9 | - [namespace](namespace.md) 10 | - [config](config.md) 11 | - [naming](naming.md) 12 | - [user](user.md) 13 | - [grpc](grpc.md) 14 | 15 | @@@ 16 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/design/index.md: -------------------------------------------------------------------------------- 1 | # 设计 2 | 3 | Fusion DiscoveryX 完全基于 Akka 开放,使用 Actor 模型来处理各类异步任务。通过 Akka Persistence 来实现事件存储和状态保持,通过 gRPC 提供客户端接口协议。 4 | 5 | @@toc { depth=3 } 6 | 7 | @@@ index 8 | 9 | - [concept](concept.md) 10 | - [architecture](architecture.md) 11 | - [config](config.md) 12 | - [naming](naming.md) 13 | - [technology](technology.md) 14 | 15 | @@@ 16 | -------------------------------------------------------------------------------- /web-console/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 100, 5 | "proseWrap": "never", 6 | "overrides": [ 7 | { 8 | "files": ".prettierrc", 9 | "options": { 10 | "parser": "json" 11 | } 12 | }, 13 | { 14 | "files": ".eslintrc", 15 | "options": { 16 | "parser": "json" 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /web-console/src/global.less: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 | // 全局样式 3 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 4 | 5 | body { 6 | font-family: PingFangSC-Regular, 'Avenir', Helvetica, Arial, sans-serif; 7 | } 8 | 9 | .fe-highlight { 10 | color: #f50; 11 | } 12 | -------------------------------------------------------------------------------- /web-console/static/assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | React Logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /web-console/src/components/Mismatch/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 2018/6/20 gongtiexin 404 5 | * */ 6 | 7 | import React from 'react'; 8 | import './index.less'; 9 | 10 | const Mismatch = () => ( 11 |
12 | 404 13 |

抱歉,你访问的页面不存在

14 |
15 | ); 16 | 17 | export default React.memo(Mismatch); 18 | -------------------------------------------------------------------------------- /docs/images/akka-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/images/akka-icon-reverse.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | fusion.http.default { 2 | server { 3 | host = 127.0.0.1 4 | port = 48000 5 | } 6 | } 7 | discoveryx { 8 | akka { 9 | actor.provider = cluster 10 | cluster.seed-nodes = ["127.0.0.1:49000"] 11 | } 12 | } 13 | 14 | jdbc-journal { 15 | use-shared-db = "postgres" 16 | } 17 | jdbc-snapshot-store { 18 | use-shared-db = "postgres" 19 | } 20 | jdbc-read-journal { 21 | use-shared-db = "postgres" 22 | } 23 | -------------------------------------------------------------------------------- /web-console/src/pages/config/constants.js: -------------------------------------------------------------------------------- 1 | const CONFIG_TYPE_ENUM = { 2 | TEXT: 0, 3 | HOCON: 1, 4 | JSON: 2, 5 | YAML: 3, 6 | PROPERTIES: 4, 7 | INI: 5, 8 | properties: { 9 | 0: { label: 'TEXT', value: 0 }, 10 | 1: { label: 'HOCON', value: 1 }, 11 | 2: { label: 'JSON', value: 2 }, 12 | 3: { label: 'YAML', value: 3 }, 13 | 4: { label: 'PROPERTIES', value: 4 }, 14 | 5: { label: 'INI', value: 5 }, 15 | }, 16 | }; 17 | 18 | export { CONFIG_TYPE_ENUM }; 19 | -------------------------------------------------------------------------------- /discoveryx-common/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | fusion.http.default.server { 2 | host = "127.0.0.1" 3 | port = 48000 4 | } 5 | discoveryx { 6 | name = discoveryx 7 | 8 | akka { 9 | actor.provider = cluster 10 | remote { 11 | artery { 12 | canonical { 13 | hostname = "127.0.0.1" 14 | port = 49000 15 | } 16 | } 17 | } 18 | cluster { 19 | roles = [] 20 | } 21 | } 22 | 23 | config-modules = [akka] 24 | } 25 | -------------------------------------------------------------------------------- /web-console/src/components/SearchTable/index.less: -------------------------------------------------------------------------------- 1 | .search-table { 2 | //padding: 24px; 3 | 4 | .ant-advanced-search-form { 5 | padding: 24px; 6 | background: #fbfbfb; 7 | border: 1px solid #d9d9d9; 8 | border-radius: 6px; 9 | margin-bottom: 20px; 10 | 11 | .ant-form-item { 12 | display: flex; 13 | } 14 | 15 | .ant-form-item-control-wrapper { 16 | flex: 1; 17 | } 18 | } 19 | 20 | .search-expand { 21 | margin-bottom: 20px; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /discoveryx-common/src/main/protobuf/fusion/define.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package fusion; 3 | 4 | import "google/protobuf/any.proto"; 5 | import "google/protobuf/wrappers.proto"; 6 | import "scalapb/scalapb.proto"; 7 | 8 | enum CommonStatus { 9 | DISABLE = 0; 10 | ENABLE = 1; 11 | } 12 | 13 | message ResultBO { 14 | int32 status = 1; 15 | string msg = 2; 16 | //google.protobuf.Any data = 3; 17 | map data = 3; 18 | CommonStatus common_status = 4; 19 | } 20 | -------------------------------------------------------------------------------- /docs/images/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Akka", 3 | "icons": [ 4 | { 5 | "src": "android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#15a9ce", 16 | "background_color": "#ffffff", 17 | "display": "standalone" 18 | } 19 | -------------------------------------------------------------------------------- /web-console/src/pages/App/index.less: -------------------------------------------------------------------------------- 1 | #app { 2 | .logo { 3 | height: 32px; 4 | line-height: 32px; 5 | text-align: center; 6 | font-weight: 700; 7 | background: rgba(255, 255, 255, 0.2); 8 | margin: 16px; 9 | color: #fff; 10 | } 11 | 12 | .ant-layout-header { 13 | display: flex; 14 | justify-content: flex-end; 15 | align-items: center; 16 | 17 | background: #fff; 18 | padding: 0; 19 | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); 20 | z-index: 2; 21 | 22 | span, 23 | a { 24 | margin-right: 40px; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/use/akka.md: -------------------------------------------------------------------------------- 1 | # 在 Akka 中使用 2 | 3 | ## Module Info 4 | 5 | 需要在项目中添加如下依赖: 6 | 7 | @@dependency[sbt,Gradle,Maven] { group="com.akka-fusion.fusion" artifact="discoveryx-client_$scala.binary_version$" version="$version$" } 8 | 9 | ## 服务发现 10 | 11 | Akka通过 [akka-discovery](https://doc.akka.io/docs/akka/current/discovery/index.html) 提供了默认的服务发现功能,DiscoveryX Client提供了对其的支持。我们只需要配置`akka.discovery`设置使用`fusion-discoveryx`使用 DiscoveryX Client 来为 akka-discovery 提供服务发现功能。 12 | 13 | @@snip [discovery](../../../../../discoveryx-client/src/main/resources/reference.conf) { #discovery } 14 | 15 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/resources/application-test.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "fusion.discoveryx.grpc.NamingService" { 3 | use-tls = false 4 | host = "helloscala.com" 5 | // host = "127.0.0.1" 6 | port = 48000 7 | } 8 | } 9 | discoveryx.client { 10 | naming { 11 | heartbeat-interval = 20.seconds 12 | namespace = "890cd0cd-22d8-11ea-8bfe-5254002e9e52" // helloscala.com gtx 13 | // namespace = "6e2864d3-22fe-11ea-89f4-d2680721077d" // local 14 | service-name = "discoveryx" 15 | ip = "127.0.0.1" 16 | port = 8000 17 | enable = true 18 | weight = 1.5 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/deploy/single.md: -------------------------------------------------------------------------------- 1 | # 单机部署 2 | 3 | *请先阅读 @ref[软件包](package.md) 。* 4 | 5 | ## 使用 6 | 7 | 单机部署默认使用嵌入式数据库 **H2**。Fusion DiscoveryX 将在第一次启动服务时在用户主目录下创建 H2 数据库目录并初始化数据表和用户,默认用户: 8 | 9 | - SN: `discoveryx` 10 | - PW: `discoveryx` 11 | 12 | @@@vars 13 | ``` 14 | unzip discoveryx-server-$version$.zip 15 | cd discoveryx-server-$version$ 16 | ./bin/discoveryx-server 17 | ``` 18 | @@@ 19 | 20 | ## H2 数据库配置 21 | 22 | 默认 H2 数据库表将创建在 `$HOME` 目录,如: 23 | 24 | ``` 25 | yangjing@yangbajing:~$ tree fusion-discoveryx/ 26 | fusion-discoveryx/ 27 | └── db.mv.db 28 | 29 | 0 directories, 1 file 30 | ``` 31 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/resources/application-local.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "fusion.discoveryx.grpc.NamingService" { 3 | use-tls = false 4 | host = "localhost" 5 | port = 48000 6 | } 7 | } 8 | discoveryx.client { 9 | naming { 10 | auto-registration = false 11 | heartbeat-interval = 10.seconds 12 | namespace = "3d355123-3081-11ea-887d-4a7eb37c5068" // local public 13 | service-name = "fusion-schedulerx" 14 | port = 8000 15 | enable = true 16 | health = true 17 | weight = 1.0 18 | metadata { 19 | env = dev 20 | application = dev.schedulerx 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-console/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | <%= htmlWebpackPlugin.options.title %> 14 | 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/resources/application-helloscala.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "fusion.discoveryx.grpc.NamingService" { 3 | use-tls = false 4 | host = "helloscala.com" 5 | port = 48000 6 | } 7 | } 8 | discoveryx.client { 9 | naming { 10 | auto-registration = false 11 | heartbeat-interval = 10.seconds 12 | namespace = "ce6340c5-3066-11ea-b4fa-5254002e9e52" // helloscala.com public 13 | service-name = "fusion-schedulerx" 14 | port = 8000 15 | enable = true 16 | health = true 17 | weight = 1.0 18 | metadata { 19 | env = dev 20 | application = dev.schedulerx 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-console/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 若无法执行 运行命令 chmod +x deploy.sh 4 | 5 | # 当前时间 6 | date=`date +%F` 7 | # 编译后资源所在的文件名 8 | dist_name="dist" 9 | # 压缩后的文件名 10 | file_name="dist-${date}.tar.gz" 11 | # 服务器用户名 12 | user="hl" 13 | # 服务器地址 14 | host="dn5" 15 | # 服务器上资源所在的路劲 16 | pwd="/home/app/frontend/recommender-app/" 17 | 18 | # 删除之前的文件 19 | rm -rf ${dist_name} 20 | rm -rf *.tar.gz 21 | 22 | # 构建项目 23 | npm run build 24 | 25 | # 压缩打包 26 | tar -zcvf ${file_name} ${dist_name} 27 | 28 | # 上传到服务器 29 | scp ${file_name} ${user}@${host}:${pwd} 30 | 31 | # 登录到目标服务器并发布 32 | ssh ${user}@${host} "cd ${pwd};tar -zxvf ${file_name} ${dist_name}" 33 | 34 | echo "发布成功" -------------------------------------------------------------------------------- /discoveryx-client/src/test/resources/application-local-2.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "fusion.discoveryx.grpc.NamingService" { 3 | use-tls = false 4 | host = "localhost" 5 | port = 48000 6 | } 7 | } 8 | discoveryx.client { 9 | naming { 10 | auto-registration = true 11 | heartbeat-interval = 10.seconds 12 | namespace = "3d355123-3081-11ea-887d-4a7eb37c5068" // local public 13 | service-name = "fusion-schedulerx" 14 | ip = "127.0.0.1" 15 | port = 8001 16 | enable = true 17 | health = true 18 | weight = 2.0 19 | metadata { 20 | env = dev 21 | application = dev.schedulerx 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/test/resources/application-test.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "fusion.discoveryx.grpc.NamingService" { 3 | use-tls = false 4 | host = "localhost" 5 | port = 48000 6 | } 7 | } 8 | discoveryx.client { 9 | naming { 10 | auto-registration = true 11 | heartbeat-interval = 15.seconds 12 | namespace = "e43263f8-2df0-11ea-b7a8-1a022201bc26" // local public 13 | service-name = "fusion-discoveryx" 14 | port = 8888 15 | health = true 16 | enable = true 17 | ephemeral = true 18 | weight = 1.5 19 | metadata { 20 | env = dev 21 | application = dev.schedulerx 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/_template/copyright.st: -------------------------------------------------------------------------------- 1 | 11 | 12 | -------------------------------------------------------------------------------- /discoveryx-functest/src/multi-jvm/resources/application.conf: -------------------------------------------------------------------------------- 1 | //akka-persistence-jdbc.shared-databases.h2 { 2 | // db { 3 | // url = "jdbc:h2:mem:discoveryx;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;" 4 | // } 5 | //} 6 | 7 | jdbc-journal { 8 | use-shared-db = "h2" 9 | } 10 | jdbc-snapshot-store { 11 | use-shared-db = "h2" 12 | } 13 | jdbc-read-journal { 14 | use-shared-db = "h2" 15 | } 16 | 17 | akka.persistence { 18 | journal { 19 | plugin = "jdbc-journal" 20 | //auto-start-journals = ["jdbc-journal"] 21 | } 22 | snapshot-store { 23 | plugin = "jdbc-snapshot-store" 24 | //auto-start-snapshot-stores = ["jdbc-snapshot-store"] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/resources/application-helloscala-2.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "fusion.discoveryx.grpc.NamingService" { 3 | use-tls = false 4 | host = "helloscala.com" 5 | port = 48000 6 | } 7 | } 8 | discoveryx.client { 9 | naming { 10 | auto-registration = true 11 | heartbeat-interval = 10.seconds 12 | namespace = "ce6340c5-3066-11ea-b4fa-5254002e9e52" // helloscala.com public 13 | service-name = "fusion-schedulerx" 14 | ip = "127.0.0.1" 15 | port = 8001 16 | enable = true 17 | health = true 18 | weight = 2.0 19 | metadata { 20 | env = dev 21 | application = dev.schedulerx 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/lib/prettify/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /discoveryx-server/src/test/resources/application-helloscala.conf: -------------------------------------------------------------------------------- 1 | akka.grpc.client { 2 | "*" { 3 | use-tls = false 4 | host = "helloscala.com" 5 | port = 48000 6 | } 7 | "fusion.discoveryx.grpc.NamingService" {} 8 | "fusion.discoveryx.server.grpc.UserService" {} 9 | } 10 | discoveryx.client { 11 | naming { 12 | heartbeat-interval = 30.seconds 13 | namespace = "ce6340c5-3066-11ea-b4fa-5254002e9e52" // helloscala.com gtx 14 | service-name = "discoveryx" 15 | ip = "172.31.130.182" 16 | port = 8000 17 | enable = true 18 | weight = 1.5 19 | metadata { 20 | env = test 21 | application = web-backend 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /docs/js/metadata-toggle.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | var hiddenText = "[+] Show project info", 3 | shownText = "[-] Hide project info", 4 | toggle = $('' + hiddenText + ''), 5 | hidden = true, 6 | infotable = $('table.project-info') 7 | 8 | toggle.insertBefore(infotable) 9 | toggle.on("click", function(event) { 10 | if (hidden) { 11 | infotable.css("display", "block") 12 | toggle.text(shownText) 13 | hidden = false 14 | } else { 15 | infotable.css("display", "none") 16 | toggle.text(hiddenText) 17 | hidden = true 18 | } 19 | }) 20 | }) -------------------------------------------------------------------------------- /web-console/src/components/Mismatch/index.less: -------------------------------------------------------------------------------- 1 | #noMatch { 2 | margin: 0; 3 | cursor: default; 4 | user-select: none; 5 | -webkit-font-smoothing: antialiased; 6 | text-rendering: optimizeLegibility; 7 | position: absolute; 8 | left: 50%; 9 | top: 50%; 10 | transform: translate(-50%, -50%); 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | 15 | span { 16 | font-size: 24px; 17 | font-weight: 500; 18 | border-bottom: 0; 19 | border-right: 1px solid #eaeaea; 20 | padding: 0 20px 0 0; 21 | width: auto; 22 | } 23 | 24 | p { 25 | margin: 0; 26 | padding-left: 20px; 27 | font-size: 14px; 28 | font-weight: 400; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /web-console/static/html/loading/loading.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 4A系统——华龙海数 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/resources/sql/schemas/h2.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS public."journal" ( 2 | "ordering" BIGINT AUTO_INCREMENT, 3 | "persistence_id" VARCHAR(255) NOT NULL, 4 | "sequence_number" BIGINT NOT NULL, 5 | "deleted" BOOLEAN DEFAULT FALSE NOT NULL, 6 | "tags" VARCHAR(255) DEFAULT NULL, 7 | "message" BYTEA NOT NULL, 8 | PRIMARY KEY("persistence_id", "sequence_number") 9 | ); 10 | 11 | CREATE UNIQUE INDEX IF NOT EXISTS "journal_ordering_idx" ON public."journal"("ordering"); 12 | 13 | CREATE TABLE IF NOT EXISTS public."snapshot" ( 14 | "persistence_id" VARCHAR(255) NOT NULL, 15 | "sequence_number" BIGINT NOT NULL, 16 | "created" BIGINT NOT NULL, 17 | "snapshot" BYTEA NOT NULL, 18 | PRIMARY KEY("persistence_id", "sequence_number") 19 | ); -------------------------------------------------------------------------------- /discoveryx-server/src/universal/share/jdbc/schema/mysql/mysql-schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS journal; 2 | 3 | CREATE TABLE IF NOT EXISTS journal ( 4 | ordering SERIAL, 5 | persistence_id VARCHAR(255) NOT NULL, 6 | sequence_number BIGINT NOT NULL, 7 | deleted BOOLEAN DEFAULT FALSE NOT NULL, 8 | tags VARCHAR(255) DEFAULT NULL, 9 | message BLOB NOT NULL, 10 | PRIMARY KEY(persistence_id, sequence_number) 11 | ); 12 | 13 | CREATE UNIQUE INDEX journal_ordering_idx ON journal(ordering); 14 | 15 | DROP TABLE IF EXISTS snapshot; 16 | 17 | CREATE TABLE IF NOT EXISTS snapshot ( 18 | persistence_id VARCHAR(255) NOT NULL, 19 | sequence_number BIGINT NOT NULL, 20 | created BIGINT NOT NULL, 21 | snapshot BLOB NOT NULL, 22 | PRIMARY KEY (persistence_id, sequence_number) 23 | ); 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # For detailed information about docker-compose visit https://docs.docker.com/compose/ 2 | # To start all docker containers required to execute the tests locally run: 3 | # docker-compose up 4 | 5 | version: '3' 6 | services: 7 | cassandra: 8 | container_name: fusion-discoveryx_cassandra 9 | build: 10 | context: ./scripts/dockers/cassandra 11 | dockerfile: Dockerfile 12 | ports: 13 | - "9042:9042" 14 | postgres: 15 | container_name: fusion-discoveryx_postgres 16 | build: 17 | context: ./scripts/dockers/postgres 18 | dockerfile: Dockerfile 19 | ports: 20 | - "5432:5432" 21 | environment: 22 | POSTGRES_DB: fusion_discoveryx 23 | POSTGRES_USER: devuser 24 | POSTGRES_PASSWORD: devPass.2019 25 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DEBUG 6 | 7 | 8 | [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /web-console/src/router/constants.js: -------------------------------------------------------------------------------- 1 | // 配置管理 2 | const CONFIG_MANAGEMENT_LIST = '/config/management/list'; 3 | // 新建配置 4 | const CONFIG_MANAGEMENT_CREATE = '/config/management/create'; 5 | // 新建配置 6 | const CONFIG_MANAGEMENT_DETAIL = '/config/management/detail'; 7 | // 服务管理 8 | const SERVICE_MANAGEMENT_LIST = '/service/management/list'; 9 | // 服务详情 10 | const SERVICE_MANAGEMENT_DETAIL = '/service/management/detail'; 11 | // 命名空间管理 12 | const NAMESPACE_MANAGEMENT_LIST = '/namespace/management/list'; 13 | // 用户管理 14 | const USER_MANAGEMENT_LIST = '/user/management/list'; 15 | 16 | export { 17 | CONFIG_MANAGEMENT_LIST, 18 | CONFIG_MANAGEMENT_CREATE, 19 | CONFIG_MANAGEMENT_DETAIL, 20 | NAMESPACE_MANAGEMENT_LIST, 21 | SERVICE_MANAGEMENT_LIST, 22 | SERVICE_MANAGEMENT_DETAIL, 23 | USER_MANAGEMENT_LIST, 24 | }; 25 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/intro.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | *Fusion DiscoveryX 成长于作者对 Akka 生态的学习,API上参考了Nacos的某些设计。* 4 | 5 | Fusion DiscoveryX(之后简称 DiscoveryX)是一款服务注册与发现管理系统,致力于帮助您发现、配置及管理微服务。基于Akka生态开发。 6 | 7 | @@project-info{ projectId="fusion-discoveryx" } 8 | 9 | ## 特性 10 | 11 | - 配置管理 12 | - 服务发现 13 | - 动态配置服务 14 | 15 | ## 开发技术 16 | 17 | DiscoveryX 在开发中主要使用到以下技术: 18 | 19 | | 功能 | 使用技术 | 20 | | ---------- | --------------------- | 21 | | 开放API | Akka gRPC | 22 | | 集群序例化 | Protobuf | 23 | | 配置持久化 | Akka Persistence | 24 | | 容错与扩展 | Akka Cluster Sharding | 25 | | REST | Akka HTTP | 26 | 27 | ## 接下来 28 | 29 | 访问 @ref[quick-start](use/quick-start.md) 开始快速使用 DiscoveryX 。 30 | 31 | 也可以访问 @ref[architecture](design/architecture.md) 了解 DiscoveryX 的架构设计与技术选择。 32 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/share/jdbc/schema/postgres/postgres-schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS public.journal; 2 | 3 | CREATE TABLE IF NOT EXISTS public.journal ( 4 | ordering BIGSERIAL, 5 | persistence_id VARCHAR(255) NOT NULL, 6 | sequence_number BIGINT NOT NULL, 7 | deleted BOOLEAN DEFAULT FALSE NOT NULL, 8 | tags VARCHAR(255) DEFAULT NULL, 9 | message BYTEA NOT NULL, 10 | PRIMARY KEY(persistence_id, sequence_number) 11 | ); 12 | 13 | CREATE UNIQUE INDEX journal_ordering_idx ON public.journal(ordering); 14 | 15 | DROP TABLE IF EXISTS public.snapshot; 16 | 17 | CREATE TABLE IF NOT EXISTS public.snapshot ( 18 | persistence_id VARCHAR(255) NOT NULL, 19 | sequence_number BIGINT NOT NULL, 20 | created BIGINT NOT NULL, 21 | snapshot BYTEA NOT NULL, 22 | PRIMARY KEY(persistence_id, sequence_number) 23 | ); 24 | 25 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/share/jdbc/schema/sqlserver/sqlserver-schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS journal; 2 | 3 | CREATE TABLE journal ( 4 | "ordering" BIGINT IDENTITY(1,1) NOT NULL, 5 | "deleted" BIT DEFAULT 0 NOT NULL, 6 | "persistence_id" VARCHAR(255) NOT NULL, 7 | "sequence_number" NUMERIC(10,0) NOT NULL, 8 | "tags" VARCHAR(255) NULL DEFAULT NULL, 9 | "message" VARBINARY(max) NOT NULL, 10 | PRIMARY KEY ("persistence_id", "sequence_number") 11 | ); 12 | 13 | CREATE UNIQUE INDEX journal_ordering_idx ON journal (ordering); 14 | 15 | DROP TABLE IF EXISTS snapshot; 16 | 17 | CREATE TABLE snapshot ( 18 | "persistence_id" VARCHAR(255) NOT NULL, 19 | "sequence_number" NUMERIC(10,0) NOT NULL, 20 | "created" NUMERIC NOT NULL, 21 | "snapshot" VARBINARY(max) NOT NULL, 22 | PRIMARY KEY ("persistence_id", "sequence_number") 23 | ); 24 | -------------------------------------------------------------------------------- /discoveryx-client/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DEBUG 6 | 7 | 8 | [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DEBUG 6 | 7 | 8 | [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/deploy/persistence-cassandra.md: -------------------------------------------------------------------------------- 1 | # 集群部署(使用Cassandra作为存储) 2 | 3 | ## 基本配置 4 | 5 | Fusion DiscoveryX 使用 Akka Persistence 作为存储层,[akka-persistence-cassandra](https://doc.akka.io/docs/akka-persistence-cassandra/current/) 为 Akka Persistence 提供了 JDBC 访问插件。 6 | 7 | ```hocon 8 | akka { 9 | persistence { 10 | journal { 11 | plugin = "cassandra-journal" 12 | // auto-start-journals = ["cassandra-journal"] 13 | } 14 | snapshot-store { 15 | plugin = "cassandra-snapshot-store" 16 | // auto-start-snapshot-stores = ["cassandra-snapshot-store"] 17 | } 18 | } 19 | } 20 | ``` 21 | 22 | ### 初始化数据库 23 | 24 | 在启动 Fusion DiscoveryX 之前,需要提前建好 Cassandra 数据库表。在软件包的 `share/cassandra/schema` 目录下能找到 Cassandra 数据库表的创建脚本。 25 | 26 | ``` 27 | ├── journal-schema.cql 28 | └── snapshot-schema.cql 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/js/magellan.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // add magellan targets to anchor headers, for h1 and h2 4 | $("a.anchor").each(function() { 5 | var anchor = $(this); 6 | var name = anchor.attr("name"); 7 | var header = anchor.parent(); 8 | header.attr("id", name); 9 | if (header.is("h1") || header.is("h2")) { 10 | header.attr("data-magellan-target", name); 11 | } 12 | }); 13 | 14 | // enable magellan plugin on the active page header links in the navigation 15 | var nav = $(".site-nav a.active.page").parent("li"); 16 | if (nav.length > 0) { 17 | // strip navigation links down to just the hash fragment 18 | nav.find("a.active.page, a.header").attr('href', function(_, current){ 19 | return this.hash ? this.hash : current; 20 | }); 21 | new Foundation.Magellan(nav); 22 | } 23 | 24 | }); 25 | -------------------------------------------------------------------------------- /discoveryx-functest/src/multi-jvm/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DEBUG 6 | 7 | 8 | [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/javadsl/DiscoveryXPlay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.javadsl; 18 | 19 | public @interface DiscoveryXPlay { 20 | } 21 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/scaladsl/DiscoveryXPlay.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.scaladsl; 18 | 19 | public @interface DiscoveryXPlay { 20 | } 21 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/share/jdbc/schema/h2/h2-schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS PUBLIC."journal"; 2 | 3 | CREATE TABLE IF NOT EXISTS PUBLIC."journal" ( 4 | "ordering" BIGINT AUTO_INCREMENT, 5 | "persistence_id" VARCHAR(255) NOT NULL, 6 | "sequence_number" BIGINT NOT NULL, 7 | "deleted" BOOLEAN DEFAULT FALSE NOT NULL, 8 | "tags" VARCHAR(255) DEFAULT NULL, 9 | "message" BYTEA NOT NULL, 10 | PRIMARY KEY("persistence_id", "sequence_number") 11 | ); 12 | 13 | CREATE UNIQUE INDEX "journal_ordering_idx" ON PUBLIC."journal"("ordering"); 14 | 15 | DROP TABLE IF EXISTS PUBLIC."snapshot"; 16 | 17 | CREATE TABLE IF NOT EXISTS PUBLIC."snapshot" ( 18 | "persistence_id" VARCHAR(255) NOT NULL, 19 | "sequence_number" BIGINT NOT NULL, 20 | "created" BIGINT NOT NULL, 21 | "snapshot" BYTEA NOT NULL, 22 | PRIMARY KEY("persistence_id", "sequence_number") 23 | ); 24 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DEBUG 6 | 7 | 8 | [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /web-console/src/stores/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin mobx统一注册store 5 | * */ 6 | import { store, hotRehydrate, rehydrate } from 'rfx-core'; 7 | import GlobalStore from './GlobalStore'; 8 | import ConfigStore from './ConfigStore'; 9 | import NamespaceStore from './NamespaceStore'; 10 | import ServiceStore from './ServiceStore'; 11 | import UserStore from './UserStore'; 12 | import { isProduction } from '../utils/constants'; 13 | 14 | store.setup({ 15 | globalStore: GlobalStore, 16 | userStore: UserStore, 17 | configStore: ConfigStore, 18 | namespaceStore: NamespaceStore, 19 | serviceStore: ServiceStore, 20 | }); 21 | 22 | // mobx hmr 23 | const stores = rehydrate(); 24 | const hmrStores = isProduction ? stores : hotRehydrate(); 25 | 26 | export default hmrStores; 27 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/deploy/index.md: -------------------------------------------------------------------------------- 1 | # 部署 2 | 3 | Fusion DiscoveryX 提供 **zip** 包和 **Docker** 镜像两种打包部署方式,高级用户亦可以下载源码自行编译打包以获得更多的定制性。Fusion DiscoveryX 支持单机、集群部署,官方集群部署提供了对 JDBC 、Cassandra 的后端存储支持,用户通过 Akka Persistence Plugins 可以很方便的集成其它存储方式。如: 4 | 5 | - MongoDB: [https://github.com/scullxbones/akka-persistence-mongo](https://github.com/scullxbones/akka-persistence-mongo) 6 | - DynamoDB: [https://github.com/akka/akka-persistence-dynamodb](https://github.com/akka/akka-persistence-dynamodb) 7 | - CouchBase: [https://github.com/akka/akka-persistence-couchbase](https://github.com/akka/akka-persistence-couchbase) 8 | 9 | @@toc { depth=3 } 10 | 11 | @@@ index 12 | 13 | - [package](package.md) 14 | - [single](single.md) 15 | - [cluster-jdbc](cluster-jdbc.md) 16 | - [persistence-cassandra](persistence-cassandra.md) 17 | - [docker](docker.md) 18 | - [other-persistence](other-persistence.md) 19 | 20 | @@@ 21 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/use/quick-start.md: -------------------------------------------------------------------------------- 1 | # 快速开始 2 | 3 | ## 下载 4 | 5 | @@@vars 6 | 下载压缩包 [discoveryx-server-$version$.zip](https://github.com/akka-fusion/fusion-discoveryx/releases),解压到目录。如: 7 | @@@ 8 | 9 | @@@vars 10 | ``` 11 | /home/yangjing/discoveryx-server-$version$ 12 | ``` 13 | @@@ 14 | 15 | ## 启动服务 16 | 17 | 进入软件根目录执行启动脚本运行: 18 | 19 | @@@vars 20 | ``` 21 | cd discoveryx-server-$version$/ 22 | ./bin/discoveryx-server 23 | ``` 24 | @@@ 25 | 26 | **Windows**系统请执行 `bin/discoveryx-server.bat` 。 27 | 28 | 打开浏览器,输入:`http://localhost:48000`即可访问 Fusion DiscoveryX 管理控制台。 29 | 30 | @@@note 31 | 默认将使用 H2 嵌入式文件数据库,这种方式只适合测试使用。若需要在产品中使用,请使用独立数据库,参阅: @ref[集群部署(使用JDBC持久化)](../deploy/cluster-jdbc.md) 或 @ref[集群部署(使用Cassandra作为存储)](../deploy/persistence-cassandra.md) 。 32 | @@@ 33 | 34 | ## 接下来 35 | 36 | - @ref[在 Akka 中使用](akka.md) 37 | - @ref[在 Play 中使用](play.md) 38 | - @ref[在 Spring 中使用](spring.md) 39 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/FusionDiscoveryXMain.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server 18 | 19 | object FusionDiscoveryXMain { 20 | def main(args: Array[String]): Unit = { 21 | DiscoveryXServer().start() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/lib/prettify/lang-scala.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null,"\t\n\r \u00a0"],["str",/^"(?:""(?:""?(?!")|[^"\\]|\\.)*"{0,3}|(?:[^\n\r"\\]|\\.)*"?)/,null,'"'],["lit",/^`(?:[^\n\r\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&(--:-@[-^{-~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\n\r'\\]|\\(?:'|[^\n\r']+))'/],["lit",/^'[$A-Z_a-z][\w$]*(?![\w$'])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/], 2 | ["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:0(?:[0-7]+|x[\da-f]+)l?|(?:0|[1-9]\d*)(?:(?:\.\d+)?(?:e[+-]?\d+)?f?|l?)|\\.\d+(?:e[+-]?\d+)?f?)/i],["typ",/^[$_]*[A-Z][\d$A-Z_]*[a-z][\w$]*/],["pln",/^[$A-Z_a-z][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]); 3 | -------------------------------------------------------------------------------- /scripts/dockers/postgres/postgres-schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS public.journal; 2 | 3 | CREATE TABLE IF NOT EXISTS public.journal 4 | ( 5 | ordering BIGSERIAL, 6 | persistence_id VARCHAR(255) NOT NULL, 7 | sequence_number BIGINT NOT NULL, 8 | deleted BOOLEAN DEFAULT FALSE, 9 | tags VARCHAR(255) DEFAULT NULL, 10 | message BYTEA NOT NULL, 11 | PRIMARY KEY (persistence_id, sequence_number) 12 | ); 13 | 14 | CREATE UNIQUE INDEX IF NOT EXISTS journal_ordering_idx ON public.journal (ordering); 15 | 16 | DROP TABLE IF EXISTS public.snapshot; 17 | 18 | CREATE TABLE IF NOT EXISTS public.snapshot 19 | ( 20 | persistence_id VARCHAR(255) NOT NULL, 21 | sequence_number BIGINT NOT NULL, 22 | created BIGINT NOT NULL, 23 | snapshot BYTEA NOT NULL, 24 | PRIMARY KEY (persistence_id, sequence_number) 25 | ); 26 | -------------------------------------------------------------------------------- /discoveryx-common/src/main/scala/fusion/discoveryx/DiscoveryXSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx 18 | 19 | import com.typesafe.config.Config 20 | 21 | case class DiscoveryXSettings() 22 | 23 | object DiscoveryXSettings { 24 | def apply(config: Config): DiscoveryXSettings = { 25 | DiscoveryXSettings() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/index.md: -------------------------------------------------------------------------------- 1 | # Fusion DiscoveryX 2 | 3 | [![Build Status](https://travis-ci.org/akka-fusion/fusion-discoveryx.svg?branch=master)](https://travis-ci.org/akka-fusion/fusion-discoveryx) 4 | 5 | Fusion DiscoveryX(之后简称 DiscoveryX)是一款服务注册与发现管理系统,致力于帮助您发现、配置及管理微服务。基于Akka生态开发。 6 | 7 | 产品文档:[https://akka-fusion.github.io/fusion-discoveryx/](https://akka-fusion.github.io/fusion-discoveryx/) 8 | 9 | *码云镜像:[https://akka-fusion.gitee.io/fusion-discoveryx/](https://akka-fusion.gitee.io/fusion-discoveryx/)* 10 | 11 | 在线演示Demo:http://helloscala.com:48000/ 。 12 | 13 | @@@note 14 | *gRPC需要HTTP 2,akka-http支持不使用tls访问HTTP 2。这里直接使用端口访问。* 15 | 16 | 在线演示Demo账号: 17 | 18 | - SN: discoveryx 19 | - PW: discoveryx 20 | @@@ 21 | 22 | @@toc { depth=2 } 23 | 24 | @@@ index 25 | 26 | - [intro](intro.md) 27 | - [use](use/index.md) 28 | - [design](design/index.md) 29 | - [api](api/index.md) 30 | - [deploy](deploy/index.md) 31 | - [release](releases.md) 32 | 33 | @@@ 34 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 2.2.2 2 | style = defaultWithAlign 3 | lineEndings = unix 4 | encoding = "UTF-8" 5 | project.git = true 6 | docstrings = JavaDoc 7 | maxColumn = 120 8 | indentOperator = spray 9 | unindentTopLevelOperators = true 10 | align.tokens = [{code = "=>", owner = "Case"}] 11 | align.openParenDefnSite = false 12 | align.openParenCallSite = false 13 | optIn.breakChainOnFirstMethodDot = false 14 | optIn.configStyleArguments = false 15 | danglingParentheses = false 16 | spaces.inImportCurlyBraces = true 17 | rewrite.neverInfix.excludeFilters = [ 18 | and 19 | min 20 | max 21 | until 22 | to 23 | by 24 | eq 25 | ne 26 | "should.*" 27 | "contain.*" 28 | "must.*" 29 | in 30 | ignore 31 | be 32 | taggedAs 33 | thrownBy 34 | synchronized 35 | have 36 | when 37 | size 38 | only 39 | noneOf 40 | oneElementOf 41 | noElementsOf 42 | atLeastOneElementOf 43 | atMostOneElementOf 44 | allElementsOf 45 | inOrderElementsOf 46 | theSameElementsAs 47 | ] 48 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx 18 | 19 | import akka.persistence.query.scaladsl._ 20 | 21 | package object server { 22 | type DiscoveryXReadJournal = ReadJournal 23 | with PersistenceIdsQuery 24 | with CurrentPersistenceIdsQuery 25 | with EventsByTagQuery 26 | with EventsByPersistenceIdQuery 27 | } 28 | -------------------------------------------------------------------------------- /web-console/src/components/Highlight/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 高亮 5 | * */ 6 | 7 | import React, { Fragment } from 'react'; 8 | import shortid from 'shortid'; 9 | import * as PropTypes from 'prop-types'; 10 | 11 | const Highlight = ({ text, keyword }) => { 12 | const reg = new RegExp(keyword, 'gi'); 13 | const match = text.match(reg); 14 | if (!match) { 15 | return text; 16 | } 17 | return ( 18 | 19 | {text.split(reg).map((fragment, i) => 20 | i > 0 ? ( 21 | 22 | {match[0]} 23 | {fragment} 24 | 25 | ) : ( 26 | fragment 27 | ), 28 | )} 29 | 30 | ); 31 | }; 32 | 33 | Highlight.propTypes = { 34 | text: PropTypes.string.isRequired, 35 | keyword: PropTypes.string.isRequired, 36 | }; 37 | 38 | export default Highlight; 39 | -------------------------------------------------------------------------------- /web-console/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "extends": ["airbnb", "plugin:prettier/recommended"], 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaFeatures": { 10 | "legacyDecorators": true 11 | } 12 | }, 13 | "rules": { 14 | "prettier/prettier": [ 15 | "error", 16 | { 17 | "singleQuote": true, 18 | "trailingComma": "all", 19 | "printWidth": 100, 20 | "proseWrap": "never" 21 | } 22 | ], 23 | "react/jsx-filename-extension": [ 24 | 0, 25 | { 26 | "extensions": [".js", ".jsx"] 27 | } 28 | ], 29 | "react/forbid-prop-types": 0, 30 | "jsx-a11y/anchor-is-valid": [ 31 | "error", 32 | { 33 | "components": ["Link"], 34 | "specialLink": ["to"] 35 | } 36 | ], 37 | "react/static-property-placement": 0, 38 | "react/state-in-constructor": 0, 39 | "react/destructuring-assignment": 0, 40 | "react/jsx-props-no-spreading": 0 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/use/sdk-scala.md: -------------------------------------------------------------------------------- 1 | # Scala SDK 2 | 3 | ## Module Info 4 | 5 | 需要在项目中添加如下依赖: 6 | 7 | @@dependency[sbt,Gradle,Maven] { group="com.akka-fusion.fusion" artifact="discoveryx-client_$scala.binary_version$" version="$version$" } 8 | 9 | @@dependencies{ projectId="discoveryx-client" } 10 | 11 | ## 使用 12 | 13 | ### gRPC Client配置 14 | 15 | Fusion DiscoveryX Client for Scala SDK 使用 akka-grpc 开发,使用时需要配置 `akka.grpc.client` 指定 DiscoveryX Server 地址。 16 | 17 | @@snip [discovery](../../../../../discoveryx-client/src/main/resources/reference.conf) { #grpc-client } 18 | 19 | @@@note 20 | gRPC服务全限定名在HOCON配置里需要使用英文双引号括起来,因为HOCON默认使用英文点号来区分配置层级。 21 | 22 | 测试时可以把`use-tls`参数设置为`false`,但在生产环境中建议使用默认值`true`。 23 | @@@ 24 | 25 | ### 自动注册服务 26 | 27 | 若需要把服务自动注册到 DiscoveryX Server,需要配置 `discoveryx.client.naming.auto-registration = true` 。同时,还需要设置`namespace`、`service-name`、`ip`、`port`,其中`ip`可通过自动检测获取(当不指定或设置为空字符串时,若发现自动检测到的IP地址不正确请手动指定)。 28 | 29 | @@snip [discovery](../../../../../discoveryx-client/src/main/resources/reference.conf) { #discoveryx-client } 30 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/resources/application-test.conf: -------------------------------------------------------------------------------- 1 | discoveryx { 2 | name = discoveryx 3 | server { 4 | naming { 5 | heartbeat-interval = 2.seconds 6 | } 7 | } 8 | client { 9 | naming { 10 | heartbeat-interval = 2.seconds 11 | } 12 | } 13 | 14 | akka { 15 | loglevel = INFO 16 | remote.artery.canonical.port = 49001 17 | cluster.seed-nodes = ["127.0.0.1:49001"] 18 | } 19 | } 20 | 21 | akka.persistence { 22 | journal { 23 | plugin = "jdbc-journal" 24 | plugin = ${?JOURNAL_PLUIGN} 25 | // auto-start-journals = ["jdbc-journal"] 26 | } 27 | snapshot-store { 28 | plugin = "jdbc-snapshot-store" 29 | plugin = ${?SNAPSHOT_PLUGIN} 30 | // auto-start-snapshot-stores = ["jdbc-snapshot-store"] 31 | } 32 | } 33 | 34 | include "persistence-postgres.conf" 35 | 36 | akka.grpc.client { 37 | "*" { 38 | use-tls = false 39 | host = "helloscala.com" 40 | port = 48000 41 | } 42 | "fusion.discoveryx.grpc.NamingService" {} 43 | "fusion.discoveryx.server.grpc.UserService" {} 44 | } 45 | -------------------------------------------------------------------------------- /web-console/src/components/PhotoSwipe/index.less: -------------------------------------------------------------------------------- 1 | #photoSwipe { 2 | a { 3 | display: unset; 4 | } 5 | 6 | .my-gallery { 7 | width: 100%; 8 | display: flex; 9 | align-items: center; 10 | justify-content: flex-start; 11 | flex-direction: column; 12 | } 13 | 14 | .my-gallery figure { 15 | display: block; 16 | margin: 10px 0 0 0; 17 | width: 100%; 18 | } 19 | 20 | .my-gallery img { 21 | width: 100%; 22 | height: auto; 23 | } 24 | 25 | .my-gallery figcaption { 26 | display: none; 27 | } 28 | 29 | //.pswp__counter { 30 | // height: 44px; /* no */ 31 | // font-size: 14px; /* no */ 32 | // line-height: 44px; /* no */ 33 | // padding: 0 10px; /* no */ 34 | //} 35 | // 36 | //.pswp__button { 37 | // width: 44px; /* no */ 38 | // height: 44px; /* no */ 39 | // background-size: 264px 88px; /* no */ 40 | //} 41 | // 42 | //.pswp__button--share { 43 | // background-position: -44px -44px; /* no */ 44 | //} 45 | // 46 | //.pswp__button--close { 47 | // background-position: 0 -44px; /* no */ 48 | //} 49 | } 50 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/json.md: -------------------------------------------------------------------------------- 1 | # JSON 说明 2 | 3 | ## Protobuf、JSON转换说明 4 | 5 | REST API使用JSON作为序例化,由Protobuf数据自动序例化为JSON(或反之)。 6 | 7 | 1. Protobuf里使用下划线定义的字段在序例化为JSON时将自动转换成驼峰格式; 8 | 2. 当某个字段未设置时,将使用Protobuf里定义的默认值。也就是说客户在提交请求时不需要每个字段都设置,同时,服务端返回结果时未设置的字段将使用默认值返回; 9 | 3. `oneof`类型字段在序例化为JSON时将不包含外层的包裹,将直接返回设置的字段。 10 | 11 | ## 示例 12 | 13 | **protobuf** 14 | 15 | @@snip [protocol](../../../../../discoveryx-server/src/main/protobuf/fusion/discoveryx/server/protocol/naming.proto) { #NamingResponse } 16 | 17 | **json** 18 | 19 | ```json 20 | { 21 | "status":200, 22 | "message":"", 23 | "serviceInfo":{ 24 | "namespace":"namespace", 25 | "serviceName":"fusion-discoveryx", 26 | "groupName":"default", 27 | "instances":[ 28 | { 29 | "instanceId":"127.0.0.1:8000", 30 | "ip":"127.0.0.1", 31 | "port":8000, 32 | "weight":0, 33 | "healthy":true, 34 | "enabled":false, 35 | "metadata":{ 36 | 37 | } 38 | } 39 | ] 40 | } 41 | } 42 | ``` -------------------------------------------------------------------------------- /discoveryx-server/src/test/resources/persistence-postgres.conf: -------------------------------------------------------------------------------- 1 | 2 | akka-persistence-jdbc { 3 | logicalDeletion.enable = false 4 | shared-databases { 5 | slick { 6 | profile = "slick.jdbc.PostgresProfile$" 7 | db { 8 | //dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource" 9 | host = "localhost" 10 | host = ${?POSTGRES_HOST} 11 | url = "jdbc:postgresql://"${akka-persistence-jdbc.shared-databases.slick.db.host}":5432/fusion_discoveryx?reWriteBatchedInserts=true" 12 | user = "devuser" 13 | user = ${?POSTGRES_USER} 14 | password = "devPass.2019" 15 | password = ${?POSTGRES_PASSWORD} 16 | driver = "org.postgresql.Driver" 17 | numThreads = 5 18 | maxConnections = 5 19 | minConnections = 1 20 | } 21 | } 22 | } 23 | } 24 | 25 | jdbc-journal { 26 | use-shared-db = "slick" 27 | } 28 | 29 | # the akka-persistence-snapshot-store in use 30 | jdbc-snapshot-store { 31 | use-shared-db = "slick" 32 | } 33 | 34 | # the akka-persistence-query provider in use 35 | jdbc-read-journal { 36 | use-shared-db = "slick" 37 | } 38 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/share/cassandra/schema/snapshot-schema.cql: -------------------------------------------------------------------------------- 1 | CREATE KEYSPACE IF NOT EXISTS akka_snapshot WITH REPLICATION = { 'class': 'SimpleStrategy', 'replication_factor': 1 }; 2 | 3 | CREATE TABLE IF NOT EXISTS akka_snapshot.snapshots 4 | ( 5 | persistence_id text, 6 | sequence_nr bigint, 7 | timestamp bigint, 8 | ser_id int, 9 | ser_manifest text, 10 | snapshot_data blob, 11 | snapshot blob, 12 | meta_ser_id int, 13 | meta_ser_manifest text, 14 | meta blob, 15 | PRIMARY KEY (persistence_id, sequence_nr) 16 | ) 17 | WITH CLUSTERING ORDER BY (sequence_nr DESC) AND gc_grace_seconds =864000 18 | AND compaction = { 19 | 'class' : 'SizeTieredCompactionStrategy', 20 | 'enabled' : true, 21 | 'tombstone_compaction_interval' : 86400, 22 | 'tombstone_threshold' : 0.2, 23 | 'unchecked_tombstone_compaction' : false, 24 | 'bucket_high' : 1.5, 25 | 'bucket_low' : 0.5, 26 | 'max_threshold' : 32, 27 | 'min_threshold' : 4, 28 | 'min_sstable_size' : 50 29 | }; 30 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/deploy/package.md: -------------------------------------------------------------------------------- 1 | # 软件包 2 | 3 | ## 下载 4 | 5 | 在 https://github.com/akka-fusion/fusion-discoveryx/releases 下载最新版本,解压缩到目录执行。 6 | 7 | **Unix/Linux** 8 | 9 | ```shell script 10 | unzip discoveryx-server-[VERSION].zip 11 | cd discoveryx-server 12 | ./bin/discoveryx-server 13 | ``` 14 | 15 | **Windows** 16 | 17 | ``` 18 | unzip discoveryx-server-[VERSION].zip 19 | cd discoveryx-server 20 | bin/discoveryx-server.bat 21 | ``` 22 | 23 | ## 源码构建 24 | 25 | ``` 26 | git clone https://github.com/akka-fusion/fusion-discoveryx.git 27 | cd fusion-discoveryx 28 | pushd web-console 29 | yarn && yarn build 30 | popd 31 | pushd ../scripts 32 | ./publish-dist.sh 33 | popd 34 | sbt "discoveryx-server" dist 35 | ``` 36 | 37 | @@@vars 38 | 最终将在 `discoveryx-server/target/universal` 目录生成 discoveryx-server-$version$.zip 软件包。解压生成的 **zip** 软件包(discoveryx-server-$version$.zip),执行 `bin` 目录里的 `sh` 或 `bat` 脚本即可运行程序。 39 | @@@ 40 | 41 | @@@vars 42 | ``` 43 | cd discoveryx-server/target/universal 44 | unzip discoveryx-server-$version$.zip 45 | cd discoveryx-server-$version$ 46 | ./bin/discoveryx-server 47 | ``` 48 | @@@ 49 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/route/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server 18 | 19 | import akka.http.scaladsl.server.Directives._ 20 | import akka.http.scaladsl.server.{ Directive, PathMatcher } 21 | 22 | package object route { 23 | def pathPost[L](pm: PathMatcher[L]): Directive[L] = path(pm) & post 24 | def pathGet[L](pm: PathMatcher[L]): Directive[L] = path(pm) & get 25 | def pathPut[L](pm: PathMatcher[L]): Directive[L] = path(pm) & put 26 | def pathDelete[L](pm: PathMatcher[L]): Directive[L] = path(pm) & delete 27 | } 28 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/util/SessionUtilsTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.util 18 | 19 | import java.net.URLEncoder 20 | import java.nio.charset.StandardCharsets 21 | 22 | import org.scalatest.wordspec.AnyWordSpec 23 | 24 | class SessionUtilsTest extends AnyWordSpec { 25 | "SessionUtilsTest" should { 26 | "tokenFromCookie" in { 27 | val str = URLEncoder.encode("abCD0/|_=-", StandardCharsets.UTF_8) 28 | println(str) 29 | } 30 | 31 | "decodeToken" in {} 32 | 33 | "generateSessionToken" in {} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/css/icons.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'icons'; 3 | font-weight: normal; 4 | font-style: normal; 5 | src: url('fonts/icons.eot?2'); 6 | src: url('fonts/icons.eot?2#iefix') format('embedded-opentype'), 7 | url('fonts/icons.woff2?2') format('woff2'), 8 | url('fonts/icons.woff?2') format('woff'), 9 | url('fonts/icons.ttf?2') format('truetype'), 10 | url('fonts/icons.svg?2#icons') format('svg'); 11 | } 12 | 13 | .icon-prev:before, 14 | .icon-next:before, 15 | .icon-up:before, 16 | .icon-down:before, 17 | .icon-link:before { 18 | font-family: "icons"; 19 | font-style: normal; 20 | font-weight: normal; 21 | speak: none; 22 | display: inline-block; 23 | text-decoration: inherit; 24 | width: 1em; 25 | margin-right: .2em; 26 | text-align: center; 27 | font-variant: normal; 28 | text-transform: none; 29 | line-height: 1em; 30 | -webkit-font-smoothing: antialiased; 31 | -moz-osx-font-smoothing: grayscale; 32 | } 33 | 34 | .icon-prev:before { content: '\3c'; } /* '<' */ 35 | .icon-next:before { content: '\3e'; } /* '>' */ 36 | .icon-up:before { content: '\5e'; } /* '^' */ 37 | .icon-down:before { content: '\76'; } /* 'v' */ 38 | .icon-link:before { content: '\a7'; } /* '§' */ 39 | -------------------------------------------------------------------------------- /web-console/static/html/loveOrHate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Love or Hate ? 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/deploy/other-persistence.md: -------------------------------------------------------------------------------- 1 | # 其它存储机制 2 | 3 | Fusion DiscoveryX 使用 Akka Persistence 作为存储层,且使用 Akka Persistence 来存储所有数据,所以理论上只要实现了 Akka Persistence Plugins 的存储系统都可应用于 Fusion DiscoveryX。 4 | 5 | 需要修改 `akka.persistence` 配置使用想要使用的存储插件,同时,还需要将相应插件依赖的 **jar** 包入到 Fusion DiscoveryX 软件 `lib` 目录里面。 6 | 7 | ## MongoDB 8 | 9 | - [https ://github.com/scullxbones/akka-persistence-mongo](https://github.com/scullxbones/akka-persistence-mongo) 10 | 11 | @@dependency[sbt,Gradle,Maven] { group="com.github.scullxbones" artifact="akka-persistence-mongo-rxmongo_2.12" version="$akka.persistence.mongo.version$" } 12 | 13 | ## DynamoDB 14 | 15 | - [https://github.com/akka/akka-persistence-dynamodb](https://github.com/akka/akka-persistence-dynamodb) 16 | 17 | @@dependency[sbt,Gradle,Maven] { group="com.typesafe.akka" artifact="akka-persistence-dynamodb_$scala.binary_version$" version="$akka.persistence.dynamodb.version$" } 18 | 19 | ## CouchBase 20 | 21 | - [https://github.com/akka/akka-persistence-couchbase](https://github.com/akka/akka-persistence-couchbase) 22 | 23 | @@dependency[sbt,Gradle,Maven] { group="com.lightbend.akka" artifact="akka-persistence-couchbase_$scala.binary_version$" version="$akka.persistence.couchbase.version$" } 24 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/akka/fusion/testkit/FusionActorTestKit.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package akka.fusion.testkit 18 | 19 | import akka.actor.testkit.typed.scaladsl.ActorTestKit 20 | import com.typesafe.config.ConfigFactory 21 | import fusion.common.config.FusionConfigFactory 22 | import fusion.discoveryx.common.Constants 23 | 24 | object FusionActorTestKit { 25 | def apply(): ActorTestKit = 26 | new ActorTestKit( 27 | name = Constants.DISCOVERYX, 28 | config = FusionConfigFactory.arrangeConfig(ConfigFactory.load("application-test"), Constants.DISCOVERYX), 29 | settings = None) 30 | } 31 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Info 2 | 3 | resolvers += Resolver.bintrayIvyRepo("2m", "sbt-plugins") 4 | 5 | addSbtPlugin("com.github.mwz" % "sbt-sonar" % "1.6.0") 6 | addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.2.0") 7 | addSbtPlugin("com.typesafe.sbt" % "sbt-multi-jvm" % "0.4.0") 8 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") 9 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.0") 10 | addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.5.2") 11 | addSbtPlugin("com.lightbend.akka" % "sbt-paradox-akka" % "0.29") 12 | addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0") 13 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") 14 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") 15 | addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.5") 16 | addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "0.7.3") 17 | addSbtPlugin("com.lightbend.sbt" % "sbt-java-formatter" % "0.4.4") 18 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.2.1") 19 | addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.7-1") 20 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.5") 21 | 22 | resolvers += Resolver.bintrayIvyRepo("helloscala", "ivy") 23 | addSbtPlugin("com.helloscala.fusion" % "fusion-sbt-plugin" % "2.0.6") 24 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/util/ActorTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.util 18 | 19 | import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit 20 | import akka.actor.typed.Behavior 21 | import akka.actor.typed.scaladsl.Behaviors 22 | import org.scalatest.WordSpecLike 23 | 24 | object ActorTest { 25 | def apply(): Behavior[String] = Behaviors.ignore 26 | } 27 | class ActorTest extends ScalaTestWithActorTestKit with WordSpecLike { 28 | "ActorTest" must { 29 | "ip-port" in { 30 | val ref = spawn(ActorTest(), "127.0.0.1:2882") 31 | println(ref) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /project/project-info.conf: -------------------------------------------------------------------------------- 1 | project-info { 2 | version: "current" 3 | scaladoc: "https://doc.akka.io/api/akka-http/current/akka/http/scaladsl" 4 | javadoc: "https://doc.akka.io/japi/akka-http/current" 5 | shared-info { 6 | jdk-versions: ["Adopt OpenJDK 8"] 7 | // snapshots: { } 8 | issues: { 9 | url: "https://github.com/akka-fusion/fusion-discoveryx/issues" 10 | text: "Fusion DiscoveryX - Github issues" 11 | } 12 | release-notes: { 13 | url: "https://github.com/akka-fusion/fusion-discoveryx/releases" 14 | text: "Fusion DiscoveryX - Github releases" 15 | } 16 | forums: [ 17 | { 18 | text: "akka-fusion/fusion-discoveryx Gitter channel" 19 | url: "https://gitter.im/akka-fusion/fusion-discoveryx" 20 | } 21 | ] 22 | levels: [ 23 | { 24 | readiness: CommunityDriven 25 | since: "2019-11-26" 26 | since-version: "0.1.0" 27 | } 28 | ] 29 | } 30 | fusion-discoveryx: ${project-info.shared-info} { 31 | title: "Fusion DiscoveryX" 32 | } 33 | discoveryx-client: ${project-info.shared-info} { 34 | title: "Fusion DiscoveryX Client" 35 | } 36 | discoveryx-client-play-ws: ${project-info.shared-info} { 37 | title: "Fusion DiscoveryX Client for Play" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /web-console/src/stores/GlobalStore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin global 5 | * */ 6 | 7 | import { action, observable } from 'mobx'; 8 | import request from '../utils/request'; 9 | 10 | export default class GlobalStore { 11 | /** 12 | * *************************** observable *************************** 13 | * */ 14 | 15 | @observable 16 | list = []; 17 | 18 | /** 19 | * ****************************** ajax ****************************** 20 | * */ 21 | 22 | /** 23 | * 获取数据 24 | * */ 25 | getList = async params => { 26 | const { data } = await request({ 27 | config: { method: 'GET', url: '/api/list', params }, 28 | }); 29 | this.setList(data); 30 | return data; 31 | }; 32 | 33 | /** 34 | * ***************************** action ***************************** 35 | * */ 36 | 37 | @action 38 | setList(data = []) { 39 | this.list = data; 40 | } 41 | 42 | /** 43 | * **************************** computed **************************** 44 | * */ 45 | // @computed 46 | // get computedData() { 47 | // if (this.msg.length > 0) { 48 | // return 'computed'; 49 | // } 50 | // return []; 51 | // } 52 | } 53 | -------------------------------------------------------------------------------- /web-console/static/html/loading/loading.css: -------------------------------------------------------------------------------- 1 | .spinner { 2 | margin: 100px auto; 3 | width: 60px; 4 | height: 60px; 5 | text-align: center; 6 | font-size: 10px; 7 | } 8 | 9 | .spinner > div { 10 | background-color: #1890FF; 11 | height: 100%; 12 | width: 6px; 13 | display: inline-block; 14 | 15 | -webkit-animation: stretchdelay 1.2s infinite ease-in-out; 16 | animation: stretchdelay 1.2s infinite ease-in-out; 17 | } 18 | 19 | .spinner .rect2 { 20 | -webkit-animation-delay: -1.1s; 21 | animation-delay: -1.1s; 22 | } 23 | 24 | .spinner .rect3 { 25 | -webkit-animation-delay: -1.0s; 26 | animation-delay: -1.0s; 27 | } 28 | 29 | .spinner .rect4 { 30 | -webkit-animation-delay: -0.9s; 31 | animation-delay: -0.9s; 32 | } 33 | 34 | .spinner .rect5 { 35 | -webkit-animation-delay: -0.8s; 36 | animation-delay: -0.8s; 37 | } 38 | 39 | @-webkit-keyframes stretchdelay { 40 | 0%, 40%, 100% { -webkit-transform: scaleY(0.4) } 41 | 20% { -webkit-transform: scaleY(1.0) } 42 | } 43 | 44 | @keyframes stretchdelay { 45 | 0%, 40%, 100% { 46 | transform: scaleY(0.4); 47 | -webkit-transform: scaleY(0.4); 48 | } 20% { 49 | transform: scaleY(1.0); 50 | -webkit-transform: scaleY(1.0); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/management/config.md: -------------------------------------------------------------------------------- 1 | # ConfigManagerService 2 | 3 | - gRPC服务地址:`/fusion.discoveryx.server.grpc.ConfigManagerService` 4 | - REST URL前缀:`/fusion/discoveryx/console/config` 5 | 6 | REST URL路径由 **REST URL前缀** + 服务名组织,均使用 **POST** 方法的请求,JSON序例化格式。如查询配置列表接口访问地址为:`POST /fusion/discoveryx/management/config/ListConfig`。Protobuf与JSON格式转换请参阅: @ref[JSON 说明](../json.md)。 7 | 8 | ## ListConfig 9 | 10 | **gRPC** 11 | 12 | @@snip [gRPC](../../../../../../discoveryx-server/src/main/protobuf/fusion/discoveryx/server/grpc/server.proto) { #ListConfig } 13 | 14 | **请求** 15 | 16 | @@snip [protocol](../../../../../../discoveryx-server/src/main/protobuf/fusion/discoveryx/server/protocol/config.proto) { #ListConfig } 17 | 18 | **响应** 19 | 20 | @@snip [protocol](../../../../../../discoveryx-server/src/main/protobuf/fusion/discoveryx/server/protocol/config.proto) { #ConfigResponse } 21 | 22 | `oneof`字段`listed`有效: 23 | 24 | @@snip [protocol](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigQueried } 25 | 26 | ## GetConfig 27 | 28 | 见 @ref[GetConfig](../open/config.md#getconfig) 。 29 | 30 | ## PublishConfig 31 | 32 | 见 @ref[PublishConfig](../open/config.md#publishconfig) 。 33 | 34 | ## RemoveConfig 35 | 36 | 见 @ref[RemoveConfig](../open/config.md#removeconfig) 。 37 | -------------------------------------------------------------------------------- /docs/js/page.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // close the overlay navigation when header links are clicked 4 | $(".overlay-nav .nav-toc a.header, .overlay-nav .nav-toc a.active.page").attr("data-toggle", "underlay overlay"); 5 | 6 | // TOCs support three styles: 7 | // - box: wrap in a shadowed box and apply TOC styling 8 | // - blocks: section TOCs in boxes in a block grid with equal heights 9 | // - list: regular list of links 10 | var tocs = $(".page-content .toc"); 11 | tocs.each(function() { 12 | var toc = $(this); 13 | // if there's no style already set then add .box for TOCs of depth 1 otherwise .blocks 14 | if (!(toc.hasClass("blocks") || toc.hasClass("box") || toc.hasClass("list"))) { 15 | toc.addClass((toc.children("ul").children("li").has("ul").length == 0) ? "box" : "blocks"); 16 | } 17 | if (toc.hasClass("blocks")) { 18 | var list = toc.children("ul"); 19 | list.addClass("row medium-up-2 large-up-3 toc-grid"); 20 | list.children("li").addClass("column column-block toc-block").attr("data-equalizer-watch", "").wrapInner("
"); 21 | new Foundation.Equalizer(list, { equalizeByRow: true, equalizeOn: "medium" }); 22 | } else if (toc.hasClass("box")) { 23 | toc.wrapInner("
"); 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /web-console/src/components/Loadable/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin react-loadable 5 | * */ 6 | 7 | import React from 'react'; 8 | import ReactLoadable from 'react-loadable'; 9 | import PropTypes from 'prop-types'; 10 | import './index.less'; 11 | 12 | const Loading = ({ error, timedOut, pastDelay }) => { 13 | if (error) { 14 | return ( 15 |
16 |

Error!

17 |
18 | ); 19 | } 20 | if (timedOut) { 21 | return ( 22 |
23 |

Taking a long time...

24 |
25 | ); 26 | } 27 | if (pastDelay) { 28 | return ( 29 |
30 |

Loading...

31 |
32 | ); 33 | } 34 | return null; 35 | }; 36 | 37 | Loading.propTypes = { 38 | error: PropTypes.bool, 39 | timedOut: PropTypes.bool, 40 | pastDelay: PropTypes.bool, 41 | }; 42 | 43 | Loading.defaultProps = { 44 | error: false, 45 | timedOut: false, 46 | pastDelay: false, 47 | }; 48 | 49 | const Loadable = opts => 50 | ReactLoadable({ 51 | loading: Loading, 52 | delay: 300, 53 | timeout: 10000, 54 | ...opts, 55 | }); 56 | 57 | export default Loadable; 58 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/scala/fusion/discoveryx/client/NamingClientDemo.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client 18 | 19 | import akka.actor.typed.ActorSystem 20 | import com.typesafe.config.ConfigFactory 21 | import fusion.common.FusionProtocol 22 | 23 | import scala.concurrent.Await 24 | import scala.concurrent.duration.Duration 25 | 26 | object NamingClientDemo { 27 | def main(args: Array[String]): Unit = { 28 | val system = ActorSystem(FusionProtocol.behavior, "NamingClient", ConfigFactory.load()) 29 | val settings = NamingClientSettings(system) 30 | val namingClient = NamingClient(settings, system) 31 | 32 | val f = namingClient.registerOnSettings() 33 | val reply = Await.result(f, Duration.Inf) 34 | println(reply) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /web-console/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function config(api) { 2 | const presets = [ 3 | [ 4 | '@babel/env', 5 | { 6 | modules: false, 7 | useBuiltIns: 'usage', 8 | corejs: 3, 9 | }, 10 | ], 11 | '@babel/react', 12 | ]; 13 | const plugins = [ 14 | // Stage 1 15 | '@babel/plugin-proposal-export-default-from', 16 | '@babel/plugin-proposal-logical-assignment-operators', 17 | ['@babel/plugin-proposal-pipeline-operator', { proposal: 'minimal' }], 18 | 19 | // Stage 2 20 | ['@babel/plugin-proposal-decorators', { legacy: true }], 21 | '@babel/plugin-proposal-function-sent', 22 | '@babel/plugin-proposal-export-namespace-from', 23 | '@babel/plugin-proposal-numeric-separator', 24 | '@babel/plugin-proposal-throw-expressions', 25 | 26 | // Stage 3 27 | ['@babel/plugin-proposal-nullish-coalescing-operator', { loose: false }], 28 | ['@babel/plugin-proposal-optional-chaining', { loose: false }], 29 | '@babel/plugin-syntax-dynamic-import', 30 | '@babel/plugin-syntax-import-meta', 31 | ['@babel/plugin-proposal-class-properties', { loose: true }], 32 | '@babel/plugin-proposal-json-strings', 33 | [ 34 | 'import', 35 | { 36 | libraryName: 'antd', 37 | style: true, 38 | }, 39 | ], 40 | ]; 41 | 42 | api.cache(true); 43 | return { 44 | presets, 45 | plugins, 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /web-console/src/components/Breadcrumb/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 面包屑导航,根据route.js生成 5 | * */ 6 | 7 | import React from 'react'; 8 | import { Link, withRouter } from 'react-router-dom'; 9 | import { Breadcrumb as AntdBreadcrumb, PageHeader } from 'antd'; 10 | import { breadcrumbNameMap, routes } from '../../router'; 11 | import './index.less'; 12 | 13 | const Breadcrumb = props => { 14 | const { 15 | location: { pathname }, 16 | } = props.history; 17 | 18 | const pathSnippets = pathname.split('/').filter(i => i); 19 | const extraBreadcrumbItems = pathSnippets.map((_, index) => { 20 | const url = `/${pathSnippets.slice(0, index + 1).join('/')}`; 21 | 22 | if (!routes.find(({ path }) => path === url) || index + 1 === pathSnippets.length) { 23 | return {breadcrumbNameMap[url]}; 24 | } 25 | return ( 26 | 27 | {breadcrumbNameMap[url]} 28 | 29 | ); 30 | }); 31 | 32 | const breadcrumb = {extraBreadcrumbItems}; 33 | 34 | return ; 35 | }; 36 | 37 | export default withRouter(props => ); 38 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/config/ConfigSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.config 18 | 19 | import akka.actor.typed.ActorSystem 20 | import com.typesafe.config.Config 21 | import fusion.discoveryx.common.Constants 22 | import fusion.discoveryx.server.{ BaseSettings, RetentionCriteriaSettings } 23 | import helloscala.common.Configuration 24 | 25 | final class ConfigSettings(configuration: Configuration) extends BaseSettings with RetentionCriteriaSettings { 26 | val c = configuration.getConfiguration(s"${Constants.DISCOVERYX}.server.config") 27 | } 28 | 29 | object ConfigSettings { 30 | def apply(system: ActorSystem[_]): ConfigSettings = apply(system.settings.config) 31 | def apply(config: Config): ConfigSettings = new ConfigSettings(Configuration(config)) 32 | } 33 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/share/jdbc/schema/oracle/oracle-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE SEQUENCE "ordering_seq" START WITH 1 INCREMENT BY 1 NOMAXVALUE 2 | / 3 | 4 | CREATE TABLE "journal" ( 5 | "ordering" NUMERIC, 6 | "deleted" char check ("deleted" in (0,1)) NOT NULL, 7 | "persistence_id" VARCHAR(255) NOT NULL, 8 | "sequence_number" NUMERIC NOT NULL, 9 | "tags" VARCHAR(255) DEFAULT NULL, 10 | "message" BLOB NOT NULL, 11 | PRIMARY KEY("persistence_id", "sequence_number") 12 | ) 13 | / 14 | 15 | CREATE UNIQUE INDEX "journal_ordering_idx" ON "journal"("ordering") 16 | / 17 | 18 | CREATE OR REPLACE TRIGGER "ordering_seq_trigger" 19 | BEFORE INSERT ON "journal" 20 | FOR EACH ROW 21 | BEGIN 22 | SELECT "ordering_seq".NEXTVAL INTO :NEW."ordering" FROM DUAL; 23 | END; 24 | / 25 | 26 | CREATE OR REPLACE PROCEDURE "reset_sequence" 27 | IS 28 | l_value NUMBER; 29 | BEGIN 30 | EXECUTE IMMEDIATE 'SELECT "ordering_seq".nextval FROM dual' INTO l_value; 31 | EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY -' || l_value || ' MINVALUE 0'; 32 | EXECUTE IMMEDIATE 'SELECT "ordering_seq".nextval FROM dual' INTO l_value; 33 | EXECUTE IMMEDIATE 'ALTER SEQUENCE "ordering_seq" INCREMENT BY 1 MINVALUE 0'; 34 | END; 35 | / 36 | 37 | CREATE TABLE "snapshot" ( 38 | "persistence_id" VARCHAR(255) NOT NULL, 39 | "sequence_number" NUMERIC NOT NULL, 40 | "created" NUMERIC NOT NULL, 41 | "snapshot" BLOB NOT NULL, 42 | PRIMARY KEY ("persistence_id", "sequence_number") 43 | ) 44 | / -------------------------------------------------------------------------------- /discoveryx-client/src/main/scala/fusion/discoveryx/client/DefaultNamingClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client 18 | 19 | import akka.actor.typed.{ ActorSystem, ExtensionId } 20 | 21 | import scala.util.{ Failure, Success } 22 | 23 | object DefaultNamingClient extends ExtensionId[NamingClient] { 24 | override def createExtension(system: ActorSystem[_]): NamingClient = { 25 | val client = NamingClient(system) 26 | if (NamingClientSettings(system).autoRegistration) { 27 | client 28 | .registerOnSettings() 29 | .onComplete { 30 | case Success(value) => system.log.info(s"Auto registration success, return is $value.") 31 | case Failure(exception) => system.log.error("Auto registration failure.", exception) 32 | }(system.executionContext) 33 | } 34 | client 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /web-console/static/html/loading/webpack.loading.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 4 | 5 | module.exports = { 6 | entry: { 7 | loading: ["babel-polyfill", path.join(__dirname, "loading")], 8 | }, 9 | output: { 10 | path: path.join(__dirname, "../../../dist/static/html/loading"), 11 | filename: "[name].js", 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.js$/, 17 | include: path.join(__dirname), 18 | loader: "babel-loader", 19 | }, 20 | { 21 | test: /\.css$/, 22 | include: path.join(__dirname), 23 | use: "css-loader", 24 | }, 25 | ], 26 | }, 27 | plugins: [ 28 | new webpack.optimize.UglifyJsPlugin({ 29 | uglifyOptions: { 30 | minimize: true, 31 | ie8: false, 32 | output: { 33 | comments: false, 34 | beautify: false, 35 | }, 36 | mangle: { 37 | keep_fnames: true, 38 | }, 39 | compress: { 40 | warnings: false, 41 | // drop_console: true, 42 | drop_debugger: true, 43 | unused: true, 44 | }, 45 | }, 46 | }), 47 | new CopyWebpackPlugin([ 48 | { 49 | from: path.resolve(__dirname), 50 | to: path.join(__dirname, "../../../dist/static/html/loading"), 51 | ignore: ["*.js"], 52 | }, 53 | ]), 54 | ], 55 | }; 56 | -------------------------------------------------------------------------------- /discoveryx-docs/docs/tmp.md: -------------------------------------------------------------------------------- 1 | ConfigManagerServiceImpl: 2 | 3 | ```scala 4 | readJournal 5 | .eventsByTag(ConfigEntity.TypeKey.name, Offset.noOffset) 6 | .filter(event => event.event.isInstanceOf[ChangedConfigEvent]) 7 | .mapConcat { event => 8 | event.persistenceId.split("\\" + PersistenceId.DefaultSeparator) match { 9 | case Array(_, ConfigEntity.ConfigKey(configKey)) if event.event.isInstanceOf[ChangedConfigEvent] => 10 | (configKey, event.event.asInstanceOf[ChangedConfigEvent]) :: Nil 11 | case _ => Nil 12 | } 13 | } 14 | .groupedWithin(1024, 5.seconds) 15 | .runForeach { list => 16 | for ((namespace, items) <- list.groupBy(_._1.namespace)) { 17 | configManager ! ShardingEnvelope(namespace, InitialConfigKeyEvents(items.map { 18 | case (key, event) => ConfigKeyEvent(key, event) 19 | })) 20 | } 21 | } 22 | ``` 23 | 24 | ConfigManager: 25 | 26 | ```scala 27 | case InitialConfigKeyEvents(keyEvents) => 28 | var tmpKeys = Vector.empty[ConfigKey] 29 | for (keyEvent <- keyEvents) { 30 | if (!dataIds.contains(keyEvent.key)) { 31 | tmpKeys :+= keyEvent.key 32 | } 33 | configEntity ! ShardingEnvelope(ConfigEntity.ConfigKey.makeEntityId(keyEvent.key), keyEvent.event.con) 34 | } 35 | dataIds ++= tmpKeys 36 | Behaviors.same 37 | 38 | .receiveSignal { 39 | case (_, Terminated(ref)) => 40 | configRefs = configRefs.filterNot(_ == ref) 41 | Behaviors.same 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/images/akka-logo-reverse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /discoveryx-common/src/main/scala/fusion/discoveryx/common/Constants.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.common 18 | 19 | import fusion.discoveryx.model.HealthyCheckProtocol 20 | 21 | object Constants { 22 | val DISCOVERYX = "discoveryx" 23 | val DEFAULT_GROUP_NAME = "default" 24 | val ENTITY_ID_SEPARATOR = ':' 25 | 26 | val MANAGEMENT = "management" 27 | val CONFIG = "config" 28 | val NAMING = "naming" 29 | 30 | val SESSION_TOKEN_NAME = "discoveryx-session-token" 31 | } 32 | 33 | object Headers { 34 | val NAMESPACE = "x-discoveryx-namespace" 35 | val SERVICE_NAME = "x-discoveryx-service-name" 36 | val IP = "x-discoveryx-ip" 37 | val PORT = "x-discoveryx-port" 38 | val INSTANCE_ID = "x-discoveryx-instance-id" 39 | } 40 | 41 | object Protocols { 42 | @inline def formatProtocol(protocol: HealthyCheckProtocol): HealthyCheckProtocol = 43 | if (protocol.isUnknown || protocol.isUnrecognized) HealthyCheckProtocol.HTTP else protocol 44 | } 45 | -------------------------------------------------------------------------------- /web-console/src/pages/config/management/Detail/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 2019/12/20 gongtiexin 服务详情 5 | * */ 6 | 7 | import React, { Component } from 'react'; 8 | import { inject, observer } from 'mobx-react'; 9 | import qs from 'query-string'; 10 | import { Descriptions, Table } from 'antd'; 11 | import PropTypes from 'prop-types'; 12 | import { CONFIG_TYPE_ENUM } from '../../constants'; 13 | 14 | @inject(({ store: { configStore } }) => ({ configStore })) 15 | @observer 16 | export default class Detail extends Component { 17 | static propTypes = { 18 | configStore: PropTypes.object.isRequired, 19 | }; 20 | 21 | componentDidMount() { 22 | this.props.configStore.getConfig(qs.parse(window.location.search)); 23 | } 24 | 25 | componentWillUnmount() { 26 | this.props.configStore.setConfig(); 27 | } 28 | 29 | render() { 30 | const { 31 | configStore: { config }, 32 | } = this.props; 33 | 34 | return ( 35 | 36 | {config.namespace} 37 | {config.dataId} 38 | {config.groupName} 39 | 40 | {CONFIG_TYPE_ENUM.properties[config.type]?.label} 41 | 42 | {config.content} 43 | 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/ManagementSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server 18 | 19 | import akka.actor.typed.ActorSystem 20 | import com.typesafe.config.Config 21 | import fusion.discoveryx.common.Constants 22 | import helloscala.common.Configuration 23 | 24 | import scala.concurrent.duration.FiniteDuration 25 | 26 | class ManagementSettings(configuration: Configuration) extends BaseSettings with RetentionCriteriaSettings { 27 | val c: Configuration = configuration.getConfiguration(s"${Constants.DISCOVERYX}.server.management") 28 | lazy val sessionTimeout: Long = c.get[FiniteDuration]("session-timeout").toMillis 29 | 30 | @inline def isValidSession(activeTime: Long) = (System.currentTimeMillis() - activeTime) < sessionTimeout 31 | } 32 | 33 | object ManagementSettings { 34 | def apply(system: ActorSystem[_]): ManagementSettings = apply(system.settings.config) 35 | def apply(config: Config): ManagementSettings = new ManagementSettings(Configuration(config)) 36 | } 37 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/conf/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logs/server.log 5 | false 6 | 7 | logs/server_%d{yyyy-MM-dd}.log 8 | 9 | 10 | [%date{ISO8601}] [%level] [%logger] [%marker] [%thread] - %msg {%mdc}%n 11 | 12 | 13 | 14 | 15 | 8192 16 | true 17 | 18 | 19 | 20 | 21 | 22 | DEBUG 23 | 24 | 25 | [%date{ISO8601}] [%level] [%logger] [%thread] [%X{akkaSource}] - %msg%n 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/naming/NamingSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.naming 18 | 19 | import akka.actor.typed.ActorSystem 20 | import com.typesafe.config.Config 21 | import fusion.discoveryx.common.Constants 22 | import fusion.discoveryx.server.{ BaseSettings, RetentionCriteriaSettings } 23 | import helloscala.common.Configuration 24 | 25 | import scala.concurrent.duration.FiniteDuration 26 | 27 | final class NamingSettings(configuration: Configuration) extends BaseSettings with RetentionCriteriaSettings { 28 | override val c = configuration.getConfiguration(s"${Constants.DISCOVERYX}.server.naming") 29 | val heartbeatTimeout: FiniteDuration = c.get[FiniteDuration]("heartbeat-timeout") 30 | val allowReplaceRegistration: Boolean = c.getBoolean("allow-replace-registration") 31 | } 32 | 33 | object NamingSettings { 34 | def apply(system: ActorSystem[_]): NamingSettings = apply(system.settings.config) 35 | def apply(config: Config): NamingSettings = new NamingSettings(Configuration(config)) 36 | } 37 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/naming/internal/SniffUtilsTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.naming.internal 18 | 19 | import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit 20 | import org.scalatest.FunSuiteLike 21 | 22 | import scala.concurrent.Await 23 | import scala.concurrent.duration._ 24 | 25 | class SniffUtilsTest extends ScalaTestWithActorTestKit with FunSuiteLike { 26 | test("testSniffTcp") { 27 | val result = Await.result(SniffUtils.sniffTcp(false, "114.67.71.244", 80), 1.minute) 28 | println(result) 29 | result shouldBe true 30 | } 31 | 32 | test("testSniffUdp") { 33 | val result = SniffUtils.sniffUdp("localhost", 137).futureValue 34 | result shouldBe true 35 | } 36 | 37 | test("testSniffHttp") { 38 | val result = SniffUtils.sniffHttp("http://gitee.com").futureValue 39 | result shouldBe true 40 | } 41 | 42 | test("testSniffHttps") { 43 | val result = SniffUtils.sniffHttp("https://gitee.com").futureValue 44 | result shouldBe true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web-console/README.md: -------------------------------------------------------------------------------- 1 |

Welcome to react-template 👋

2 |

3 | Version 4 | 5 | Documentation 6 | 7 | 8 | Maintenance 9 | 10 | 11 | License: ISC 12 | 13 |

14 | 15 | > A frontend project template for React.js 16 | 17 | ### 🏠 [Homepage](https://github.com/gongtiexin/react-template#readme) 18 | 19 | ## Install 20 | 21 | ```sh 22 | yarn 23 | ``` 24 | 25 | ## Author 26 | 27 | 👤 **tiexin.gong ** 28 | 29 | - Github: [@gongtiexin](https://github.com/gongtiexin) 30 | 31 | ## 🤝 Contributing 32 | 33 | Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/gongtiexin/react-template/issues). 34 | 35 | ## Show your support 36 | 37 | Give a ⭐️ if this project helped you! 38 | 39 | ## 📝 License 40 | 41 | Copyright © 2019 [tiexin.gong ](https://github.com/gongtiexin).
42 | 43 | 44 | 45 | --- 46 | 47 | _This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | 3 | sudo: false 4 | 5 | #scala: 6 | # - "2.12.10" 7 | # - "2.13.1" 8 | 9 | before_install: curl -Ls https://git.io/jabba | bash && . ~/.jabba/jabba.sh 10 | install: jabba install "adopt@~1.$TRAVIS_JDK.0-0" && jabba use "$_" && java -Xmx32m -version 11 | 12 | addons: 13 | apt: 14 | packages: 15 | - graphviz 16 | 17 | before_cache: 18 | - find $HOME/.ivy2 -name "ivydata-*.properties" -print -delete 19 | - find $HOME/.sbt -name "*.lock" -print -delete 20 | 21 | cache: 22 | directories: 23 | - $HOME/.cache/coursier 24 | - $HOME/.ivy2/cache 25 | - $HOME/.sbt 26 | - $HOME/.jabba/jdk 27 | 28 | #git: 29 | # depth: 1 # Avoid sbt-dynver not seeing the tag 30 | 31 | branches: 32 | only: 33 | - master 34 | - develop 35 | 36 | env: 37 | global: 38 | - TRAVIS_JDK=11 39 | 40 | stages: 41 | - test 42 | if: (branch = master) AND (NOT (type IN (push, pull_request))) 43 | - name: test-scala212 44 | if: (branch = develop) 45 | # - paradox 46 | 47 | jobs: 48 | include: 49 | - stage: test 50 | script: sbt test:compile "discoveryx-functest/multi-jvm:testOnly fusion.discoveryx.functest.DiscoveryXMultiTest" 51 | name: "Master discoveryx-functest/multi-jvm:testOnly fusion.discoveryx.functest.DiscoveryXMultiTest" 52 | - stage: test-scala212 53 | script: sbt ";++ 2.12.10 ;test:compile ;discoveryx-functest/multi-jvm:testOnly fusion.discoveryx.functest.DiscoveryXMultiTest" 54 | env: TRAVIS_JDK=8 55 | name: "Develop discoveryx-functest/multi-jvm:testOnly fusion.discoveryx.functest.DiscoveryXMultiTest for Java 8" 56 | # - stage: paradox 57 | # script: scripts/travis-paradox.sh 58 | # name: "Generate paradox" 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fusion DiscoveryX 2 | 3 | [![Build Status](https://travis-ci.org/akka-fusion/fusion-discoveryx.svg?branch=master)](https://travis-ci.org/akka-fusion/fusion-discoveryx) 4 | 5 | 服务注册与发现管理系统,基于Akka生态开发。 6 | 7 | 产品文档:[https://akka-fusion.github.io/fusion-discoveryx/](https://akka-fusion.github.io/fusion-discoveryx/) 8 | 9 | ## 开发 10 | 11 | ### Docker运行环境 12 | 13 | ```shell script 14 | docker-compose -f docker-compose.yml up -d // --build 15 | ``` 16 | 17 | > *若使用Cassandra做为后端存储,还需要初始化keyspace和相关表(PostgreSQL,Dockerfile已经初始化数据库表)。* 18 | > 19 | > ```shell script 20 | > docker exec fusion-discoveryx_cassandra cqlsh -f /docker-entrypoint-initdb.d/cassandra-schema.cql 21 | > ``` 22 | 23 | `-d`后可添加参数:`postgres`或`cassandra`只启动特定数据库。 24 | 25 | ### 服务发现测试 26 | 27 | *默认使用akka-persistence-jdbc(postgres) 数据库,若需要使用akka-persistence-cassandra请将测试类的配置文件修改为`application-test_cassandra.conf`。* 28 | 29 | ```sbtshell 30 | > discoveryx-server/testOnly fusion.discoveryx.server.naming.route.NamingManagementRouteTest 31 | ``` 32 | 33 | **配置管理测试** 34 | 35 | ```sbtshell 36 | > discoveryx-server/testOnly fusion.discoveryx.server.config.route.ConfigManagementRouteTest 37 | ``` 38 | 39 | ### Multi Node 测试 40 | 41 | ```sbtshell 42 | > discoveryx-functest/multi-jvm:testOnly fusion.discoveryx.functest.DiscoveryXMultiTest 43 | ``` 44 | 45 | ## Package, Deployment 46 | 47 | ``` 48 | ./release.sh 49 | ``` 50 | 51 | 生成的zip软件包在目录:`discoveryx-server/target/universal`。 52 | 53 | ## 技术 54 | 55 | | 功能 | 使用技术 | 56 | | ---------- | --------------------- | 57 | | 开放API | Akka gRPC | 58 | | 集群序例化 | Protobuf | 59 | | 配置持久化 | Akka Persistence | 60 | | 容错与扩展 | Akka Cluster Sharding | 61 | | REST | Akka HTTP | 62 | 63 | -------------------------------------------------------------------------------- /web-console/src/components/Pagination/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 分页组件 5 | * */ 6 | 7 | import React, { Component } from 'react'; 8 | import { observer } from 'mobx-react'; 9 | import { Pagination as AntdPagination } from 'antd'; 10 | import PropTypes from 'prop-types'; 11 | import './index.less'; 12 | 13 | @observer 14 | export default class Pagination extends Component { 15 | static propTypes = { 16 | page: PropTypes.number.isRequired, 17 | size: PropTypes.number, 18 | totalElements: PropTypes.number.isRequired, 19 | handleChange: PropTypes.func.isRequired, 20 | showSizeChanger: PropTypes.bool, 21 | }; 22 | 23 | static defaultProps = { 24 | size: 10, 25 | showSizeChanger: true, 26 | }; 27 | 28 | onShowSizeChange = (current, pageSize) => { 29 | const { handleChange } = this.props; 30 | handleChange({ page: current, size: pageSize }); 31 | }; 32 | 33 | onChange = (pageNumber, pageSize) => { 34 | const { handleChange } = this.props; 35 | handleChange({ page: pageNumber, size: pageSize }); 36 | }; 37 | 38 | showTotal = total => `共 ${total || 0} 条`; 39 | 40 | render() { 41 | const { page, size, totalElements, showSizeChanger } = this.props; 42 | 43 | return ( 44 | 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /discoveryx-functest/src/multi-jvm/scala/akka/remote/testkit/STMultiNodeSpec.scala: -------------------------------------------------------------------------------- 1 | package akka.remote.testkit 2 | 3 | import akka.actor.typed.ActorSystem 4 | import com.typesafe.config.{ Config, ConfigFactory } 5 | import fusion.discoveryx.server.DiscoveryX 6 | import org.scalatest.concurrent.ScalaFutures 7 | import org.scalatest.time.{ Millis, Span } 8 | import org.scalatest._ 9 | 10 | import scala.language.implicitConversions 11 | 12 | trait STMultiNodeSpec 13 | extends MultiNodeSpecCallbacks 14 | with WordSpecLike 15 | with Matchers 16 | with OptionValues 17 | with EitherValues 18 | with BeforeAndAfterAll 19 | with ScalaFutures { 20 | self: SchudulerXMultiNodeSpec => 21 | 22 | override def initialParticipants: Int = roles.size 23 | 24 | implicit override def patienceConfig: PatienceConfig = 25 | PatienceConfig(scaled(Span(5000, Millis)), scaled(Span(50, Millis))) 26 | 27 | def typedSystem: ActorSystem[_] = discoveryX.system 28 | 29 | override def beforeAll(): Unit = multiNodeSpecBeforeAll() 30 | 31 | override def afterAll(): Unit = multiNodeSpecAfterAll() 32 | 33 | // Might not be needed anymore if we find a nice way to tag all logging from a node 34 | implicit override def convertToWordSpecStringWrapper(s: String): WordSpecStringWrapper = 35 | new WordSpecStringWrapper(s"$s (on node '${self.myself.name}', $getClass)") 36 | } 37 | 38 | abstract class SchudulerXMultiNodeSpec(nodeConfig: MultiNodeConfig, protected val discoveryX: DiscoveryX) 39 | extends MultiNodeSpec(nodeConfig.myself, discoveryX.classicSystem, nodeConfig.roles, nodeConfig.deployments) { 40 | def this(nodeConfig: MultiNodeConfig, create: Config => DiscoveryX) { 41 | this(nodeConfig, create(ConfigFactory.load(nodeConfig.config))) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /discoveryx-client/src/main/scala/fusion/discoveryx/client/ConfigClientSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client 18 | 19 | import akka.actor.typed.ActorSystem 20 | import com.typesafe.config.Config 21 | import fusion.discoveryx.common.Constants 22 | import helloscala.common.Configuration 23 | 24 | object ConfigClientSettings { 25 | def apply(system: ActorSystem[_]): ConfigClientSettings = fromConfig(system.settings.config) 26 | def fromConfig(config: Config, prefix: String = s"${Constants.DISCOVERYX}.client.config"): ConfigClientSettings = 27 | fromConfiguration(Configuration(config), prefix) 28 | def fromConfiguration( 29 | config: Configuration, 30 | prefix: String = s"${Constants.DISCOVERYX}.client.config"): ConfigClientSettings = 31 | new ConfigClientSettings(config.getConfiguration(prefix)) 32 | } 33 | final class ConfigClientSettings private (c: Configuration) { 34 | val namespace: Option[String] = c.get[Option[String]]("namespace") 35 | val dataId: Option[String] = c.get[Option[String]]("data-id") orElse c.get[Option[String]]("dataId") 36 | override def toString: String = c.underlying.root().toString 37 | } 38 | -------------------------------------------------------------------------------- /web-console/src/components/NamespaceChoose/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { inject, observer } from 'mobx-react'; 3 | import { Tabs } from 'antd'; 4 | import PropTypes from 'prop-types'; 5 | import { upStorage } from 'up-utils'; 6 | 7 | const { TabPane } = Tabs; 8 | 9 | @inject(({ store: { namespaceStore } }) => ({ namespaceStore })) 10 | @observer 11 | export default class NamespaceChoose extends Component { 12 | static propTypes = { 13 | namespaceStore: PropTypes.object.isRequired, 14 | fetchData: PropTypes.func.isRequired, 15 | }; 16 | 17 | constructor(props) { 18 | super(props); 19 | this.state = { namespace: upStorage.getSession('namespace') }; 20 | } 21 | 22 | componentDidMount() { 23 | this.props.namespaceStore.getNamespaceList().then(namespaceList => { 24 | let { namespace } = this.state; 25 | if (!namespaceList.some(item => item.namespace === upStorage.getSession('namespace'))) { 26 | namespace = namespaceList[0]?.namespace; 27 | } 28 | this.handleNamespaceChange(namespace); 29 | }); 30 | } 31 | 32 | componentWillUnmount() { 33 | this.props.namespaceStore.setNamespaceList(); 34 | } 35 | 36 | handleNamespaceChange = namespace => { 37 | this.setState({ namespace }); 38 | upStorage.setSession('namespace', namespace); 39 | this.props.fetchData(namespace); 40 | }; 41 | 42 | render() { 43 | const { 44 | namespaceStore: { namespaceList }, 45 | } = this.props; 46 | const { namespace } = this.state; 47 | 48 | return ( 49 | 50 | {namespaceList.map(item => ( 51 | 52 | ))} 53 | 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /discoveryx-client/src/test/scala/fusion/discoveryx/client/DefaultNamingClientTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client 18 | 19 | import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit 20 | import com.typesafe.config.ConfigFactory 21 | import fusion.discoveryx.model.InstanceQuery 22 | import org.scalatest.wordspec.AnyWordSpecLike 23 | 24 | class DefaultNamingClientTest 25 | extends ScalaTestWithActorTestKit(ConfigFactory.load("application-local.conf")) 26 | with AnyWordSpecLike { 27 | private val client = DefaultNamingClient(system) 28 | "DefaultNamingClient" must { 29 | "url" in { 30 | val url = "http://fusion-schedulerx/cluster/health" 31 | // val uris = Future.sequence(Vector.fill(4)(client.generateUri(url))).futureValue 32 | val uris = (0 until 4).map(_ => client.generateUri(url).futureValue).toVector 33 | println(uris) 34 | } 35 | 36 | "queryService" in { 37 | val reply = client.queryInstance(InstanceQuery(client.settings.namespace.get, "fusion-schedulerx")).futureValue 38 | println(reply.status) 39 | reply.getServiceInfo.instances.foreach(println) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/config/ConfigManager.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.config 18 | 19 | import akka.actor.typed.scaladsl.Behaviors 20 | import akka.actor.typed.{ ActorRef, ActorSystem, Behavior } 21 | import akka.cluster.sharding.typed.scaladsl.{ ClusterSharding, Entity, EntityTypeKey } 22 | import akka.cluster.sharding.typed.{ ClusterShardingSettings, ShardingEnvelope } 23 | import fusion.discoveryx.server.config.internal.ConfigManagerBehavior 24 | 25 | import scala.concurrent.duration._ 26 | 27 | object ConfigManager { 28 | trait Command 29 | trait Event 30 | 31 | val NAME = "ConfigManager" 32 | val TypeKey: EntityTypeKey[Command] = EntityTypeKey(NAME) 33 | 34 | def init(system: ActorSystem[_]): ActorRef[ShardingEnvelope[Command]] = 35 | ClusterSharding(system).init( 36 | Entity(TypeKey)(entityContext => apply(entityContext.entityId)) 37 | .withSettings(ClusterShardingSettings(system).withPassivateIdleEntityAfter(Duration.Zero))) 38 | 39 | private def apply(entityId: String): Behavior[Command] = 40 | Behaviors.setup(context => new ConfigManagerBehavior(entityId, context).eventSourcedBehavior()) 41 | } 42 | -------------------------------------------------------------------------------- /web-console/src/router/feature.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Redirect, Route } from 'react-router-dom'; 3 | import PropTypes from 'prop-types'; 4 | 5 | // class RecordRoute extends PureComponent { 6 | // // static propTypes = {}; 7 | // 8 | // // constructor(props) { 9 | // // super(props); 10 | // // } 11 | // 12 | // componentDidMount() {} 13 | // 14 | // componentWillUnmount() {} 15 | // 16 | // render() { 17 | // return ; 18 | // } 19 | // } 20 | // 21 | // const PrivateRoute = ({ 22 | // component: RouteComponent, 23 | // loggedIn, 24 | // protect, 25 | // ...rest 26 | // }) => ( 27 | // 30 | // loggedIn || !protect ? ( 31 | // 32 | // ) : ( 33 | // 38 | // ) 39 | // } 40 | // /> 41 | // ); 42 | 43 | const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => { 44 | return ( 45 | 48 | isAuthenticated ? ( 49 | 50 | ) : ( 51 | 57 | ) 58 | } 59 | /> 60 | ); 61 | }; 62 | 63 | PrivateRoute.propTypes = { 64 | component: PropTypes.func.isRequired, 65 | isAuthenticated: PropTypes.bool, 66 | }; 67 | 68 | PrivateRoute.defaultProps = { 69 | isAuthenticated: true, 70 | }; 71 | 72 | export { PrivateRoute }; 73 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/scaladsl/module/DiscoveryXWSModule.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.scaladsl.module 18 | 19 | import akka.actor.typed.ActorSystem 20 | import fusion.discoveryx.client.play.scaladsl.{ DiscoveryXPlay, DiscoveryXPlayWSClient, DiscoveryXWSClient } 21 | import fusion.discoveryx.common.Constants 22 | import javax.inject.{ Inject, Provider, Singleton } 23 | import play.api.inject.{ SimpleModule, bind } 24 | import play.api.libs.ws.WSClient 25 | import play.shaded.ahc.org.asynchttpclient.AsyncHttpClient 26 | 27 | class DiscoveryXWSModule 28 | extends SimpleModule( 29 | bind[DiscoveryXPlayWSClient].toProvider[DiscoveryXPlayWSClientProvider], 30 | bind[WSClient].qualifiedWith(Constants.DISCOVERYX).to[DiscoveryXPlayWSClient], 31 | bind[WSClient].qualifiedWith[DiscoveryXPlay].to[DiscoveryXPlayWSClient]) 32 | 33 | @Singleton 34 | class DiscoveryXPlayWSClientProvider @Inject() (asyncHttpClient: AsyncHttpClient)(implicit system: ActorSystem[_]) 35 | extends Provider[DiscoveryXPlayWSClient] { 36 | override lazy val get: DiscoveryXPlayWSClient = DiscoveryXWSClient.wsClient(asyncHttpClient) 37 | } 38 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/javadsl/DiscoveryXPlayWSClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.javadsl 18 | 19 | import akka.actor.typed.ActorSystem 20 | import fusion.discoveryx.client.{ DefaultNamingClient, NamingClient } 21 | import play.libs.ws.{ WSClient, WSRequest } 22 | 23 | import scala.concurrent.duration._ 24 | import scala.concurrent.{ Await, Future } 25 | 26 | final class DiscoveryXPlayWSClient(client: WSClient)(implicit system: ActorSystem[_]) extends WSClient { 27 | val namingClient: NamingClient = DefaultNamingClient(system) 28 | 29 | override def getUnderlying: AnyRef = client.getUnderlying 30 | 31 | override def asScala(): play.api.libs.ws.WSClient = client.asScala() 32 | 33 | override def url(uri: String): WSRequest = url(uri, namingClient.settings.queryTimeout) 34 | 35 | def url(uri: String, timeout: FiniteDuration): WSRequest = Await.result(asyncUrl(uri), timeout) 36 | 37 | def asyncUrl(url: String): Future[WSRequest] = 38 | namingClient.generateUri(url).map(uri => client.url(uri.map(_.toString()).getOrElse(url)))(system.executionContext) 39 | 40 | override def close(): Unit = client.close() 41 | } 42 | -------------------------------------------------------------------------------- /web-console/src/utils/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 常量 5 | * */ 6 | const browserRedirect = () => { 7 | const sUserAgent = navigator.userAgent.toLowerCase(); 8 | const bIsIpad = sUserAgent.includes('ipad'); 9 | const bIsIphoneOs = sUserAgent.includes('iphone os'); 10 | const bIsMidp = sUserAgent.includes('midp'); 11 | const bIsUc7 = sUserAgent.includes('rv:1.2.3.4'); 12 | const bIsUc = sUserAgent.includes('ucweb'); 13 | const bIsAndroid = sUserAgent.includes('android'); 14 | const bIsCE = sUserAgent.includes('windows ce'); 15 | const bIsWM = sUserAgent.includes('windows mobile'); 16 | if (bIsIpad || bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM) { 17 | // phone 18 | return 'phone'; 19 | } 20 | // pc 21 | return 'pc'; 22 | }; 23 | 24 | const isProduction = process.env.NODE_ENV === 'production'; 25 | 26 | const ECHARTS_DEFULT_OPTION = { 27 | tooltip: { 28 | trigger: 'axis', 29 | }, 30 | legend: {}, 31 | xAxis: { 32 | type: 'category', 33 | }, 34 | yAxis: [ 35 | { 36 | type: 'value', 37 | name: '数量', 38 | nameTextStyle: { 39 | color: '#000000a6', 40 | }, 41 | splitLine: { show: false }, 42 | }, 43 | { 44 | type: 'value', 45 | splitLine: { show: false }, 46 | name: '百分比', 47 | min: 0, 48 | max: 100, 49 | interval: 20, 50 | axisLabel: { 51 | formatter: '{value} %', 52 | }, 53 | nameTextStyle: { 54 | color: '#000000a6', 55 | }, 56 | }, 57 | ], 58 | series: [], 59 | }; 60 | 61 | const PAGE_OBJECT = { page: 1, size: 20, totalElements: 0, data: [] }; 62 | 63 | export { isProduction, ECHARTS_DEFULT_OPTION, browserRedirect, PAGE_OBJECT }; 64 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/javadsl/DiscoveryXStandaloneWSClient.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.javadsl 18 | 19 | import akka.actor.typed.ActorSystem 20 | import fusion.discoveryx.client.{ DefaultNamingClient, NamingClient } 21 | import play.libs.ws.{ StandaloneWSClient, StandaloneWSRequest } 22 | 23 | import scala.concurrent.duration.FiniteDuration 24 | import scala.concurrent.{ Await, Future } 25 | 26 | final class DiscoveryXStandaloneWSClient(client: StandaloneWSClient)(implicit system: ActorSystem[_]) 27 | extends StandaloneWSClient { 28 | val namingClient: NamingClient = DefaultNamingClient(system) 29 | 30 | override def getUnderlying: AnyRef = client.getUnderlying 31 | 32 | override def url(uri: String): StandaloneWSRequest = url(uri, namingClient.settings.queryTimeout) 33 | 34 | def url(uri: String, timeout: FiniteDuration): StandaloneWSRequest = Await.result(asyncUrl(uri), timeout) 35 | 36 | def asyncUrl(url: String): Future[StandaloneWSRequest] = 37 | namingClient.generateUri(url).map(uri => client.url(uri.map(_.toString()).getOrElse(url)))(system.executionContext) 38 | 39 | override def close(): Unit = client.close() 40 | } 41 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/naming/NamingManager.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.naming 18 | 19 | import akka.actor.typed.scaladsl.Behaviors 20 | import akka.actor.typed.{ ActorRef, ActorSystem, Behavior } 21 | import akka.cluster.sharding.typed.scaladsl.{ ClusterSharding, Entity, EntityTypeKey } 22 | import akka.cluster.sharding.typed.{ ClusterShardingSettings, ShardingEnvelope } 23 | import fusion.discoveryx.server.naming.internal.NamingManagerBehavior 24 | 25 | import scala.concurrent.duration._ 26 | 27 | object NamingManager { 28 | trait Command extends NamingService.Command 29 | trait Event 30 | 31 | val NAME = "NamingManager" 32 | val TypeKey: EntityTypeKey[Command] = EntityTypeKey(NAME) 33 | 34 | def init(system: ActorSystem[_]): ActorRef[ShardingEnvelope[Command]] = { 35 | ClusterSharding(system).init( 36 | Entity(TypeKey)(entityContext => NamingManager(entityContext.entityId)) 37 | .withSettings(ClusterShardingSettings(system).withPassivateIdleEntityAfter(Duration.Zero))) 38 | } 39 | 40 | private def apply(namespace: String): Behavior[Command] = 41 | Behaviors.setup(context => new NamingManagerBehavior(namespace, context).eventSourcedBehavior()) 42 | } 43 | -------------------------------------------------------------------------------- /web-console/src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 单页应用的入口文件 5 | * */ 6 | import 'core-js/stable'; 7 | import 'regenerator-runtime/runtime'; 8 | 9 | import React from 'react'; 10 | import { render } from 'react-dom'; 11 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 12 | import { Provider } from 'mobx-react'; 13 | import { hotRehydrate, rehydrate } from 'rfx-core'; 14 | import moment from 'moment'; 15 | import { ConfigProvider } from 'antd'; 16 | import zhCN from 'antd/lib/locale-provider/zh_CN'; 17 | import 'normalize.css'; 18 | import { isProduction } from './utils/constants'; 19 | import './stores'; 20 | import './global.less'; 21 | import Loadable from './components/Loadable'; 22 | 23 | /** 24 | * 代码拆分和按需加载 25 | */ 26 | const LoadableApp = Loadable({ 27 | loader: () => import(/* webpackChunkName: "route-root" */ './pages/App'), 28 | }); 29 | 30 | const LoadableLogin = Loadable({ 31 | loader: () => import(/* webpackChunkName: "route-login" */ './components/Login'), 32 | }); 33 | 34 | /** 35 | * moment时区设置为中国 36 | */ 37 | moment.locale('zh-cn'); 38 | 39 | const store = rehydrate(); 40 | 41 | const renderApp = () => { 42 | render( 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | , 53 | document.getElementById('root'), 54 | ); 55 | }; 56 | 57 | function run() { 58 | renderApp(); 59 | if (module.hot) { 60 | module.hot.accept(renderApp); 61 | } 62 | } 63 | 64 | store.userStore.getCurrentSessionUser().then(run, run); 65 | -------------------------------------------------------------------------------- /docs/css/fonts/icons.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2017 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /discoveryx-functest/src/multi-jvm/scala/fusion/discoveryx/functest/TestUtils.scala: -------------------------------------------------------------------------------- 1 | package fusion.discoveryx.functest 2 | 3 | import akka.actor.typed.ActorSystem 4 | import akka.http.scaladsl.model.HttpMethods 5 | import akka.http.scaladsl.model.headers.Cookie 6 | import fusion.discoveryx.client.HttpUtils 7 | import fusion.discoveryx.common.Constants 8 | import fusion.discoveryx.server.protocol._ 9 | 10 | import scala.concurrent.Future 11 | 12 | object TestUtils { 13 | def apply(system: ActorSystem[_]): TestUtils = new TestUtils()(system) 14 | } 15 | 16 | import fusion.discoveryx.server.util.ProtobufJsonSupport._ 17 | class TestUtils private ()(implicit system: ActorSystem[_]) { 18 | private val httpClient = HttpUtils(system) 19 | 20 | def login(): Future[UserResponse] = { 21 | httpClient 22 | .singleRequest( 23 | HttpMethods.POST, 24 | "http://localhost:48000/fusion/discoveryx/console/sign/Login", 25 | entity = Login(Constants.DISCOVERYX, Constants.DISCOVERYX)) 26 | .onSuccessResponseAs[UserResponse] 27 | } 28 | 29 | def listNamespace(logined: Logined, page: Int = 1, size: Int = 20)(): Future[ManagementResponse] = { 30 | httpClient 31 | .singleRequest( 32 | HttpMethods.POST, 33 | "http://localhost:48000/fusion/discoveryx/console/namespace/ListNamespace", 34 | List(Cookie(Constants.SESSION_TOKEN_NAME, logined.token)), 35 | ListNamespace(page, size)) 36 | .onSuccessResponseAs[ManagementResponse] 37 | } 38 | 39 | def createNamespace(logined: Logined, namespace: String): Future[ManagementResponse] = { 40 | httpClient 41 | .singleRequest( 42 | HttpMethods.POST, 43 | "http://localhost:48000/fusion/discoveryx/console/namespace/CreateNamespace", 44 | List(Cookie(Constants.SESSION_TOKEN_NAME, logined.token)), 45 | CreateNamespace(namespace, s"Default namespace: $namespace.")) 46 | .onSuccessResponseAs[ManagementResponse] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/user/UserServiceClientTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.user 18 | 19 | import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit 20 | import akka.grpc.GrpcClientSettings 21 | import akka.stream.SystemMaterializer 22 | import com.typesafe.config.ConfigFactory 23 | import fusion.core.extension.FusionCore 24 | import fusion.discoveryx.server.grpc.{ UserService, UserServiceClient } 25 | import fusion.discoveryx.server.protocol.CreateUser 26 | import fusion.discoveryx.server.util.ProtobufJson4s 27 | import org.scalatest.WordSpecLike 28 | 29 | class UserServiceClientTest 30 | extends ScalaTestWithActorTestKit(ConfigFactory.load("application-helloscala.conf")) 31 | with WordSpecLike { 32 | "UserServiceClient" must { 33 | implicit val classicSystem = FusionCore(system).classicSystem 34 | implicit val mat = SystemMaterializer(system).materializer 35 | implicit val ec = system.executionContext 36 | val client = UserServiceClient(GrpcClientSettings.fromConfig(UserService.name)) 37 | 38 | "CreateUser" in { 39 | val resp = 40 | client.createUser(CreateUser("discoveryx", "discoveryx", "Fusion DiscoveryX Administrator")).futureValue 41 | println(ProtobufJson4s.toJsonPrettyString(resp)) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/config/ConfigManagerServiceTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.config 18 | 19 | import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit 20 | import akka.fusion.testkit.FusionActorTestKit 21 | import fusion.discoveryx.server.config.service.ConfigManagerServiceImpl 22 | import fusion.discoveryx.server.namespace.NamespaceRef.ExistNamespace 23 | import fusion.discoveryx.server.namespace.{ NamespaceManager, NamespaceRef } 24 | import fusion.discoveryx.server.protocol.ListConfig 25 | import fusion.discoveryx.server.util.ProtobufJson4s 26 | import helloscala.common.IntStatus 27 | import org.scalatest.WordSpecLike 28 | 29 | class ConfigManagerServiceTest extends ScalaTestWithActorTestKit(FusionActorTestKit()) with WordSpecLike { 30 | NamespaceManager.init(system) 31 | private val namespaceRef = spawn(NamespaceRef()).narrow[ExistNamespace] 32 | private val configManagerService = new ConfigManagerServiceImpl(namespaceRef) 33 | 34 | "ConfigManagerService" must { 35 | val namespace = "me.yangbajing" 36 | 37 | "listConfig" in { 38 | val resp = configManagerService.listConfig(ListConfig(namespace)).futureValue 39 | resp.status shouldBe IntStatus.OK 40 | println(ProtobufJson4s.toJsonPrettyString(resp)) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /web-console/config/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 配置参数 5 | * */ 6 | 7 | const path = require('path'); 8 | 9 | const entry = path.resolve(__dirname, '../src/index'); 10 | const indexHtml = path.resolve(__dirname, '../index.html'); 11 | const root = path.resolve(__dirname, '../'); 12 | const srcPath = path.resolve(__dirname, '../src'); 13 | const distPath = path.resolve(__dirname, '../dist'); 14 | const staticPath = path.resolve(__dirname, '../static'); 15 | const distStaticPath = path.resolve(__dirname, '../dist/static'); 16 | const nodeModulesPath = path.resolve(__dirname, '../node_modules'); 17 | 18 | // alias 19 | const componentsPath = path.resolve(__dirname, '../src/components'); 20 | const stylesPath = path.resolve(__dirname, '../src/styles'); 21 | const utilsPath = path.resolve(__dirname, '../src/utils'); 22 | 23 | module.exports = { 24 | root, 25 | webpack: { 26 | build: { 27 | env: { 28 | NODE_ENV: 'production', 29 | }, 30 | vendor: ['react', 'react-dom', 'react-router-dom', 'mobx', 'mobx-react'], 31 | plugins: { 32 | CopyWebpackPlugin: [ 33 | { 34 | from: staticPath, 35 | to: distStaticPath, 36 | ignore: ['assets/*/*.*'], 37 | }, 38 | ], 39 | }, 40 | }, 41 | dev: { 42 | env: { 43 | NODE_ENV: 'development', 44 | }, 45 | devServer: { 46 | port: 3000, 47 | }, 48 | }, 49 | publicPath: '/', 50 | alias: { 51 | '@components': componentsPath, 52 | '@styles': stylesPath, 53 | '@utils': utilsPath, 54 | }, 55 | modifyVars: { 56 | // "primary-color": "#1890ff", 57 | }, 58 | }, 59 | path: { 60 | entry, 61 | indexHtml, 62 | srcPath, 63 | distPath, 64 | staticPath, 65 | distStaticPath, 66 | nodeModulesPath, 67 | }, 68 | }; 69 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/util/ProtobufJson4s.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.util 18 | 19 | import org.json4s.JValue 20 | import org.json4s.jackson.JsonMethods 21 | import scalapb.json4s.{ Parser, Printer } 22 | import scalapb.{ GeneratedMessage, GeneratedMessageCompanion, Message } 23 | 24 | object ProtobufJson4s { 25 | val printer: Printer = new Printer().includingDefaultValueFields.formattingEnumsAsNumber.formattingLongAsNumber 26 | val parser: Parser = new Parser().ignoringUnknownFields 27 | 28 | def toJsonString[A <: GeneratedMessage](m: A): String = JsonMethods.compact(toJson(m)) 29 | 30 | def toJsonPrettyString[A <: GeneratedMessage](m: A): String = JsonMethods.pretty(toJson(m)) 31 | 32 | def toJson[A <: GeneratedMessage](m: A): JValue = printer.toJson(m) 33 | 34 | @inline def parse[A <: GeneratedMessage](m: A): JValue = toJson(m) 35 | 36 | def fromJson[A <: GeneratedMessage with Message[A]: GeneratedMessageCompanion](value: JValue): A = 37 | parser.fromJson(value) 38 | 39 | def fromJsonString[A <: GeneratedMessage with Message[A]: GeneratedMessageCompanion](str: String): A = 40 | parser.fromJsonString(str) 41 | 42 | @inline def extract[A <: GeneratedMessage with Message[A]: GeneratedMessageCompanion](value: JValue): A = 43 | fromJson(value) 44 | } 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Thumbnails 7 | ._* 8 | 9 | # Files that might appear in the root of a volume 10 | .DocumentRevisions-V100 11 | .fseventsd 12 | .Spotlight-V100 13 | .TemporaryItems 14 | .Trashes 15 | .VolumeIcon.icns 16 | .com.apple.timemachine.donotpresent 17 | 18 | # Directories potentially created on remote AFP share 19 | .AppleDB 20 | .AppleDesktop 21 | Network Trash Folder 22 | Temporary Items 23 | .apdisk 24 | ### Vim template 25 | # Swap 26 | [._]*.s[a-v][a-z] 27 | [._]*.sw[a-p] 28 | [._]s[a-rt-v][a-z] 29 | [._]ss[a-gi-z] 30 | [._]sw[a-p] 31 | *.swp 32 | 33 | ### Eclipse template 34 | .classpath 35 | .settings/ 36 | .metadata 37 | 38 | # External tool builders 39 | .externalToolBuilders/ 40 | 41 | # Locally stored "Eclipse launch configurations" 42 | *.launch 43 | 44 | # Code Recommenders 45 | .recommenders/ 46 | 47 | # Annotation Processing 48 | .apt_generated/ 49 | 50 | # Scala IDE specific (Scala & Java development for Eclipse) 51 | .cache-main 52 | .scala_dependencies 53 | .worksheet 54 | ### Windows template 55 | # Windows thumbnail cache files 56 | Thumbs.db 57 | ehthumbs.db 58 | ehthumbs_vista.db 59 | 60 | # Dump file 61 | *.stackdump 62 | 63 | # Folder config file 64 | [Dd]esktop.ini 65 | 66 | # Recycle Bin used on file shares 67 | $RECYCLE.BIN/ 68 | 69 | # Windows Installer files 70 | *.cab 71 | *.msi 72 | *.msix 73 | *.msm 74 | *.msp 75 | 76 | # Windows shortcuts 77 | *.lnk 78 | 79 | # Linux 80 | .directory 81 | .Trash-* 82 | .nfs* 83 | 84 | ### VisualStudioCode template 85 | .vscode/* 86 | !.vscode/settings.json 87 | !.vscode/tasks.json 88 | !.vscode/launch.json 89 | !.vscode/extensions.json 90 | 91 | 92 | .idea/ 93 | out/ 94 | .metals/ 95 | .scannerwork/ 96 | /.jvmopts 97 | *.log 98 | target/ 99 | logs/ 100 | ~* 101 | *.tmp 102 | *.bak 103 | !/sbt-dist/bin/ 104 | 105 | node_modules/ 106 | /discoveryx-server/src/main/resources/dist/ 107 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/DiscoveryPersistenceQuery.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server 18 | 19 | import akka.actor.typed.ActorSystem 20 | import akka.persistence.cassandra.query.scaladsl.CassandraReadJournal 21 | import akka.persistence.jdbc.query.scaladsl.JdbcReadJournal 22 | import akka.persistence.query.PersistenceQuery 23 | import com.typesafe.scalalogging.StrictLogging 24 | 25 | class DiscoveryPersistenceQuery private (system: ActorSystem[_]) extends StrictLogging { 26 | def readJournal: DiscoveryXReadJournal = { 27 | val reader = system.settings.config.getString("akka.persistence.journal.plugin") match { 28 | case s if s.startsWith("cassandra") => 29 | PersistenceQuery(system).readJournalFor[CassandraReadJournal](CassandraReadJournal.Identifier) 30 | case s if s.startsWith("jdbc") => 31 | PersistenceQuery(system).readJournalFor[JdbcReadJournal](JdbcReadJournal.Identifier) 32 | case s => 33 | throw new ExceptionInInitializerError( 34 | s"The configuration key `akka-persistence` has an invalid value [$s], only support `cassandra-journal`, `jdbc-journal`.") 35 | } 36 | logger.debug(s"ReadJournal is [$reader].") 37 | reader 38 | } 39 | } 40 | 41 | object DiscoveryPersistenceQuery { 42 | def apply(system: ActorSystem[_]): DiscoveryPersistenceQuery = new DiscoveryPersistenceQuery(system) 43 | } 44 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/test/scala/fusion/discoveryx/client/play/scaladsl/DiscoveryXWSClientTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.scaladsl 18 | 19 | import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit 20 | import org.scalatest.WordSpecLike 21 | import play.api.libs.ws.ahc.AhcWSClient 22 | 23 | class DiscoveryXWSClientTest extends ScalaTestWithActorTestKit with WordSpecLike { 24 | "StandaloneWSClient" must { 25 | "create" in { 26 | // #standaloneWSClient-create 27 | val client = DiscoveryXWSClient.standaloneWSClient() 28 | val url = "http://fusion-schedulerx/cluster/health" 29 | val req = client.url(url) 30 | req.url should not be url 31 | req.url should not contain "fusion-schedulerx" 32 | // #standaloneWSClient-create 33 | } 34 | } 35 | 36 | "WSClient" must { 37 | "create" in { 38 | val client = DiscoveryXWSClient.wsClient() 39 | val url = "http://fusion-schedulerx/cluster/health" 40 | val req = client.url(url) 41 | req.url should not be url 42 | req.url should not contain "fusion-schedulerx" 43 | } 44 | 45 | "wrapper" in { 46 | val client = DiscoveryXWSClient.wsClient(AhcWSClient()) 47 | val url = "http://fusion-schedulerx/cluster/health" 48 | val req = client.url(url) 49 | req.url should not be url 50 | req.url should not contain "fusion-schedulerx" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /discoveryx-docs/docs/Fusion DiscoveryX.drawio: -------------------------------------------------------------------------------- 1 | 7Vtbc5s4FP41ntl9aMZI4Mtj7LhNdrcdN3an7b7JIIM2AnlAju399SuMMBfJl3ZtwHHzEnQAgb4Pfecc6bgFh/76Q4gW3kfmYNoCbWfdgg8tAAyjDcS/2LJJLL22mRjckDjyoswwIf9iaWxL65I4OCpcyBmjnCyKRpsFAbZ5wYbCkK2Kl80ZLT51gVysGCY2oqr1K3G4l46r089OPGLievLRPdBNTvgovViOJPKQw1Y5Exy14DBkjCdH/nqIaQxeikty3/s9Z3cvFuKAn3LDdPZ1PkXO397naIW7T/3vz4+rd0Cy8YroUo5Yvi3fpBCEbBk4OO6l3YKDlUc4niyQHZ9dCdKFzeM+FS1DHCJK3EAcUzwXbzVQX1K+9ysOOV7nTPKlP2DmYx5uxCXyrJUCuPuCkuYqo8NKbV6OCdOURiQ/AXfXdYaSOJBA/QBoRv/MoJ0BpU6/iBI0VJjMtgYmo3cxmHrNh8loAEwKSh9RIFTFj0cJOjSeRrNQHLnx0W8TErgUcxa0gHhee4zDiEQcBzb+vXZ0LbOILuiq6BqmBt3OpcDtHcGEhdxjLgsQ/YuxhUTiH8z5RroitOSsiBNeE/4td/w97urOkq2Htex529ikjUAM5lu+kbsrbma3bVvpfXs5idgytPEhYZe+EoUu5geuk/qPnYIbVBkOMUWcvBa94tnp6v+i6zBd3UbRBRTpGrJgTtxEwEKdenkodOoXqm675AZ0QqVzAxcTqtQP3d6nD0/89DuN+vQN4xdfh/myGsUXVKTqE/JFKHV1UgVrlyo1ZUxUfxRwwjf7kGxguNqHvaIXgBpowYWg/TJAvT/ffXl8mvTZ4rP3x8Ns9Cl17zlM4ukzkc1MUkaZdXAh2QGX1J2SKPycEFmqEGlBBRXpjvbhagJSG6Pdw4zWwIxZp0ew9niECQ5fiRje9ehYDxZdhKlZ1KhWx6zmfPXN17HOibOlqvhJ+/BOcxg9omNXxCisU/86b1b/YJVx3KFlmrcXIoMqXYs+8VYXnZ5Hk6mwDCnZLpmXIIs8tIgPlz69tzkLBTDx+IkthAjNMB2ziHDC4k2rGeOc+eICGp8YIPvF3QI+ZDS+T/QG59u/XB/3csOLsxL6bMkpCfBwtzF5MO3+gS0Mq8CIYamMwEoJUVNr93k8vFlCTFAzIeBwzhGw4PrCrUj4Un4f7+cLg01RFBE7Nb8n9KjiHXXZhqn6bP2FzVqHBOqWSXVkH4nEGkRas/ZNDHUJ7f7lBQnL43Q6rt3pg9KSo6biotqCC6hujlT3kf+coIH6FU2ThRysAWrK5FATkyR43iUmNU8PWKpyMDp1x8RQ3Yxq+vzo1j8/ulc6P9TkspS4N2x+QI3/qHZ+pA/LQXa3/VOwEkPkRUAiHrIXnCYccirNxQdYMqWFj7YADudzmjQf8YnjbJfFdAwUOToDCf1S1aSp2TbUVWKBi3Gg1rndGge6crhqOVDXThLXKmxJKh4n7csoBu/8rMiS4Ho5OaUA1NCl65cjZV9pwu2SoiterpgUdXdQZmhvnIoTqu0rZkJNBxTs0xXFOcVrGTKKaNJRo8dTAp2jYV7+hwcaJFLbydGgfMKYkXixNCXCKG1qGN3+nVXsJAlx5X0ZzGpXZc1Tu0qCYKWrLWO7of8PEtWY9SZJNM2zkajp6nwk7t9/1vipp0AkZcF5Eg9FqDTs7o+3eiU3otsH1JXKwTOI1/7So2uCTFs6Uilk+6KhxkIGdcs/lUKmriY3HLLdSvEFIBPN7DeVifRlv0yFo/8A -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/route/FusionRouteTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.route 18 | 19 | import akka.actor.ActorSystem 20 | import akka.http.scaladsl.testkit.{ RouteTestTimeout, ScalatestRouteTest } 21 | import akka.testkit.TestDuration 22 | import com.typesafe.config.{ Config, ConfigFactory } 23 | import fusion.core.extension.FusionCore 24 | import fusion.discoveryx.server.DiscoveryX 25 | import org.scalatest.concurrent.ScalaFutures 26 | import org.scalatest.time.{ Millis, Span } 27 | import org.scalatest.{ EitherValues, Matchers, OptionValues, Suite } 28 | 29 | import scala.concurrent.duration._ 30 | 31 | // TODO 不依赖 DiscoveryX,依赖 SpawnFactory 即可 32 | 33 | trait FusionRouteTest extends ScalatestRouteTest with Matchers with OptionValues with EitherValues with ScalaFutures { 34 | this: Suite => 35 | protected implicit val timeout: RouteTestTimeout = RouteTestTimeout(5.seconds.dilated) 36 | protected var discoveryX: DiscoveryX = _ 37 | 38 | implicit override def patienceConfig: PatienceConfig = 39 | PatienceConfig(scaled(Span(10.second.toMillis, Millis)), scaled(Span(15, Millis))) 40 | 41 | override protected def createActorSystem(): ActorSystem = { 42 | discoveryX = DiscoveryX.fromOriginalConfig(ConfigFactory.load("application-test.conf")) 43 | FusionCore(discoveryX.system) 44 | discoveryX.classicSystem 45 | } 46 | 47 | override def testConfig: Config = discoveryX.config 48 | } 49 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/design/concept.md: -------------------------------------------------------------------------------- 1 | # Fusion DiscoveryX 概念 2 | 3 | ## 命名空间(namespace) 4 | 5 | 用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 6 | 7 | ## 配置 8 | 9 | 在系统开发过程中,开发者通常会将一些需要变更的参数、变量等从代码中分离出来独立管理,以独立的配置文件的形式存在。目的是让静态的系统工件或者交付物(如 WAR,JAR 包等)更好地和实际的物理运行环境进行适配。配置管理一般包含在系统部署的过程中,由系统管理员或者运维人员完成。配置变更是调整系统运行时的行为的有效手段。 10 | 11 | ## 配置管理 12 | 13 | 系统配置的编辑、存储、分发、变更管理、历史版本管理、变更审计等所有与配置相关的活动。 14 | 15 | ## 配置项 16 | 17 | 一个具体的可配置的参数与其值域,通常以 param-key=param-value 的形式存在。例如我们常配置系统的日志输出级别(logLevel=INFO|WARN|ERROR) 就是一个配置项。 18 | 19 | ## 配置集 20 | 21 | 一组相关或者不相关的配置项的集合称为配置集。在系统中,一个配置文件通常就是一个配置集,包含了系统各个方面的配置。例如,一个配置集可能包含了数据源、线程池、日志级别等配置项。 22 | 23 | ## 配置集 ID(dataId) 24 | 25 | DiscoveryX 中的某个配置集的 ID(dataId)。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集,每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包(如 com.akka-fusion.schedulerx.log.level)的命名规则保证全局唯一性。此命名规则非强制。 26 | 27 | ## 配置分组 28 | 29 | DiscoveryX 中的一组配置集,是组织配置的维度之一。通过一个有意义的字符串(如 Buy 或 Trade )对配置集进行分组,从而区分 Data ID 相同的配置集。当您在 DiscoveryX 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 `default` 。配置分组的常见场景:不同的应用或组件使用了相同的配置类型,如 database_url 配置和 MQ_topic 配置。 30 | 31 | ## 配置快照 32 | 33 | DiscoveryX 的客户端 SDK 会在本地生成配置的快照。当客户端无法连接到 DiscoveryX Server 时,可以使用配置快照显示系统的整体容灾能力。配置快照类似于 Git 中的本地 commit,也类似于缓存,会在适当的时机更新,但是并没有缓存过期(expiration)的概念。 *DiscoveryX 使用 akka-persistence 提供配置快照(通过把每次配置修改事件都进行存储),理论上可以提供无线版本的配置快照。* 34 | 35 | ## 服务 36 | 37 | 通过预定义接口网络访问的提供给客户端的软件功能。 38 | 39 | ## 服务名 40 | 41 | 服务提供的标识,通过该标识可以唯一确定其指代的服务。 42 | 43 | ## 服务注册中心 44 | 45 | 存储服务实例和服务负载均衡策略的数据库。 46 | 47 | ## 服务发现 48 | 49 | 在计算机网络上,(通常使用服务名)对服务下的实例的地址和元数据进行探测,并以预先定义的接口提供给客户端进行查询。 50 | 51 | ## 元信息 52 | 53 | DiscoveryX 数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。 54 | 55 | ## 应用 56 | 57 | 用于标识服务提供方的服务的属性。 58 | 59 | ## 服务分组 60 | 61 | 不同的服务可以归类到同一分组。 62 | 63 | ## 实例 64 | 65 | 提供一个或多个服务的具有可访问网络地址(ip:port)的进程。 66 | 67 | ## 权重 68 | 69 | 实例级别的配置。权重为浮点数。权重越大,分配给该实例的流量越大。 70 | 71 | ## 健康检查 72 | 73 | 以指定方式检查服务下挂载的实例 (Instance) 的健康度,从而确认该实例 (Instance) 是否能提供服务。根据检查结果,实例 (Instance) 会被判断为健康或不健康。对服务发起解析请求时,不健康的实例 (Instance) 不会返回给客户端。 74 | -------------------------------------------------------------------------------- /discoveryx-client/src/main/scala/fusion/discoveryx/client/impl/ConfigClientImpl.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.impl 18 | 19 | import akka.NotUsed 20 | import akka.actor.typed.ActorSystem 21 | import akka.stream.scaladsl.Source 22 | import com.typesafe.scalalogging.StrictLogging 23 | import fusion.core.extension.FusionCore 24 | import fusion.discoveryx.client.{ ConfigClient, ConfigClientSettings } 25 | import fusion.discoveryx.grpc.ConfigServiceClient 26 | import fusion.discoveryx.model._ 27 | 28 | import scala.concurrent.Future 29 | 30 | private[client] class ConfigClientImpl(val settings: ConfigClientSettings, val configClient: ConfigServiceClient)( 31 | implicit system: ActorSystem[_]) 32 | extends ConfigClient 33 | with StrictLogging { 34 | FusionCore(system).shutdowns.beforeServiceUnbind("ConfigClient") { () => 35 | configClient.close() 36 | } 37 | logger.info(s"ConfigClient was instanced, setting is [$settings], class is [$getClass].") 38 | 39 | override def serverStatus(in: ServerStatusQuery): Future[ServerStatusBO] = configClient.serverStatus(in) 40 | 41 | override def getConfig(in: ConfigGet): Future[ConfigReply] = configClient.getConfig(in) 42 | 43 | override def publishConfig(in: ConfigItem): Future[ConfigReply] = configClient.publishConfig(in) 44 | 45 | override def removeConfig(in: ConfigRemove): Future[ConfigReply] = configClient.removeConfig(in) 46 | 47 | override def listenerConfig(in: ConfigChangeListen): Source[ConfigChanged, NotUsed] = configClient.listenerConfig(in) 48 | } 49 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/BaseSettings.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server 18 | 19 | import akka.persistence.typed.scaladsl.{ RetentionCriteria, SnapshotCountRetentionCriteria } 20 | import helloscala.common.Configuration 21 | 22 | trait BaseSettings { 23 | val c: Configuration 24 | 25 | def defaultPage: Int = c.getInt("default-page") 26 | def defaultSize: Int = c.getInt("default-size") 27 | 28 | def findSize(size: Int): Int = if (size < defaultSize) defaultSize else size 29 | 30 | def findPage(page: Int): Int = if (page < defaultPage) defaultPage else page 31 | 32 | def findOffset(page: Int, size: Int): Int = if (page > 0) (page - 1) * size else 0 33 | 34 | /** 35 | * @return (page, size, offset) 36 | */ 37 | def generatePageSizeOffset(_page: Int, _size: Int): (Int, Int, Int) = { 38 | val page = findPage(_page) 39 | val size = findSize(_size) 40 | val offset = if (page > 0) (page - 1) * size else 0 41 | (page, size, offset) 42 | } 43 | } 44 | 45 | trait RetentionCriteriaSettings { 46 | val c: Configuration 47 | def journalOnDelete: Boolean = c.getBoolean("journal-on-delete") 48 | def numberOfEvents: Int = c.getInt("snapshot.number-of-events") 49 | def keepNSnapshots: Int = c.getInt("snapshot.keep-n-snapshots") 50 | 51 | def retentionCriteria: SnapshotCountRetentionCriteria = { 52 | val retention = RetentionCriteria.snapshotEvery(numberOfEvents, keepNSnapshots) 53 | if (journalOnDelete) retention.withDeleteEventsOnSnapshot else retention 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docs/js/scrollsneak.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on Scroll Sneak: 3 | * http://mrcoles.com/scroll-sneak/ 4 | * 5 | * Note: this version of scroll adjustment assumes auto expanding/collapsing navigation 6 | */ 7 | $(function() { 8 | 9 | var prefix = "docs.nav.scroll"; 10 | var nav = $(".site-nav"); 11 | 12 | // if window.name matches, then scroll to the position and clean up window.name 13 | if (window.name.search('^'+prefix+'_(\\d+)_') == 0) { 14 | var name = window.name.split('_'); 15 | nav.scrollTop(name[1]); 16 | window.name = name.slice(2).join('_'); 17 | } 18 | 19 | var originalName; 20 | var active = nav.find("a.active.page").parent("li"); 21 | var activeParents = active.parentsUntil(nav); 22 | 23 | // add scroll sneak to all the page links in the navigation 24 | // most of this is adjusting for the auto expanding/collapsing 25 | nav.find("a.page").each(function() { 26 | var link = $(this); 27 | // if the active page is positioned above this link but not an ancestor, 28 | // then scroll needs to be adjusted because of the active sections collapsing 29 | var collapseHeight = 0; 30 | if ((active.length > 0) && (active.position().top < link.position().top) && (link.parentsUntil(nav).filter(active).length == 0)) { 31 | // find the active section that will collapse, by searching for the first common parent 32 | var collapsing = active; 33 | activeParents.each(function() { 34 | var ancestor = $(this); 35 | if (ancestor.has(link).length > 0) return false; 36 | collapsing = ancestor; 37 | }); 38 | collapseHeight = Math.round(collapsing.children("ul").first().outerHeight(true)); 39 | } 40 | link.click(function() { 41 | var link = $(this); 42 | var adjustment = collapseHeight; 43 | // prevent multiple clicks storing the scroll position on window.name 44 | if (typeof(originalName) == 'undefined') originalName = window.name; 45 | // store the current scroll position into the window.name 46 | var position = Math.max(0, nav.scrollTop() - adjustment); 47 | if (position) window.name = prefix + '_' + position + '_' + originalName; 48 | }); 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /web-console/src/components/Chart/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin React Echarts Component 5 | * 6 | * option: { 7 | * option: 基础配置项, 8 | * data: 数据源, 9 | * row: 横坐标上的属性(对于data里面的key), 10 | * column: 纵坐标上的属性(对于data里面的key), 11 | * value: 图上的属性(对于data里面的key), 12 | * seriesTemplates: 每个series的配置 13 | * } 14 | * */ 15 | 16 | import React, { useEffect, useRef } from 'react'; 17 | import echarts from 'echarts'; 18 | import { computedEchartsOption } from 'up-utils'; 19 | import lodashIsEqual from 'lodash/isEqual'; 20 | import * as PropTypes from 'prop-types'; 21 | 22 | const EChart = props => { 23 | const { option, style } = props; 24 | const echartsRef = useRef(null); 25 | let echartsInstance; 26 | 27 | const renderChart = () => { 28 | if (echartsRef.current) { 29 | const renderedInstance = echarts.getInstanceByDom(echartsRef.current); 30 | if (renderedInstance) { 31 | echartsInstance = renderedInstance; 32 | } else { 33 | echartsInstance = echarts.init(echartsRef.current); 34 | } 35 | echartsInstance.setOption(computedEchartsOption(option)); 36 | } 37 | }; 38 | 39 | useEffect(() => { 40 | const handleWindowResize = () => { 41 | console.log('echarts resize'); 42 | echartsInstance.resize(); 43 | }; 44 | window.addEventListener('resize', handleWindowResize); 45 | return () => { 46 | window.removeEventListener('resize', handleWindowResize); 47 | echartsInstance.dispose(); 48 | console.log('echarts dispose'); 49 | }; 50 | }, []); 51 | 52 | useEffect(() => { 53 | renderChart(); 54 | console.log('echarts render'); 55 | }); 56 | 57 | return
; 58 | }; 59 | 60 | const areEqual = (prevProps, nextProps) => { 61 | return lodashIsEqual(prevProps.option.data, nextProps.option.data); 62 | }; 63 | 64 | EChart.propTypes = { 65 | option: PropTypes.object.isRequired, 66 | style: PropTypes.object, 67 | }; 68 | 69 | EChart.defaultProps = { 70 | style: { width: '100%', height: 400 }, 71 | }; 72 | 73 | export default React.memo(EChart, areEqual); 74 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/namespace/NamespaceRef.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.namespace 18 | 19 | import akka.actor.typed.scaladsl.Behaviors 20 | import akka.actor.typed.{ ActorRef, Behavior } 21 | import akka.cluster.ddata.typed.scaladsl.{ DistributedData, Replicator } 22 | import akka.cluster.ddata.{ ORSet, ORSetKey } 23 | 24 | /** 25 | * 需要 Singleton actor Management() 已启动 26 | */ 27 | object NamespaceRef { 28 | sealed trait Command 29 | final case class ExistNamespace(namespace: String, replyTo: ActorRef[NamespaceExists]) extends Command 30 | private final case class InternalNamespaceExists(exists: Boolean, replyTo: ActorRef[NamespaceExists]) extends Command 31 | 32 | final case class NamespaceExists(exists: Boolean) 33 | 34 | val NAME = "Namespace" 35 | val Key: ORSetKey[String] = ORSetKey(NAME) 36 | 37 | def apply(): Behavior[Command] = DistributedData.withReplicatorMessageAdapter[Command, ORSet[String]] { 38 | replicatorAdapter => 39 | Behaviors.receive[Command] { 40 | case (ctx, ExistNamespace(namespace, replyTo)) => 41 | replicatorAdapter.askGet(Replicator.Get(Key, Replicator.ReadLocal), { 42 | case chg @ Replicator.GetSuccess(Key) => 43 | InternalNamespaceExists(chg.get(Key).contains(namespace), replyTo) 44 | case _ => 45 | ctx.log.warn(s"ORSet key is [$Key], it's not found.") 46 | InternalNamespaceExists(false, replyTo) 47 | }) 48 | Behaviors.same 49 | 50 | case (_, InternalNamespaceExists(exists, replyTo)) => 51 | replyTo ! NamespaceExists(exists) 52 | Behaviors.same 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/namespace/service/NamespaceManagerServiceImpl.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.namespace.service 18 | 19 | import akka.actor.typed.scaladsl.AskPattern._ 20 | import akka.actor.typed.{ ActorRef, ActorSystem } 21 | import akka.util.Timeout 22 | import fusion.discoveryx.server.grpc.NamespaceManagerService 23 | import fusion.discoveryx.server.namespace.NamespaceManager 24 | import fusion.discoveryx.server.protocol.ManagementCommand.Cmd 25 | import fusion.discoveryx.server.protocol._ 26 | 27 | import scala.concurrent.Future 28 | import scala.concurrent.duration._ 29 | 30 | class NamespaceManagerServiceImpl(managementRef: ActorRef[NamespaceManager.Command])(implicit system: ActorSystem[_]) 31 | extends NamespaceManagerService { 32 | implicit private val timeout: Timeout = 5.seconds 33 | 34 | /** 35 | * #ListNamespace 36 | */ 37 | override def listNamespace(in: ListNamespace): Future[ManagementResponse] = 38 | askCommand(Cmd.List(in)) 39 | 40 | /** 41 | * #CreateNamespace 42 | */ 43 | override def createNamespace(in: CreateNamespace): Future[ManagementResponse] = 44 | askCommand(Cmd.Create(in)) 45 | 46 | /** 47 | * #ModifyNamespace 48 | */ 49 | override def modifyNamespace(in: ModifyNamespace): Future[ManagementResponse] = 50 | askCommand(Cmd.Modify(in)) 51 | 52 | /** 53 | * #RemoveNamespace 54 | */ 55 | override def removeNamespace(in: RemoveNamespace): Future[ManagementResponse] = 56 | askCommand(Cmd.Remove(in)) 57 | 58 | @inline private def askCommand(cmd: Cmd): Future[ManagementResponse] = 59 | managementRef.ask[ManagementResponse](replyTo => ManagementCommand(replyTo, cmd)) 60 | } 61 | -------------------------------------------------------------------------------- /discoveryx-server/src/test/scala/fusion/discoveryx/server/user/service/UserServiceTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.user.service 18 | 19 | import akka.actor.testkit.typed.scaladsl.{ ActorTestKit, ScalaTestWithActorTestKit } 20 | import com.typesafe.config.ConfigFactory 21 | import fusion.common.config.FusionConfigFactory 22 | import fusion.discoveryx.common.Constants 23 | import fusion.discoveryx.server.user.{ UserEntity, UserManager } 24 | import fusion.discoveryx.server.protocol.{ CreateUser, Login, UserRole } 25 | import fusion.discoveryx.server.util.ProtobufJson4s 26 | import helloscala.common.IntStatus 27 | import org.scalatest.WordSpecLike 28 | 29 | class UserServiceTest 30 | extends ScalaTestWithActorTestKit( 31 | ActorTestKit( 32 | Constants.DISCOVERYX, 33 | FusionConfigFactory 34 | .arrangeConfig(ConfigFactory.load("application-test.conf"), Constants.DISCOVERYX, Seq("akka")))) 35 | with WordSpecLike { 36 | private val userService = new UserServiceImpl(UserEntity.init(system), UserManager.init(system)) 37 | 38 | "UserServiceTest" should { 39 | "removeUser" in {} 40 | 41 | "logout" in {} 42 | 43 | "createUser" in { 44 | val in = CreateUser("discoveryx", "discoveryx", "DiscoveryX Administrator", UserRole.ADMIN) 45 | val response = userService.createUser(in).futureValue 46 | println(ProtobufJson4s.toJsonPrettyString(response)) 47 | response.status should be(IntStatus.OK) 48 | } 49 | 50 | "modifyUser" in {} 51 | 52 | "listUser" in {} 53 | 54 | "login" in { 55 | val response = userService.login(Login("discoveryx", "discoveryx")).futureValue 56 | println(ProtobufJson4s.toJsonPrettyString(response)) 57 | response.status should be(IntStatus.OK) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /discoveryx-client/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | // #discoveryx-client 2 | discoveryx.client { 3 | naming { 4 | // 自动注册服务。default: false 5 | #auto-registration = false 6 | 7 | // 心跳间隔 8 | heartbeat-interval = 10.seconds 9 | 10 | // 查询服务实例时只返回一个健康实例(轮询) 11 | one-healthy = true 12 | 13 | // 查询服务实例时返回所有健康实例(若 one-healthy 设置为 true,则 all-healthy 不进行判断) 14 | all-healthy = false 15 | 16 | // 名称空间 17 | #namespace = "" 18 | 19 | // 注册的服务实例名 20 | #service-name = "discoveryx" 21 | 22 | // 注册的服务实例IP地址 23 | #ip = "127.0.0.1" 24 | 25 | // 注册的服务实例网络端口 26 | #port = 8000 27 | 28 | // 是否启用。default: true 29 | #enable = true 30 | 31 | // 设置为true服务注册后立即可用,否则等一次心跳消息后可用。default: true 32 | #health = true 33 | 34 | // 注册的服务实例权重。default: 1.0 35 | #weight = 1.0 36 | 37 | // 注册的服务实例元数据 38 | #metadata { 39 | # env = test 40 | # application = web-backend 41 | #} 42 | 43 | // 健康检查方法,支持:CLIENT_REPORT, SERVER_SNIFF。default:CLIENT_REPORT 44 | #healthy-check-method = CLIENT_REPORT 45 | 46 | // 健康检查间隔(秒),当 healthy-check-method = SERVER_SNIFF 时有效。default:15 47 | #healthy-check-interval = 15 48 | 49 | // 几次健康检查失败设置服务实例为不健康状态。default:1 50 | #unhealthy-check-count = 1 51 | 52 | // 健康检查协议,支持:UDP、TCP、HTTP(当 healthy-check-method = SERVER_SNIFF 时有效 )。default:HTTP 53 | #protocol = HTTP 54 | 55 | // 健康检查是否使用 TLS。default: false 56 | # use-tls = false 57 | 58 | // protocol 设置为 HTTP 或 HTTPS 时指定的GET请求URI PATH路径 59 | // 对于HTTP(HTTPS)检测,当HTTP响应状态值为 [200, 299] 范围内时认为服务实例状态为 healthy。 60 | #http-path = 61 | 62 | // 调用NamingClient.generateUri默认超时时间。default: 5 seconds 63 | #query-timeout = 5.seconds 64 | } 65 | } 66 | // #discoveryx-client 67 | 68 | // #grpc-client 69 | akka.grpc.client { 70 | // DiscoveryX 配置管理服务 71 | "fusion.discoveryx.grpc.ConfigService" { 72 | // 是否使用 tls 73 | use-tls = false 74 | host = "127.0.0.1" 75 | port = 48000 76 | } 77 | // DiscoveryX 服务注册、发现管理服务 78 | "fusion.discoveryx.grpc.NamingService" { 79 | // 是否使用 tls 80 | use-tls = false 81 | host = "127.0.0.1" 82 | port = 48000 83 | } 84 | } 85 | // #grpc-client 86 | 87 | // #discovery 88 | akka.discovery { 89 | method = fusion-discoveryx 90 | 91 | fusion-discoveryx { 92 | class = fusion.discoveryx.client.DiscoveryXAkkaDiscovery 93 | 94 | setting = discoveryx.client.naming 95 | } 96 | } 97 | // #discovery 98 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/javadsl/module/DiscoveryXWSModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.javadsl.module; 18 | 19 | import akka.actor.typed.ActorSystem; 20 | import com.typesafe.config.Config; 21 | import fusion.discoveryx.client.play.javadsl.DiscoveryXPlay; 22 | import fusion.discoveryx.client.play.javadsl.DiscoveryXPlayWSClient; 23 | import fusion.discoveryx.client.play.javadsl.DiscoveryXWSClient; 24 | import fusion.discoveryx.common.Constants; 25 | import play.Environment; 26 | import play.inject.Binding; 27 | import play.inject.Module; 28 | import play.libs.ws.WSClient; 29 | import play.shaded.ahc.org.asynchttpclient.AsyncHttpClient; 30 | 31 | import javax.inject.Provider; 32 | import java.util.Arrays; 33 | import java.util.List; 34 | 35 | public class DiscoveryXWSModule extends Module { 36 | @Override 37 | public List> bindings(Environment environment, Config config) { 38 | return Arrays.asList(bindClass(DiscoveryXPlayWSClient.class).toProvider(DiscoveryXWSPlayClientProvider.class), 39 | bindClass(WSClient.class).qualifiedWith(Constants.DISCOVERYX()).to(DiscoveryXPlayWSClient.class), 40 | bindClass(WSClient.class).qualifiedWith(DiscoveryXPlay.class).to(DiscoveryXPlayWSClient.class)); 41 | } 42 | 43 | public static class DiscoveryXWSPlayClientProvider implements Provider { 44 | private final DiscoveryXPlayWSClient client; 45 | 46 | public DiscoveryXWSPlayClientProvider(AsyncHttpClient asyncHttpClient, ActorSystem system) { 47 | this.client = DiscoveryXWSClient.wsClient(asyncHttpClient, system); 48 | } 49 | 50 | @Override 51 | public DiscoveryXPlayWSClient get() { 52 | return client; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/util/SessionUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.util 18 | 19 | import java.net.{ URLDecoder, URLEncoder } 20 | import java.nio.charset.StandardCharsets 21 | import java.util.concurrent.ThreadLocalRandom 22 | 23 | import akka.http.scaladsl.model.HttpRequest 24 | import akka.http.scaladsl.model.headers.Cookie 25 | import fusion.discoveryx.common.Constants 26 | import fusion.discoveryx.server.protocol.TokenAccount 27 | import helloscala.common.util.DigestUtils 28 | 29 | import scala.util.control.NonFatal 30 | 31 | object SessionUtils { 32 | def getTokenFromRequest(request: HttpRequest): Option[String] = { 33 | request 34 | .header[Cookie] 35 | .flatMap(tokenFromCookie) 36 | .orElse(request.headers.find(_.name == Constants.SESSION_TOKEN_NAME).map(_.value)) 37 | } 38 | 39 | def tokenFromCookie(cookie: Cookie): Option[String] = 40 | cookie.cookies.find(_.name == Constants.SESSION_TOKEN_NAME).map(_.value) 41 | 42 | def decodeToken(token: String): String = new String(URLDecoder.decode(token, StandardCharsets.UTF_8.toString)) 43 | 44 | def parseAccount(originalToken: String): Either[String, TokenAccount] = 45 | try { 46 | val token = decodeToken(originalToken) 47 | SessionUtils.decodeToken(token).split("\\|") match { 48 | case Array(account, _) => Right(TokenAccount(originalToken, account)) 49 | case other => Left(s"Parsing account error from token. the parse value is [$other].") 50 | } 51 | } catch { 52 | case NonFatal(e) => Left(e.getLocalizedMessage) 53 | } 54 | 55 | def generateSessionToken(account: String): String = { 56 | val bytes = Array.ofDim[Byte](40) 57 | ThreadLocalRandom.current().nextBytes(bytes) 58 | val token = DigestUtils.md5Hex(bytes) 59 | URLEncoder.encode(s"$account|$token", StandardCharsets.UTF_8.toString) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/naming/service/NamingServiceHelper.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.naming.service 18 | 19 | import java.util.concurrent.TimeoutException 20 | 21 | import akka.actor.typed.scaladsl.AskPattern._ 22 | import akka.actor.typed.{ ActorRef, Scheduler } 23 | import akka.cluster.sharding.typed.ShardingEnvelope 24 | import akka.util.Timeout 25 | import fusion.discoveryx.model.NamingReply 26 | import fusion.discoveryx.server.namespace.NamespaceRef.{ ExistNamespace, NamespaceExists } 27 | import fusion.discoveryx.server.naming.NamingService 28 | import fusion.discoveryx.server.protocol.NamingReplyCommand 29 | import helloscala.common.IntStatus 30 | 31 | import scala.concurrent.{ ExecutionContext, Future } 32 | 33 | trait NamingServiceHelper { 34 | val serviceInstanceRegion: ActorRef[ShardingEnvelope[NamingService.Command]] 35 | val namespaceRef: ActorRef[ExistNamespace] 36 | 37 | protected def askNaming(namespace: String, serviceName: String, cmd: NamingReplyCommand.Cmd)( 38 | implicit timeout: Timeout, 39 | scheduler: Scheduler, 40 | ec: ExecutionContext): Future[NamingReply] = { 41 | namespaceRef.ask[NamespaceExists](replyTo => ExistNamespace(namespace, replyTo)).flatMap { 42 | case NamespaceExists(true) => 43 | NamingService.makeEntityId(namespace, serviceName) match { 44 | case Right(entityId) => 45 | serviceInstanceRegion 46 | .ask[NamingReply](replyTo => ShardingEnvelope(entityId, NamingReplyCommand(replyTo, cmd))) 47 | .recover { 48 | case _: TimeoutException => NamingReply(IntStatus.GATEWAY_TIMEOUT) 49 | } 50 | case Left(errMsg) => Future.successful(NamingReply(IntStatus.INTERNAL_ERROR, errMsg)) 51 | } 52 | case _ => 53 | Future.successful(NamingReply(IntStatus.NOT_FOUND, s"Namespace [$namespace] not found.")) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /discoveryx-docs/src/main/paradox/api/open/config.md: -------------------------------------------------------------------------------- 1 | # ConfigService 2 | 3 | - gRPC服务地址:`/fusion.discoveryx.grpc.ConfigService` 4 | - REST URL前缀:`/fusion/discoveryx/v1/config` 5 | 6 | REST URL路径由 **REST URL前缀** + 服务名组织,均使用 **POST** 方法的请求,JSON序例化格式。如查询配置接口访问地址为:`POST /fusion/discoveryx/v1/config/QueryConfig`。Protobuf与JSON格式转换请参阅: @ref[JSON 说明](../json.md)。 7 | 8 | ### GetConfig 9 | 10 | **gRPC** 11 | 12 | @@snip [gRPC](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/grpc/discoveryx.proto) { #GetConfig } 13 | 14 | **请求** 15 | 16 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigGet } 17 | 18 | **响应** 19 | 20 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigReply } 21 | 22 | `oneof`的`queried`字段将返回已注册实例信息,如下: 23 | 24 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigQueried } 25 | 26 | ### PublishConfig 27 | 28 | **gRPC** 29 | 30 | @@snip [gRPC](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/grpc/discoveryx.proto) { #PublishConfig } 31 | 32 | **请求** 33 | 34 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigItem } 35 | 36 | **响应** 37 | 38 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigReply } 39 | 40 | ### RemoveConfig 41 | 42 | **gRPC** 43 | 44 | @@snip [gRPC](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/grpc/discoveryx.proto) { #RemoveConfig } 45 | 46 | **请求** 47 | 48 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigRemove } 49 | 50 | **响应** 51 | 52 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigReply } 53 | 54 | ### ListenerConfig 55 | 56 | **gRPC** 57 | 58 | @@snip [gRPC](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/grpc/discoveryx.proto) { #ListenerConfig } 59 | 60 | **请求** 61 | 62 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigChangeListen } 63 | 64 | **响应** 65 | 66 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ConfigChanged } 67 | @@snip [model](../../../../../../discoveryx-common/src/main/protobuf/fusion/discoveryx/model/discoveryx.proto) { #ChangeType } 68 | -------------------------------------------------------------------------------- /discoveryx-client/src/main/scala/fusion/discoveryx/client/HttpUtils.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client 18 | 19 | import akka.actor.typed.ActorSystem 20 | import akka.http.scaladsl.Http 21 | import akka.http.scaladsl.marshalling.{ Marshal, Marshaller } 22 | import akka.http.scaladsl.model._ 23 | import akka.http.scaladsl.unmarshalling.{ Unmarshal, Unmarshaller } 24 | import akka.stream.Materializer 25 | 26 | import scala.collection.immutable 27 | import scala.concurrent.Future 28 | 29 | final class ResponseAs(val response: Future[HttpResponse])(implicit mat: Materializer) { 30 | import mat.executionContext 31 | def responseAs[R](implicit um: Unmarshaller[ResponseEntity, R]): Future[R] = 32 | response.flatMap(resp => Unmarshal(resp.entity).to[R]) 33 | 34 | def onSuccessResponseAs[R](implicit um: Unmarshaller[ResponseEntity, R]): Future[R] = 35 | response.flatMap { 36 | case resp if resp.status.isSuccess() => Unmarshal(resp.entity).to[R] 37 | case resp => Future.failed(new IllegalStateException(s"Http request failed, the response is $resp.")) 38 | } 39 | } 40 | 41 | final class HttpUtils private ()(implicit system: ActorSystem[_]) { 42 | def singleRequest[A]( 43 | method: HttpMethod = HttpMethods.GET, 44 | uri: Uri = "/", 45 | headers: immutable.Seq[HttpHeader] = Nil, 46 | entity: A = null, 47 | protocol: HttpProtocol = HttpProtocols.`HTTP/1.1`)( 48 | implicit 49 | m: Marshaller[A, RequestEntity]): ResponseAs = { 50 | import system.executionContext 51 | val entityF = if (null == entity) Future.successful(HttpEntity.Empty) else Marshal(entity).to[RequestEntity] 52 | val responseF = entityF.flatMap { entity => 53 | val request = HttpRequest(method, uri, headers, entity, protocol) 54 | Http(system).singleRequest(request) 55 | } 56 | new ResponseAs(responseF) 57 | } 58 | } 59 | 60 | object HttpUtils { 61 | def apply(system: ActorSystem[_]): HttpUtils = new HttpUtils()(system) 62 | } 63 | -------------------------------------------------------------------------------- /scripts/dockers/cassandra/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # first arg is `-f` or `--some-option` 5 | # or there are no args 6 | if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then 7 | set -- cassandra -f "$@" 8 | fi 9 | 10 | # allow the container to be started with `--user` 11 | if [ "$1" = 'cassandra' -a "$(id -u)" = '0' ]; then 12 | find /var/lib/cassandra /var/log/cassandra "$CASSANDRA_CONFIG" \ 13 | \! -user cassandra -exec chown cassandra '{}' + 14 | exec gosu cassandra "$BASH_SOURCE" "$@" 15 | fi 16 | 17 | _ip_address() { 18 | # scrape the first non-localhost IP address of the container 19 | # in Swarm Mode, we often get two IPs -- the container IP, and the (shared) VIP, and the container IP should always be first 20 | ip address | awk ' 21 | $1 == "inet" && $NF != "lo" { 22 | gsub(/\/.+$/, "", $2) 23 | print $2 24 | exit 25 | } 26 | ' 27 | } 28 | 29 | # "sed -i", but without "mv" (which doesn't work on a bind-mounted file, for example) 30 | _sed-in-place() { 31 | local filename="$1"; shift 32 | local tempFile 33 | tempFile="$(mktemp)" 34 | sed "$@" "$filename" > "$tempFile" 35 | cat "$tempFile" > "$filename" 36 | rm "$tempFile" 37 | } 38 | 39 | if [ "$1" = 'cassandra' ]; then 40 | : ${CASSANDRA_RPC_ADDRESS='0.0.0.0'} 41 | 42 | : ${CASSANDRA_LISTEN_ADDRESS='auto'} 43 | if [ "$CASSANDRA_LISTEN_ADDRESS" = 'auto' ]; then 44 | CASSANDRA_LISTEN_ADDRESS="$(_ip_address)" 45 | fi 46 | 47 | : ${CASSANDRA_BROADCAST_ADDRESS="$CASSANDRA_LISTEN_ADDRESS"} 48 | 49 | if [ "$CASSANDRA_BROADCAST_ADDRESS" = 'auto' ]; then 50 | CASSANDRA_BROADCAST_ADDRESS="$(_ip_address)" 51 | fi 52 | : ${CASSANDRA_BROADCAST_RPC_ADDRESS:=$CASSANDRA_BROADCAST_ADDRESS} 53 | 54 | if [ -n "${CASSANDRA_NAME:+1}" ]; then 55 | : ${CASSANDRA_SEEDS:="cassandra"} 56 | fi 57 | : ${CASSANDRA_SEEDS:="$CASSANDRA_BROADCAST_ADDRESS"} 58 | 59 | _sed-in-place "$CASSANDRA_CONFIG/cassandra.yaml" \ 60 | -r 's/(- seeds:).*/\1 "'"$CASSANDRA_SEEDS"'"/' 61 | 62 | for yaml in \ 63 | broadcast_address \ 64 | broadcast_rpc_address \ 65 | cluster_name \ 66 | endpoint_snitch \ 67 | listen_address \ 68 | num_tokens \ 69 | rpc_address \ 70 | start_rpc \ 71 | ; do 72 | var="CASSANDRA_${yaml^^}" 73 | val="${!var}" 74 | if [ "$val" ]; then 75 | _sed-in-place "$CASSANDRA_CONFIG/cassandra.yaml" \ 76 | -r 's/^(# )?('"$yaml"':).*/\2 '"$val"'/' 77 | fi 78 | done 79 | 80 | for rackdc in dc rack; do 81 | var="CASSANDRA_${rackdc^^}" 82 | val="${!var}" 83 | if [ "$val" ]; then 84 | _sed-in-place "$CASSANDRA_CONFIG/cassandra-rackdc.properties" \ 85 | -r 's/^('"$rackdc"'=).*/\1 '"$val"'/' 86 | fi 87 | done 88 | fi 89 | 90 | exec "$@" -------------------------------------------------------------------------------- /discoveryx-server/src/main/scala/fusion/discoveryx/server/config/service/ConfigManagerServiceImpl.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.server.config.service 18 | 19 | import akka.actor.typed.scaladsl.AskPattern._ 20 | import akka.actor.typed.{ ActorRef, ActorSystem } 21 | import akka.cluster.sharding.typed.ShardingEnvelope 22 | import akka.util.Timeout 23 | import com.typesafe.scalalogging.StrictLogging 24 | import fusion.discoveryx.server.config.ConfigManager 25 | import fusion.discoveryx.server.grpc.ConfigManagerService 26 | import fusion.discoveryx.server.namespace.NamespaceRef 27 | import fusion.discoveryx.server.namespace.NamespaceRef.{ ExistNamespace, NamespaceExists } 28 | import fusion.discoveryx.server.protocol.ConfigManagerCommand.Cmd 29 | import fusion.discoveryx.server.protocol._ 30 | import helloscala.common.IntStatus 31 | 32 | import scala.concurrent.Future 33 | import scala.concurrent.duration._ 34 | 35 | class ConfigManagerServiceImpl(namespaceRef: ActorRef[NamespaceRef.ExistNamespace])(implicit system: ActorSystem[_]) 36 | extends ConfigManagerService 37 | with StrictLogging { 38 | private implicit val timeout: Timeout = 10.seconds 39 | private val configManager: ActorRef[ShardingEnvelope[ConfigManager.Command]] = ConfigManager.init(system) 40 | 41 | /** 42 | * #ListConfig 43 | * Query config list. Will not return config content. 44 | */ 45 | override def listConfig(in: ListConfig): Future[ConfigResponse] = askConfig(in.namespace, Cmd.List(in)) 46 | 47 | @inline private def askConfig(namespace: String, cmd: ConfigManagerCommand.Cmd): Future[ConfigResponse] = 48 | namespaceRef 49 | .ask[NamespaceExists](replyTo => ExistNamespace(namespace, replyTo)) 50 | .flatMap { 51 | case NamespaceExists(true) => 52 | configManager.ask[ConfigResponse](replyTo => ShardingEnvelope(namespace, ConfigManagerCommand(replyTo, cmd))) 53 | case _ => 54 | Future.successful(ConfigResponse(IntStatus.BAD_REQUEST, s"Namespace [$namespace] not found.")) 55 | }(system.executionContext) 56 | } 57 | -------------------------------------------------------------------------------- /discoveryx-server/src/universal/conf/application.conf: -------------------------------------------------------------------------------- 1 | discoveryx { 2 | config-modules = [akka] 3 | 4 | akka { 5 | loglevel = INFO 6 | http.server.preview.enable-http2 = on 7 | actor.provider = cluster 8 | remote.artery.canonical.port = 49001 9 | cluster.seed-nodes = ["127.0.0.1:49001"] 10 | cluster.roles = [management, config, naming] 11 | } 12 | 13 | server { 14 | management { 15 | enable = true 16 | default-page = 1 17 | default-size = 20 18 | session-timeout = 2.hours 19 | journal-on-delete = on 20 | snapshot.number-of-events = 200 21 | snapshot.keep-n-snapshots = 2 22 | } 23 | config { 24 | enable = true 25 | default-page = 1 26 | default-size = 20 27 | journal-on-delete = on 28 | snapshot.number-of-events = 200 29 | snapshot.keep-n-snapshots = 2 30 | } 31 | naming { 32 | enable = true 33 | heartbeat-timeout = 30.seconds 34 | default-page = 1 35 | default-size = 20 36 | allow-replace-registration = true 37 | journal-on-delete = on 38 | snapshot.number-of-events = 200 39 | snapshot.keep-n-snapshots = 2 40 | } 41 | } 42 | } 43 | 44 | fusion.http.default { 45 | server { 46 | host = "0.0.0.0" 47 | port = 48000 48 | } 49 | } 50 | 51 | akka-persistence-jdbc { 52 | logicalDeletion.enable = false 53 | shared-databases { 54 | h2 { 55 | profile = "slick.jdbc.H2Profile$" 56 | db { 57 | url = "jdbc:h2:~/fusion-discoveryx/db/discoveryx;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;" 58 | user = devuser 59 | password = devPass.2019 60 | numThreads = 5 61 | maxConnections = 5 62 | minConnections = 1 63 | } 64 | } 65 | postgres { 66 | profile = "slick.jdbc.PostgresProfile$" 67 | db { 68 | url = "jdbc:postgresql://localhost:5432/fusion_discoveryx?reWriteBatchedInserts=true" 69 | user = "devuser" 70 | password = "devPass.2019" 71 | driver = "org.postgresql.Driver" 72 | numThreads = 5 73 | maxConnections = 5 74 | minConnections = 1 75 | } 76 | } 77 | } 78 | } 79 | 80 | jdbc-journal { 81 | use-shared-db = "h2" 82 | } 83 | jdbc-snapshot-store { 84 | use-shared-db = "h2" 85 | } 86 | jdbc-read-journal { 87 | use-shared-db = "h2" 88 | } 89 | 90 | akka.persistence { 91 | journal { 92 | plugin = "jdbc-journal" 93 | //auto-start-journals = ["jdbc-journal"] 94 | } 95 | snapshot-store { 96 | plugin = "jdbc-snapshot-store" 97 | //auto-start-snapshot-stores = ["jdbc-snapshot-store"] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /web-console/src/stores/ConfigStore.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 配置管理 5 | * */ 6 | 7 | import { action, observable } from 'mobx'; 8 | import request from '../utils/request'; 9 | import { PAGE_OBJECT } from '../utils/constants'; 10 | 11 | export default class ConfigStore { 12 | /** 13 | * *************************** observable *************************** 14 | * */ 15 | 16 | @observable 17 | configPage = PAGE_OBJECT; 18 | 19 | @observable 20 | config = {}; 21 | 22 | /** 23 | * ****************************** ajax ****************************** 24 | * */ 25 | 26 | getConfigPage = async data => { 27 | const { listed } = await request({ 28 | config: { 29 | method: 'POST', 30 | url: '/fusion/discoveryx/console/config/ListConfig', 31 | data, 32 | }, 33 | }); 34 | const configPage = { 35 | page: listed.page, 36 | size: listed.size, 37 | totalElements: listed.totalElements, 38 | data: listed.configs, 39 | }; 40 | this.setConfigPage(configPage); 41 | return configPage; 42 | }; 43 | 44 | getConfig = async data => { 45 | const { config } = await request({ 46 | config: { 47 | method: 'POST', 48 | url: '/fusion/discoveryx/v1/config/GetConfig', 49 | data, 50 | }, 51 | }); 52 | this.setConfig(config); 53 | return config; 54 | }; 55 | 56 | createConfig = data => 57 | request({ 58 | config: { 59 | method: 'POST', 60 | url: '/fusion/discoveryx/v1/config/PublishConfig', 61 | data, 62 | }, 63 | success: { message: '新建成功' }, 64 | error: { message: '新建失败' }, 65 | }); 66 | 67 | deleteConfig = data => 68 | request({ 69 | config: { 70 | method: 'POST', 71 | url: '/fusion/discoveryx/v1/config/RemoveConfig', 72 | data, 73 | }, 74 | success: { message: '删除成功' }, 75 | error: { message: '删除失败' }, 76 | }); 77 | 78 | /** 79 | * ***************************** action ***************************** 80 | * */ 81 | 82 | @action 83 | setConfigPage(data = PAGE_OBJECT) { 84 | this.configPage = data; 85 | } 86 | 87 | @action 88 | setConfig(data = {}) { 89 | this.config = data; 90 | } 91 | 92 | /** 93 | * **************************** computed **************************** 94 | * */ 95 | // @computed 96 | // get computedData() { 97 | // if (this.msg.length > 0) { 98 | // return 'computed'; 99 | // } 100 | // return []; 101 | // } 102 | } 103 | -------------------------------------------------------------------------------- /discoveryx-client-play-ws/src/main/scala/fusion/discoveryx/client/play/javadsl/DiscoveryXWSClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 akka-fusion.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fusion.discoveryx.client.play.javadsl; 18 | 19 | import akka.actor.typed.ActorSystem; 20 | import akka.stream.SystemMaterializer; 21 | import play.libs.ws.StandaloneWSClient; 22 | import play.libs.ws.WSClient; 23 | import play.libs.ws.ahc.AhcWSClient; 24 | import play.libs.ws.ahc.AhcWSClientConfigFactory; 25 | import play.libs.ws.ahc.StandaloneAhcWSClient; 26 | import play.shaded.ahc.org.asynchttpclient.AsyncHttpClient; 27 | 28 | public class DiscoveryXWSClient { 29 | // #standaloneWSClient 30 | public static DiscoveryXStandaloneWSClient standaloneWSClient(StandaloneWSClient client, ActorSystem system) { 31 | return new DiscoveryXStandaloneWSClient(client, system); 32 | } 33 | 34 | public static DiscoveryXStandaloneWSClient standaloneWSClient(ActorSystem system) { 35 | return standaloneWSClient(StandaloneAhcWSClient.create( 36 | AhcWSClientConfigFactory.forConfig(system.settings().config(), system.dynamicAccess().classLoader()), 37 | SystemMaterializer.get(system).materializer()), system); 38 | } 39 | // #standaloneWSClient 40 | 41 | // #wsClient 42 | public static DiscoveryXPlayWSClient wsClient(WSClient client, ActorSystem system) { 43 | return new DiscoveryXPlayWSClient(client, system); 44 | } 45 | 46 | public static DiscoveryXPlayWSClient wsClient(AsyncHttpClient asyncHttpClient, ActorSystem system) { 47 | return wsClient(new AhcWSClient(asyncHttpClient, SystemMaterializer.get(system).materializer()), system); 48 | } 49 | 50 | public static DiscoveryXPlayWSClient wsClient(ActorSystem system) { 51 | return wsClient( 52 | AhcWSClient.create( 53 | AhcWSClientConfigFactory.forConfig(system.settings().config(), system.dynamicAccess().classLoader()), 54 | null, 55 | SystemMaterializer.get(system).materializer()), 56 | system); 57 | } 58 | // #wsClient 59 | } 60 | -------------------------------------------------------------------------------- /discoveryx-server/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | discoveryx { 2 | config-modules = [akka] 3 | 4 | akka { 5 | http.server.preview.enable-http2 = on 6 | actor.provider = cluster 7 | cluster.roles = [management, config, naming] 8 | } 9 | server { 10 | management { 11 | enable = true 12 | default-page = 1 13 | default-size = 20 14 | session-timeout = 2.hours 15 | journal-on-delete = on 16 | snapshot.number-of-events = 200 17 | snapshot.keep-n-snapshots = 2 18 | } 19 | config { 20 | enable = true 21 | default-page = 1 22 | default-size = 20 23 | journal-on-delete = on 24 | snapshot.number-of-events = 200 25 | snapshot.keep-n-snapshots = 2 26 | } 27 | naming { 28 | enable = true 29 | heartbeat-timeout = 30.seconds 30 | default-page = 1 31 | default-size = 20 32 | allow-replace-registration = true 33 | journal-on-delete = on 34 | snapshot.number-of-events = 200 35 | snapshot.keep-n-snapshots = 2 36 | } 37 | } 38 | } 39 | 40 | akka.persistence { 41 | journal { 42 | plugin = "jdbc-journal" 43 | plugin = ${?JOURNAL_PLUIGN} 44 | // auto-start-journals = ["jdbc-journal"] 45 | } 46 | snapshot-store { 47 | plugin = "jdbc-snapshot-store" 48 | plugin = ${?SNAPSHOT_PLUGIN} 49 | // auto-start-snapshot-stores = ["jdbc-snapshot-store"] 50 | } 51 | } 52 | 53 | akka-persistence-jdbc { 54 | logicalDeletion.enable = false 55 | shared-databases { 56 | h2 { 57 | profile = "slick.jdbc.H2Profile$" 58 | db { 59 | url = "jdbc:h2:~/fusion-discoveryx/db/discoveryx;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE;" 60 | user = devuser 61 | password = devPass.2019 62 | numThreads = 5 63 | maxConnections = 5 64 | minConnections = 1 65 | } 66 | } 67 | postgres { 68 | profile = "slick.jdbc.PostgresProfile$" 69 | db { 70 | url = "jdbc:postgresql://localhost:5432/fusion_discoveryx?reWriteBatchedInserts=true" 71 | user = "devuser" 72 | password = "devPass.2019" 73 | driver = "org.postgresql.Driver" 74 | numThreads = 5 75 | maxConnections = 5 76 | minConnections = 1 77 | } 78 | } 79 | mysql { 80 | profile = "slick.jdbc.MySQLProfile$" 81 | db { 82 | url = "jdbc:mysql://localhost:5432/fusion_discoveryx?useSSL=false&autoReconnect=true" 83 | user = "devuser" 84 | password = "devPass.2019" 85 | driver = "org.postgresql.Driver" 86 | numThreads = 5 87 | maxConnections = 5 88 | minConnections = 1 89 | } 90 | } 91 | } 92 | } 93 | 94 | cassandra-journal { 95 | support-deletes = on 96 | } 97 | -------------------------------------------------------------------------------- /web-console/src/router/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Date Author Des 3 | *---------------------------------------------- 4 | * 18-3-22 gongtiexin 配置路由 5 | * */ 6 | 7 | import Loadable from '../components/Loadable'; 8 | import { 9 | CONFIG_MANAGEMENT_CREATE, 10 | CONFIG_MANAGEMENT_DETAIL, 11 | CONFIG_MANAGEMENT_LIST, 12 | NAMESPACE_MANAGEMENT_LIST, 13 | SERVICE_MANAGEMENT_DETAIL, 14 | SERVICE_MANAGEMENT_LIST, 15 | USER_MANAGEMENT_LIST, 16 | } from './constants'; 17 | 18 | const routes = [ 19 | { 20 | path: CONFIG_MANAGEMENT_LIST, 21 | component: Loadable({ 22 | loader: () => 23 | import( 24 | /* webpackChunkName: "route-config-management-list" */ '../pages/config/management/List' 25 | ), 26 | }), 27 | }, 28 | { 29 | path: CONFIG_MANAGEMENT_CREATE, 30 | component: Loadable({ 31 | loader: () => 32 | import( 33 | /* webpackChunkName: "route-config-management-create" */ '../pages/config/management/Create' 34 | ), 35 | }), 36 | }, 37 | { 38 | path: CONFIG_MANAGEMENT_DETAIL, 39 | component: Loadable({ 40 | loader: () => 41 | import( 42 | /* webpackChunkName: "route-config-management-detail" */ '../pages/config/management/Detail' 43 | ), 44 | }), 45 | }, 46 | { 47 | path: NAMESPACE_MANAGEMENT_LIST, 48 | component: Loadable({ 49 | loader: () => 50 | import( 51 | /* webpackChunkName: "route-namespace-management-list" */ '../pages/namespace/management/List' 52 | ), 53 | }), 54 | }, 55 | { 56 | path: SERVICE_MANAGEMENT_LIST, 57 | component: Loadable({ 58 | loader: () => 59 | import( 60 | /* webpackChunkName: "route-service-management-list" */ '../pages/service/management/List' 61 | ), 62 | }), 63 | }, 64 | { 65 | path: SERVICE_MANAGEMENT_DETAIL, 66 | component: Loadable({ 67 | loader: () => 68 | import( 69 | /* webpackChunkName: "route-service-management-detail" */ '../pages/service/management/Detail' 70 | ), 71 | }), 72 | }, 73 | { 74 | path: USER_MANAGEMENT_LIST, 75 | component: Loadable({ 76 | loader: () => 77 | import( 78 | /* webpackChunkName: "route-user-management-list" */ '../pages/user/management/List' 79 | ), 80 | }), 81 | }, 82 | ]; 83 | 84 | const breadcrumbNameMap = { 85 | [CONFIG_MANAGEMENT_LIST]: '配置列表', 86 | [CONFIG_MANAGEMENT_CREATE]: '新建配置', 87 | [CONFIG_MANAGEMENT_DETAIL]: '配置详情', 88 | [NAMESPACE_MANAGEMENT_LIST]: '命名空间', 89 | [SERVICE_MANAGEMENT_LIST]: '服务列表', 90 | [SERVICE_MANAGEMENT_DETAIL]: '服务详情', 91 | [USER_MANAGEMENT_LIST]: '用户管理', 92 | }; 93 | 94 | export { routes, breadcrumbNameMap }; 95 | --------------------------------------------------------------------------------