From d037c23a11ee6913f96b840e6312f0a234329b16 Mon Sep 17 00:00:00 2001 From: andrewshan Date: Thu, 29 Jul 2021 17:13:26 +0800 Subject: [PATCH 1/3] first submit for polaris-java --- .gitignore | 46 + CONTRIBUTING.md | 26 + Code-of-Conduct.md | 5 + LICENSE | 892 ++++++++++++++++++ README-zh.md | 60 ++ README.md | 61 +- checkstyle/checkstyle.xml | 560 +++++++++++ checkstyle/suppressions.xml | 8 + .../polaris-circuitbreaker-api/pom.xml | 22 + .../circuitbreak/api/CircuitBreakAPI.java | 35 + .../polaris-circuitbreaker-client/pom.xml | 27 + .../client/api/DefaultCircuitBreakAPI.java | 58 ++ .../client/api/ServiceCallResultChecker.java | 158 ++++ .../task/InstancesCircuitBreakTask.java | 188 ++++ .../client/task/InstancesDetectTask.java | 134 +++ .../client/task/PriorityTaskScheduler.java | 47 + ...laris.client.api.ServiceCallResultListener | 1 + .../polaris-circuitbreaker-examples/pom.xml | 58 ++ .../examples/CircuitBreakExample.java | 102 ++ .../examples/CircuitBreakExampleUtils.java | 94 ++ .../src/main/resources/log4j2.xml | 28 + .../src/main/resources/polaris.yml | 6 + .../polaris-circuitbreaker-factory/pom.xml | 103 ++ .../factory/CircuitBreakAPIFactory.java | 43 + .../factory/test/CircuitBreakerTest.java | 228 +++++ .../src/test/resources/log4j2.xml | 28 + .../src/test/resources/polaris.yml | 17 + polaris-circuitbreaker/pom.xml | 21 + polaris-client/pom.xml | 108 +++ .../polaris/client/api/BaseEngine.java | 75 ++ .../polaris/client/api/SDKContext.java | 273 ++++++ .../client/api/ServiceCallResultListener.java | 77 ++ .../tencent/polaris/client/flow/BaseFlow.java | 309 ++++++ .../client/flow/DefaultFlowControlParam.java | 68 ++ .../polaris/client/flow/FlowControlParam.java | 66 ++ .../client/flow/GetResourcesInvoker.java | 255 +++++ .../client/flow/ResourcesResponse.java | 115 +++ .../test/java/MessagePersistHandlerTest.java | 63 ++ .../src/test/resources/testdata/contact.yml | 15 + .../polaris-config-opensource/pom.xml | 22 + .../DefaultOpensourceConfigProvider.java | 50 + ...t.polaris.api.config.DefaultConfigProvider | 1 + .../main/resources/conf/opensource-config.yml | 194 ++++ polaris-common/polaris-config-tencent/pom.xml | 22 + .../DefaultInternalConfigProvider.java | 74 ++ ...t.polaris.api.config.DefaultConfigProvider | 1 + .../conf/default-internal-config.yml | 183 ++++ polaris-common/polaris-config/pom.xml | 32 + .../polaris/api/config/Configuration.java | 58 ++ .../api/config/DefaultConfigProvider.java | 35 + .../config/consumer/CircuitBreakerConfig.java | 73 ++ .../api/config/consumer/ConsumerConfig.java | 65 ++ .../config/consumer/LoadBalanceConfig.java | 48 + .../api/config/consumer/LocalCacheConfig.java | 103 ++ .../consumer/OutlierDetectionConfig.java | 57 ++ .../config/consumer/ServiceRouterConfig.java | 66 ++ .../polaris/api/config/global/APIConfig.java | 73 ++ .../api/config/global/ClusterConfig.java | 69 ++ .../api/config/global/ClusterType.java | 32 + .../api/config/global/FlowCacheConfig.java | 50 + .../api/config/global/GlobalConfig.java | 57 ++ .../polaris/api/config/global/RunMode.java | 33 + .../config/global/ServerConnectorConfig.java | 79 ++ .../api/config/global/StatReporterConfig.java | 42 + .../api/config/global/SystemConfig.java | 66 ++ .../api/config/plugin/DefaultPlugins.java | 49 + .../api/config/plugin/PluginConfig.java | 50 + .../config/plugin/PluginConfigProvider.java | 40 + .../api/config/provider/ProviderConfig.java | 44 + .../api/config/provider/RateLimitConfig.java | 49 + .../api/config/provider/RegisterConfig.java | 39 + .../api/config/verify/DefaultValues.java | 256 +++++ .../polaris/api/config/verify/Verifier.java | 40 + .../polaris/factory/ConfigAPIFactory.java | 91 ++ .../factory/config/ConfigurationImpl.java | 150 +++ .../consumer/CircuitBreakerConfigImpl.java | 176 ++++ .../config/consumer/ConsumerConfigImpl.java | 123 +++ .../consumer/LoadBalanceConfigImpl.java | 69 ++ .../config/consumer/LocalCacheConfigImpl.java | 237 +++++ .../consumer/OutlierDetectionConfigImpl.java | 121 +++ .../consumer/ServiceRouterConfigImpl.java | 106 +++ .../factory/config/global/APIConfigImpl.java | 167 ++++ .../config/global/ClusterConfigImpl.java | 159 ++++ .../config/global/FlowCacheConfigImpl.java | 93 ++ .../config/global/GlobalConfigImpl.java | 114 +++ .../global/ServerConnectorConfigImpl.java | 207 ++++ .../config/global/StatReporterConfigImpl.java | 77 ++ .../config/global/SystemConfigImpl.java | 132 +++ .../config/plugin/PluginConfigImpl.java | 213 +++++ .../config/provider/ProviderConfigImpl.java | 71 ++ .../config/provider/RateLimitConfigImpl.java | 96 ++ .../config/provider/RegisterConfigImpl.java | 58 ++ .../polaris/factory/util/ConfigUtils.java | 71 ++ .../polaris/factory/util/DurationUtils.java | 101 ++ .../polaris/factory/util/IPV4Util.java | 35 + .../factory/util/TimeStrJsonDeserializer.java | 49 + polaris-common/polaris-model/pom.xml | 71 ++ .../polaris/api/control/Destroyable.java | 76 ++ .../polaris/api/exception/ErrorCode.java | 179 ++++ .../api/exception/PolarisException.java | 58 ++ .../api/exception/RetriableException.java | 35 + .../polaris/api/exception/ServerCodes.java | 89 ++ .../ServerErrorResponseException.java | 50 + .../api/pojo/CircuitBreakerStatus.java | 109 +++ .../polaris/api/pojo/DefaultInstance.java | 251 +++++ .../pojo/DefaultServiceEventKeysProvider.java | 66 ++ .../polaris/api/pojo/DetectResult.java | 93 ++ .../tencent/polaris/api/pojo/Instance.java | 95 ++ .../polaris/api/pojo/InstanceGauge.java | 111 +++ .../polaris/api/pojo/InstanceLocalValue.java | 69 ++ .../polaris/api/pojo/InstanceWeight.java | 44 + .../api/pojo/OutlierDetectionStatus.java | 80 ++ .../polaris/api/pojo/RegistryCacheValue.java | 54 ++ .../tencent/polaris/api/pojo/RetStatus.java | 41 + .../com/tencent/polaris/api/pojo/Service.java | 35 + .../polaris/api/pojo/ServiceEventKey.java | 73 ++ .../api/pojo/ServiceEventKeysProvider.java | 44 + .../tencent/polaris/api/pojo/ServiceInfo.java | 72 ++ .../polaris/api/pojo/ServiceInstances.java | 65 ++ .../api/pojo/ServiceInstancesWrap.java | 139 +++ .../tencent/polaris/api/pojo/ServiceKey.java | 76 ++ .../polaris/api/pojo/ServiceMetadata.java | 37 + .../tencent/polaris/api/pojo/ServiceRule.java | 45 + .../polaris/api/pojo/StatusDimension.java | 105 +++ .../com/tencent/polaris/api/pojo/Subset.java | 59 ++ .../tencent/polaris/api/rpc/BaseEntity.java | 56 ++ .../com/tencent/polaris/api/rpc/Criteria.java | 61 ++ .../polaris/api/rpc/MetadataFailoverType.java | 62 ++ .../polaris/api/rpc/RequestBaseEntity.java | 48 + .../polaris/api/rpc/ServiceCallResult.java | 240 +++++ .../api/utils/ClosableReadWriteLock.java | 59 ++ .../polaris/api/utils/CollectionUtils.java | 31 + .../tencent/polaris/api/utils/MapUtils.java | 31 + .../tencent/polaris/api/utils/RuleUtils.java | 36 + .../polaris/api/utils/StringUtils.java | 101 ++ .../polaris/api/utils/ThreadPoolUtils.java | 52 + .../pojo/DefaultInstanceLocalValue.java | 85 ++ .../polaris/client/pojo/InstanceByProto.java | 214 +++++ .../com/tencent/polaris/client/pojo/Node.java | 68 ++ .../client/pojo/ServiceInstancesByProto.java | 232 +++++ .../client/pojo/ServiceRuleByProto.java | 92 ++ .../polaris/client/util/CommonValidator.java | 41 + .../client/util/NamedThreadFactory.java | 41 + .../tencent/polaris/client/util/Utils.java | 93 ++ .../src/main/templates/Version.java | 30 + polaris-common/polaris-protobuf/pom.xml | 67 ++ .../src/main/proto/circuitbreaker.proto | 185 ++++ .../src/main/proto/client.proto | 25 + .../src/main/proto/grpcapi_discovery.proto | 27 + .../src/main/proto/grpcapi_v2_metric.proto | 17 + .../src/main/proto/model.proto | 31 + .../src/main/proto/ratelimit.proto | 180 ++++ .../src/main/proto/ratelimit_v2.proto | 168 ++++ .../src/main/proto/request.proto | 23 + .../src/main/proto/response.proto | 74 ++ .../src/main/proto/routing.proto | 72 ++ .../src/main/proto/service.proto | 100 ++ polaris-common/pom.xml | 22 + polaris-dependencies/pom.xml | 222 +++++ .../polaris-discovery-api/pom.xml | 27 + .../tencent/polaris/api/core/ConsumerAPI.java | 118 +++ .../tencent/polaris/api/core/ProviderAPI.java | 69 ++ .../api/rpc/CommonProviderBaseEntity.java | 69 ++ .../api/rpc/GetAllInstancesRequest.java | 47 + .../polaris/api/rpc/GetInstancesRequest.java | 135 +++ .../api/rpc/GetOneInstanceRequest.java | 121 +++ .../polaris/api/rpc/GetResourcesRequest.java | 53 ++ .../polaris/api/rpc/GetResourcesResponse.java | 47 + .../api/rpc/GetServiceRuleRequest.java | 44 + .../api/rpc/InstanceDeregisterRequest.java | 49 + .../api/rpc/InstanceHeartbeatRequest.java | 32 + .../api/rpc/InstanceRegisterRequest.java | 95 ++ .../api/rpc/InstanceRegisterResponse.java | 53 ++ .../polaris/api/rpc/InstancesFuture.java | 27 + .../polaris/api/rpc/InstancesResponse.java | 87 ++ .../polaris/api/rpc/ServiceRuleResponse.java | 44 + .../polaris-discovery-client/pom.xml | 32 + .../client/api/DefaultConsumerAPI.java | 148 +++ .../client/api/DefaultProviderAPI.java | 118 +++ .../discovery/client/flow/AsyncFlow.java | 71 ++ .../client/flow/CommonInstancesRequest.java | 218 +++++ .../client/flow/CommonRuleRequest.java | 89 ++ .../client/flow/GetAllInstancesSupplier.java | 41 + .../client/flow/GetInstancesSupplier.java | 41 + .../client/flow/GetOneInstanceSupplier.java | 41 + .../discovery/client/flow/SyncFlow.java | 124 +++ .../discovery/client/util/Validator.java | 192 ++++ .../polaris-discovery-examples/pom.xml | 47 + .../consumer/GetAllInstancesExample.java | 42 + .../consumer/GetOneInstanceExample.java | 46 + .../examples/provider/DeregisterExample.java | 47 + .../examples/provider/HeartbeatExample.java | 46 + .../examples/provider/RegisterExample.java | 50 + .../router/examples/utils/ExampleUtils.java | 136 +++ .../src/main/resources/log4j2.xml | 28 + .../src/main/resources/polaris.yml | 12 + .../polaris-discovery-factory/pom.xml | 118 +++ .../factory/api/DiscoveryAPIFactory.java | 134 +++ .../discovery/test/core/ConsumerTest.java | 338 +++++++ .../test/core/MetadataRouterTest.java | 207 ++++ .../discovery/test/core/ProviderTest.java | 104 ++ .../discovery/test/core/RetryConnectTest.java | 115 +++ .../test/core/ServiceDynamicRuleTest.java | 110 +++ .../test/core/ServiceExpireTest.java | 171 ++++ .../test/suite/DiscoveryAPITestingSuite.java | 34 + .../src/test/resources/log4j2.xml | 28 + .../src/test/resources/polaris.yml | 21 + polaris-discovery/pom.xml | 22 + .../polaris-factory-shaded/pom.xml | 83 ++ polaris-distribution/pom.xml | 19 + polaris-factory/pom.xml | 46 + .../polaris/factory/api/APIFactory.java | 209 ++++ .../factory/api/test/APIFactoryTest.java | 114 +++ .../src/test/resources/conf/default.yaml | 88 ++ polaris-factory/src/test/resources/log4j2.xml | 28 + .../src/test/resources/svc1_routing.json | 40 + polaris-plugins/polaris-plugin-api/pom.xml | 27 + .../polaris/api/plugin/IdAwarePlugin.java | 35 + .../tencent/polaris/api/plugin/Manager.java | 54 ++ .../tencent/polaris/api/plugin/Plugin.java | 65 ++ .../polaris/api/plugin/PluginType.java | 82 ++ .../tencent/polaris/api/plugin/Supplier.java | 50 + .../polaris/api/plugin/TypeProvider.java | 33 + .../polaris/api/plugin/cache/FlowCache.java | 64 ++ .../api/plugin/cache/PluginHashKey.java | 52 + .../circuitbreaker/CircuitBreakResult.java | 148 +++ .../plugin/circuitbreaker/CircuitBreaker.java | 57 ++ .../api/plugin/common/InitContext.java | 40 + .../api/plugin/common/PluginTypes.java | 95 ++ .../api/plugin/common/RpcTypeProviders.java | 35 + .../api/plugin/common/ValueContext.java | 104 ++ .../compose/DefaultRouterChainGroup.java | 56 ++ .../api/plugin/compose/Extensions.java | 189 ++++ .../api/plugin/compose/RouterChainGroup.java | 48 + .../api/plugin/compose/ServerServiceInfo.java | 75 ++ .../api/plugin/detect/OutlierDetector.java | 42 + .../api/plugin/impl/PluginManager.java | 144 +++ .../api/plugin/loadbalance/LoadBalancer.java | 43 + .../api/plugin/ratelimiter/AmountInfo.java | 31 + .../api/plugin/ratelimiter/InitCriteria.java | 43 + .../plugin/ratelimiter/LocalQuotaInfo.java | 39 + .../api/plugin/ratelimiter/QuotaBucket.java | 64 ++ .../api/plugin/ratelimiter/QuotaResult.java | 56 ++ .../plugin/ratelimiter/RemoteQuotaInfo.java | 72 ++ .../ratelimiter/ServiceRateLimiter.java | 32 + .../AbstractResourceEventListener.java | 40 + .../api/plugin/registry/CacheHandler.java | 74 ++ .../registry/EventCompleteNotifier.java | 41 + .../api/plugin/registry/InstanceProperty.java | 72 ++ .../api/plugin/registry/LocalRegistry.java | 95 ++ .../registry/ResourceEventListener.java | 33 + .../api/plugin/registry/ResourceFilter.java | 55 ++ .../api/plugin/registry/RuleFilter.java | 46 + .../plugin/registry/ServiceUpdateRequest.java | 57 ++ .../api/plugin/route/LocationLevel.java | 8 + .../polaris/api/plugin/route/RouteInfo.java | 200 ++++ .../polaris/api/plugin/route/RouteResult.java | 114 +++ .../api/plugin/route/RouterConstants.java | 12 + .../api/plugin/route/ServiceRouter.java | 61 ++ .../plugin/server/CommonProviderRequest.java | 162 ++++ .../plugin/server/CommonProviderResponse.java | 41 + .../api/plugin/server/EventHandler.java | 35 + .../plugin/server/ReportClientRequest.java | 59 ++ .../plugin/server/ReportClientResponse.java | 73 ++ .../api/plugin/server/ServerConnector.java | 89 ++ .../api/plugin/server/ServerEvent.java | 64 ++ .../plugin/server/ServiceEventHandler.java | 93 ++ .../api/plugin/stat/CircuitBreakGauge.java | 79 ++ .../stat/DefaultCircuitBreakResult.java | 98 ++ .../plugin/stat/DefaultRateLimitResult.java | 54 ++ .../api/plugin/stat/RateLimitGauge.java | 48 + .../polaris/api/plugin/stat/StatInfo.java | 53 ++ .../polaris/api/plugin/stat/StatReporter.java | 38 + .../api/plugin/weight/WeightAdjuster.java | 50 + .../polaris/api/plugin/weight/WeightType.java | 36 + ...om.tencent.polaris.api.plugin.TypeProvider | 1 + .../circuitbreaker-common/pom.xml | 23 + .../common/AbstractStateMachine.java | 178 ++++ .../common/ChangeStateUtils.java | 155 +++ .../common/CircuitBreakUtils.java | 269 ++++++ .../circuitbreaker/common/ConfigGroup.java | 46 + .../circuitbreaker/common/ConfigSet.java | 58 ++ .../common/ConfigSetLocator.java | 32 + .../circuitbreaker/common/HalfOpenConfig.java | 85 ++ .../common/HalfOpenCounter.java | 94 ++ .../circuitbreaker/common/RuleIdentifier.java | 88 ++ .../circuitbreaker/common/StateMachine.java | 118 +++ .../circuitbreaker/common/stat/Bucket.java | 130 +++ .../common/stat/ResMetricArray.java | 54 ++ .../common/stat/SliceWindow.java | 196 ++++ .../common/stat/TimePosition.java | 40 + .../circuitbreaker/common/stat/TimeRange.java | 69 ++ .../circuitbreaker-errcount/pom.xml | 22 + .../circuitbreaker/errcount/Config.java | 66 ++ .../errcount/ConsecutiveCircuitBreaker.java | 223 +++++ .../errcount/ConsecutiveCounter.java | 63 ++ .../errcount/StateMachineImpl.java | 55 ++ ...ris.api.config.plugin.PluginConfigProvider | 1 + ...s.api.plugin.circuitbreaker.CircuitBreaker | 1 + .../circuitbreaker-errrate/pom.xml | 22 + .../circuitbreaker/errrate/Config.java | 131 +++ .../circuitbreaker/errrate/Dimension.java | 40 + .../errrate/ErrRateCircuitBreaker.java | 231 +++++ .../errrate/ErrRateCounter.java | 64 ++ .../errrate/StateMachineImpl.java | 76 ++ ...ris.api.config.plugin.PluginConfigProvider | 1 + ...s.api.plugin.circuitbreaker.CircuitBreaker | 1 + .../circuitbreaker/pom.xml | 21 + .../outlier-detector-http/pom.xml | 16 + .../detector/http/HttpOutlierDetector.java | 95 ++ ....polaris.api.plugin.detect.OutlierDetector | 1 + .../outlier-detector-tcp/pom.xml | 16 + .../detector/tcp/TcpOutlierDetector.java | 141 +++ ....polaris.api.plugin.detect.OutlierDetector | 1 + .../outlier-detector-udp/pom.xml | 16 + .../detector/udp/UdpOutlierDetector.java | 109 +++ ....polaris.api.plugin.detect.OutlierDetector | 1 + .../outlier-detector/pom.xml | 21 + .../polaris-plugins-circuitbreaker/pom.xml | 36 + .../connector/connector-polaris-grpc/pom.xml | 23 + .../plugins/connector/grpc/Connection.java | 268 ++++++ .../connector/grpc/ConnectionManager.java | 452 +++++++++ .../plugins/connector/grpc/GrpcConnector.java | 488 ++++++++++ .../plugins/connector/grpc/GrpcUtil.java | 176 ++++ .../connector/grpc/ServiceUpdateTask.java | 227 +++++ .../connector/grpc/SpecStreamClient.java | 355 +++++++ .../grpc/codec/AbstractCacheHandler.java | 39 + .../grpc/codec/CircuitBreakCacheHandler.java | 52 + .../connector/grpc/codec/CommonHandler.java | 83 ++ .../grpc/codec/RateLimitingCacheHandler.java | 64 ++ .../grpc/codec/RoutingCacheHandler.java | 52 + .../codec/ServiceInstancesCacheHandler.java | 55 ++ ...t.polaris.api.plugin.registry.CacheHandler | 4 + ....polaris.api.plugin.server.ServerConnector | 1 + .../polaris-plugins-client/connector/pom.xml | 19 + .../flow-cache/flow-cache-expired/pom.xml | 16 + .../flow/cache/expired/ExpiredFlowCache.java | 151 +++ .../flow/cache/expired/FlowCacheItem.java | 42 + .../flow/cache/expired/PatternCacheItem.java | 34 + .../flow/cache/expired/PluginCacheItem.java | 48 + .../flow/cache/expired/PluginCacheValues.java | 52 + ...tencent.polaris.api.plugin.cache.FlowCache | 1 + .../polaris-plugins-client/flow-cache/pom.xml | 19 + .../polaris-plugins-client/pom.xml | 38 + .../resource-cache/pom.xml | 19 + .../resource-cache-memory/pom.xml | 22 + .../local/cache/memory/CacheObject.java | 299 ++++++ .../local/cache/memory/InMemoryRegistry.java | 661 +++++++++++++ .../cache/memory/MessagePersistHandler.java | 323 +++++++ ....polaris.api.plugin.registry.LocalRegistry | 1 + .../loadbalancer/loadbalancer-random/pom.xml | 15 + .../random/WeightedRandomBalance.java | 104 ++ ...olaris.api.plugin.loadbalance.LoadBalancer | 1 + .../loadbalancer-ringhash/pom.xml | 16 + .../ringhash/ConsistentHashLoadBalance.java | 138 +++ .../loadbalancer/ringhash/HashStrategy.java | 26 + .../loadbalancer/ringhash/MurmurHash.java | 69 ++ ...olaris.api.plugin.loadbalance.LoadBalancer | 1 + .../loadbalancer/pom.xml | 20 + .../polaris-plugins-discovery/pom.xml | 38 + .../polaris-plugins-discovery/router/pom.xml | 27 + .../router/router-canary/pom.xml | 22 + .../plugin/router/canary/CanaryRouter.java | 90 ++ ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../router/router-common/pom.xml | 15 + .../router/common/AbstractServiceRouter.java | 109 +++ .../router/router-healthy/pom.xml | 22 + .../plugins/router/healthy/RecoverRouter.java | 77 ++ ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../router/router-isolated/pom.xml | 22 + .../router/isolated/IsolatedRouter.java | 68 ++ ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../router/router-metadata/pom.xml | 22 + .../plugins/router/metadata/FailOverType.java | 22 + .../router/metadata/MetadataRouter.java | 178 ++++ .../router/metadata/MetadataRouterConfig.java | 41 + ...ris.api.config.plugin.PluginConfigProvider | 1 + ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../router/router-nearby/pom.xml | 22 + .../plugins/router/nearby/NearbyRouter.java | 374 ++++++++ .../router/nearby/NearbyRouterConfig.java | 151 +++ .../router/nearby/ReportClientTask.java | 78 ++ ...ris.api.config.plugin.PluginConfigProvider | 1 + ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../router/router-rule/pom.xml | 22 + .../plugins/router/rule/PrioritySubsets.java | 45 + .../plugins/router/rule/RuleBasedRouter.java | 529 +++++++++++ .../plugins/router/rule/RuleMatchType.java | 19 + .../plugins/router/rule/RuleStatus.java | 30 + .../plugins/router/rule/WeightedSubset.java | 49 + ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../router/router-set/pom.xml | 22 + .../polaris/plugin/router/set/SetRouter.java | 239 +++++ ...ent.polaris.api.plugin.route.ServiceRouter | 1 + .../polaris-plugins-observability/pom.xml | 37 + .../stat-common/pom.xml | 22 + .../common/api/ServiceCallStatCollector.java | 104 ++ .../AbstractSignatureStatInfoCollector.java | 53 ++ .../model/MetricValueAggregationStrategy.java | 55 ++ ...icValueAggregationStrategyCollections.java | 334 +++++++ .../stat/common/model/StatInfoCollector.java | 45 + .../model/StatInfoCollectorContainer.java | 53 ++ .../common/model/StatInfoGaugeCollector.java | 63 ++ .../stat/common/model/StatInfoHandler.java | 38 + .../model/StatInfoRevisionCollector.java | 90 ++ .../plugins/stat/common/model/StatMetric.java | 87 ++ .../stat/common/model/StatRevisionMetric.java | 38 + .../stat/common/model/SystemMetricModel.java | 71 ++ .../plugins/stat/common/util/FnvHash.java | 43 + .../stat/common/util/SignatureUtil.java | 47 + ...laris.client.api.ServiceCallResultListener | 1 + .../polaris/plugins/stat/common/TestUtil.java | 32 + .../model/StatInfoGaugeCollectorTest.java | 118 +++ .../model/StatInfoRevisionCollectorTest.java | 79 ++ .../stat/common/model/StatMetricTest.java | 46 + .../stat/common/util/SignatureUtilTest.java | 27 + .../stat-prometheus/pom.xml | 27 + .../handler/PrometheusPushHandler.java | 391 ++++++++ .../handler/PrometheusPushHandlerConfig.java | 93 ++ .../PrometheusPushHandlerConfigProvider.java | 43 + .../handler/PushAddressProvider.java | 22 + .../handler/ServiceDiscoveryProvider.java | 53 ++ .../prometheus/plugin/PrometheusReporter.java | 97 ++ ...ris.api.config.plugin.PluginConfigProvider | 1 + ...ncent.polaris.api.plugin.stat.StatReporter | 1 + .../handler/PrometheusPushHandlerTest.java | 445 +++++++++ .../src/test/resources/log4j2.xml | 28 + .../polaris-plugins-ratelimit/pom.xml | 36 + .../ratelimiter/pom.xml | 20 + .../ratelimiter/ratelimiter-common/pom.xml | 16 + .../common/bucket/BucketShareInfo.java | 58 ++ .../common/bucket/UpdateIdentifier.java | 44 + .../common/slide/SlidingWindow.java | 131 +++ .../ratelimiter/common/slide/Window.java | 115 +++ .../ratelimiter/ratelimiter-reject/pom.xml | 22 + .../ratelimiter/reject/RejectRateLimiter.java | 61 ++ .../ratelimiter/reject/RemoteAwareBucket.java | 202 ++++ .../ratelimiter/reject/TokenBucket.java | 423 +++++++++ ....api.plugin.ratelimiter.ServiceRateLimiter | 1 + polaris-plugins/pom.xml | 23 + .../polaris-ratelimit-api/pom.xml | 27 + .../polaris/ratelimit/api/core/LimitAPI.java | 48 + .../ratelimit/api/rpc/QuotaRequest.java | 75 ++ .../ratelimit/api/rpc/QuotaResponse.java | 41 + .../ratelimit/api/rpc/QuotaResultCode.java | 32 + .../polaris-ratelimit-client/pom.xml | 27 + .../ratelimit/client/api/DefaultLimitAPI.java | 127 +++ .../client/flow/AsyncRateLimitConnector.java | 123 +++ .../client/flow/DurationBaseCallback.java | 31 + .../ratelimit/client/flow/HostIdentifier.java | 60 ++ .../client/flow/InitializeRecord.java | 37 + .../ratelimit/client/flow/QuotaFlow.java | 289 ++++++ .../client/flow/RateLimitExtension.java | 129 +++ .../client/flow/RateLimitWindow.java | 273 ++++++ .../client/flow/RateLimitWindowSet.java | 150 +++ .../client/flow/ServiceIdentifier.java | 53 ++ .../client/flow/StreamCounterSet.java | 365 +++++++ .../client/flow/WindowContainer.java | 100 ++ .../client/pojo/CommonQuotaRequest.java | 117 +++ .../ratelimit/client/sync/RemoteSyncTask.java | 246 +++++ .../client/utils/LimitValidator.java | 39 + .../client/utils/RateLimitConstants.java | 65 ++ .../polaris-ratelimit-examples/pom.xml | 47 + .../ratelimit/examples/RateLimitExample.java | 66 ++ .../examples/utils/LimitExampleUtils.java | 89 ++ .../src/main/resources/log4j2.xml | 28 + .../src/main/resources/polaris.yml | 6 + .../polaris-ratelimit-factory/pom.xml | 91 ++ .../ratelimit/factory/LimitAPIFactory.java | 61 ++ .../polaris/ratelimit/test/core/Consts.java | 55 ++ .../ratelimit/test/core/LocalTest.java | 159 ++++ .../src/test/resources/log4j2.xml | 28 + .../src/test/resources/polaris.yml | 13 + polaris-ratelimit/pom.xml | 21 + polaris-router/polaris-router-api/pom.xml | 22 + .../polaris/router/api/core/RouterAPI.java | 49 + .../api/rpc/ProcessLoadBalanceRequest.java | 54 ++ .../api/rpc/ProcessLoadBalanceResponse.java | 33 + .../router/api/rpc/ProcessRoutersRequest.java | 102 ++ .../api/rpc/ProcessRoutersResponse.java | 37 + polaris-router/polaris-router-client/pom.xml | 27 + .../router/client/api/DefaultRouterAPI.java | 146 +++ .../router/client/util/RouterValidator.java | 54 ++ .../polaris-router-examples/pom.xml | 52 + .../polaris/router/examples/ExampleUtils.java | 71 ++ .../router/examples/RouterExample.java | 92 ++ .../src/main/resources/log4j2.xml | 28 + .../src/main/resources/polaris.yml | 12 + polaris-router/polaris-router-factory/pom.xml | 22 + .../polaris/factory/api/RouterAPIFactory.java | 78 ++ polaris-router/pom.xml | 22 + polaris-test/polaris-test-common/pom.xml | 16 + .../tencent/polaris/test/common/Consts.java | 41 + .../polaris/test/common/TestUtils.java | 62 ++ .../polaris-test-mock-discovery/pom.xml | 22 + .../mock/discovery/HeaderInterceptor.java | 38 + .../test/mock/discovery/LocationInfo.java | 44 + .../test/mock/discovery/NamingServer.java | 80 ++ .../test/mock/discovery/NamingService.java | 441 +++++++++ polaris-test/pom.xml | 28 + pom.xml | 185 ++++ 501 files changed, 41859 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 Code-of-Conduct.md create mode 100644 LICENSE create mode 100644 README-zh.md create mode 100644 checkstyle/checkstyle.xml create mode 100644 checkstyle/suppressions.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-api/pom.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-api/src/main/java/com/tencent/polaris/circuitbreak/api/CircuitBreakAPI.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/pom.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/DefaultCircuitBreakAPI.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/ServiceCallResultChecker.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesCircuitBreakTask.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesDetectTask.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/PriorityTaskScheduler.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-examples/pom.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExample.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExampleUtils.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/log4j2.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/polaris.yml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-factory/pom.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-factory/src/main/java/com/tencent/polaris/circuitbreak/factory/CircuitBreakAPIFactory.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/java/com/tencent/polaris/circuitbreaker/factory/test/CircuitBreakerTest.java create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/log4j2.xml create mode 100644 polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/polaris.yml create mode 100644 polaris-circuitbreaker/pom.xml create mode 100644 polaris-client/pom.xml create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/api/BaseEngine.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/api/SDKContext.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/api/ServiceCallResultListener.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/flow/DefaultFlowControlParam.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/flow/FlowControlParam.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/flow/GetResourcesInvoker.java create mode 100644 polaris-client/src/main/java/com/tencent/polaris/client/flow/ResourcesResponse.java create mode 100644 polaris-client/src/test/java/MessagePersistHandlerTest.java create mode 100644 polaris-client/src/test/resources/testdata/contact.yml create mode 100644 polaris-common/polaris-config-opensource/pom.xml create mode 100644 polaris-common/polaris-config-opensource/src/main/java/com/tencent/polaris/config/opensource/DefaultOpensourceConfigProvider.java create mode 100644 polaris-common/polaris-config-opensource/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider create mode 100644 polaris-common/polaris-config-opensource/src/main/resources/conf/opensource-config.yml create mode 100644 polaris-common/polaris-config-tencent/pom.xml create mode 100644 polaris-common/polaris-config-tencent/src/main/java/com/tencent/polaris/config/internal/DefaultInternalConfigProvider.java create mode 100644 polaris-common/polaris-config-tencent/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider create mode 100644 polaris-common/polaris-config-tencent/src/main/resources/conf/default-internal-config.yml create mode 100644 polaris-common/polaris-config/pom.xml create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/Configuration.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/DefaultConfigProvider.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/CircuitBreakerConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ConsumerConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LoadBalanceConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LocalCacheConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/OutlierDetectionConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/APIConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterType.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/FlowCacheConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/GlobalConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/RunMode.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ServerConnectorConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/StatReporterConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/SystemConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfigProvider.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RateLimitConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RegisterConfig.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/DefaultValues.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/Verifier.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/ConfigAPIFactory.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/ConfigurationImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/CircuitBreakerConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ConsumerConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LoadBalanceConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LocalCacheConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/OutlierDetectionConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ServiceRouterConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/APIConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ClusterConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/FlowCacheConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/GlobalConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ServerConnectorConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/StatReporterConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/SystemConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/plugin/PluginConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RateLimitConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RegisterConfigImpl.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/ConfigUtils.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/DurationUtils.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/IPV4Util.java create mode 100644 polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/TimeStrJsonDeserializer.java create mode 100644 polaris-common/polaris-model/pom.xml create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/control/Destroyable.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ErrorCode.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/PolarisException.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/RetriableException.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerCodes.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerErrorResponseException.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/CircuitBreakerStatus.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceEventKeysProvider.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DetectResult.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceGauge.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceLocalValue.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceWeight.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/OutlierDetectionStatus.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RegistryCacheValue.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RetStatus.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Service.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKeysProvider.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInfo.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstances.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstancesWrap.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceKey.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceMetadata.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceRule.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/StatusDimension.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Subset.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/BaseEntity.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/Criteria.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/MetadataFailoverType.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/RequestBaseEntity.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/ServiceCallResult.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ClosableReadWriteLock.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CollectionUtils.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/MapUtils.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/StringUtils.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ThreadPoolUtils.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/DefaultInstanceLocalValue.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/InstanceByProto.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/Node.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceInstancesByProto.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceRuleByProto.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/CommonValidator.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/NamedThreadFactory.java create mode 100644 polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/Utils.java create mode 100644 polaris-common/polaris-model/src/main/templates/Version.java create mode 100644 polaris-common/polaris-protobuf/pom.xml create mode 100644 polaris-common/polaris-protobuf/src/main/proto/circuitbreaker.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/client.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/grpcapi_discovery.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/grpcapi_v2_metric.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/model.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/ratelimit.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/ratelimit_v2.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/request.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/response.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/routing.proto create mode 100644 polaris-common/polaris-protobuf/src/main/proto/service.proto create mode 100644 polaris-common/pom.xml create mode 100644 polaris-dependencies/pom.xml create mode 100644 polaris-discovery/polaris-discovery-api/pom.xml create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ConsumerAPI.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ProviderAPI.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/CommonProviderBaseEntity.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetAllInstancesRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetInstancesRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetOneInstanceRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesResponse.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetServiceRuleRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceDeregisterRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceHeartbeatRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterRequest.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterResponse.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesFuture.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesResponse.java create mode 100644 polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/ServiceRuleResponse.java create mode 100644 polaris-discovery/polaris-discovery-client/pom.xml create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultConsumerAPI.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultProviderAPI.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/AsyncFlow.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonInstancesRequest.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonRuleRequest.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetAllInstancesSupplier.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetInstancesSupplier.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetOneInstanceSupplier.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/SyncFlow.java create mode 100644 polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/util/Validator.java create mode 100644 polaris-discovery/polaris-discovery-examples/pom.xml create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetAllInstancesExample.java create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetOneInstanceExample.java create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/DeregisterExample.java create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/HeartbeatExample.java create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/RegisterExample.java create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/utils/ExampleUtils.java create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/resources/log4j2.xml create mode 100644 polaris-discovery/polaris-discovery-examples/src/main/resources/polaris.yml create mode 100644 polaris-discovery/polaris-discovery-factory/pom.xml create mode 100644 polaris-discovery/polaris-discovery-factory/src/main/java/com/tencent/polaris/factory/api/DiscoveryAPIFactory.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ConsumerTest.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/MetadataRouterTest.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ProviderTest.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceDynamicRuleTest.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceExpireTest.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/suite/DiscoveryAPITestingSuite.java create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/resources/log4j2.xml create mode 100644 polaris-discovery/polaris-discovery-factory/src/test/resources/polaris.yml create mode 100644 polaris-discovery/pom.xml create mode 100644 polaris-distribution/polaris-factory-shaded/pom.xml create mode 100644 polaris-distribution/pom.xml create mode 100644 polaris-factory/pom.xml create mode 100644 polaris-factory/src/main/java/com/tencent/polaris/factory/api/APIFactory.java create mode 100644 polaris-factory/src/test/java/com/tencent/polaris/factory/api/test/APIFactoryTest.java create mode 100644 polaris-factory/src/test/resources/conf/default.yaml create mode 100644 polaris-factory/src/test/resources/log4j2.xml create mode 100644 polaris-factory/src/test/resources/svc1_routing.json create mode 100644 polaris-plugins/polaris-plugin-api/pom.xml create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/IdAwarePlugin.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Manager.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Plugin.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/PluginType.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Supplier.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/TypeProvider.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/FlowCache.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/PluginHashKey.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreakResult.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreaker.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/InitContext.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/RpcTypeProviders.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/ValueContext.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/DefaultRouterChainGroup.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/RouterChainGroup.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/ServerServiceInfo.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/detect/OutlierDetector.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/impl/PluginManager.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/loadbalance/LoadBalancer.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/AmountInfo.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/InitCriteria.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/LocalQuotaInfo.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaBucket.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaResult.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/RemoteQuotaInfo.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/ServiceRateLimiter.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/AbstractResourceEventListener.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/EventCompleteNotifier.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/InstanceProperty.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/LocalRegistry.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceEventListener.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceFilter.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/RuleFilter.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ServiceUpdateRequest.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/ServiceRouter.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderRequest.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderResponse.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/EventHandler.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientRequest.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientResponse.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerConnector.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerEvent.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServiceEventHandler.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/CircuitBreakGauge.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultCircuitBreakResult.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultRateLimitResult.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/RateLimitGauge.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatInfo.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatReporter.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightAdjuster.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightType.java create mode 100644 polaris-plugins/polaris-plugin-api/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.TypeProvider create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/AbstractStateMachine.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ChangeStateUtils.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/CircuitBreakUtils.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigGroup.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSet.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSetLocator.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenConfig.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenCounter.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/RuleIdentifier.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/StateMachine.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/Bucket.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/ResMetricArray.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/SliceWindow.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimePosition.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimeRange.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/Config.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCircuitBreaker.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCounter.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/StateMachineImpl.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Config.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Dimension.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCircuitBreaker.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCounter.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/StateMachineImpl.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/java/com/tencent/polaris/plugins/outlier/detector/http/HttpOutlierDetector.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/java/com/tencent/polaris/plugins/outlier/detector/tcp/TcpOutlierDetector.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/java/com/tencent/polaris/plugins/outlier/detector/udp/UdpOutlierDetector.java create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/pom.xml create mode 100644 polaris-plugins/polaris-plugins-circuitbreaker/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/Connection.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ConnectionManager.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcConnector.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ServiceUpdateTask.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/SpecStreamClient.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/AbstractCacheHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CommonHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RateLimitingCacheHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler create mode 100644 polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.server.ServerConnector create mode 100644 polaris-plugins/polaris-plugins-client/connector/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/ExpiredFlowCache.java create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/FlowCacheItem.java create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PatternCacheItem.java create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheItem.java create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheValues.java create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.cache.FlowCache create mode 100644 polaris-plugins/polaris-plugins-client/flow-cache/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/resource-cache/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/pom.xml create mode 100644 polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/CacheObject.java create mode 100644 polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/InMemoryRegistry.java create mode 100644 polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/MessagePersistHandler.java create mode 100644 polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.LocalRegistry create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/java/com/tencent/polaris/plugins/loadbalancer/random/WeightedRandomBalance.java create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/ConsistentHashLoadBalance.java create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/HashStrategy.java create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/MurmurHash.java create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer create mode 100644 polaris-plugins/polaris-plugins-discovery/loadbalancer/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-canary/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/java/com/tencent/polaris/plugin/router/canary/CanaryRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-common/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-common/src/main/java/com/tencent/polaris/plugins/router/common/AbstractServiceRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-healthy/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/java/com/tencent/polaris/plugins/router/healthy/RecoverRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-isolated/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/java/com/tencent/polaris/plugins/router/isolated/IsolatedRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-metadata/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/FailOverType.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouterConfig.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-nearby/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/ReportClientTask.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/PrioritySubsets.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleMatchType.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleStatus.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/WeightedSubset.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-set/pom.xml create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/java/com/tencent/polaris/plugin/router/set/SetRouter.java create mode 100644 polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter create mode 100644 polaris-plugins/polaris-plugins-observability/pom.xml create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/pom.xml create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/api/ServiceCallStatCollector.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/AbstractSignatureStatInfoCollector.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategy.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategyCollections.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollector.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollectorContainer.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollector.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoHandler.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollector.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatMetric.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatRevisionMetric.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/SystemMetricModel.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/FnvHash.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtil.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/TestUtil.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollectorTest.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollectorTest.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatMetricTest.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtilTest.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/pom.xml create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandler.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfig.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfigProvider.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PushAddressProvider.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/ServiceDiscoveryProvider.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/plugin/PrometheusReporter.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.stat.StatReporter create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerTest.java create mode 100644 polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/resources/log4j2.xml create mode 100644 polaris-plugins/polaris-plugins-ratelimit/pom.xml create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/pom.xml create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/pom.xml create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/BucketShareInfo.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/UpdateIdentifier.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/SlidingWindow.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/Window.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/pom.xml create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RejectRateLimiter.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RemoteAwareBucket.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/TokenBucket.java create mode 100644 polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter create mode 100644 polaris-plugins/pom.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-api/pom.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/core/LimitAPI.java create mode 100644 polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaRequest.java create mode 100644 polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResponse.java create mode 100644 polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResultCode.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/pom.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/api/DefaultLimitAPI.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/AsyncRateLimitConnector.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/DurationBaseCallback.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/HostIdentifier.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/InitializeRecord.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitExtension.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindow.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindowSet.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/ServiceIdentifier.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/StreamCounterSet.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/WindowContainer.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/pojo/CommonQuotaRequest.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/sync/RemoteSyncTask.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/LimitValidator.java create mode 100644 polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/RateLimitConstants.java create mode 100644 polaris-ratelimit/polaris-ratelimit-examples/pom.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/RateLimitExample.java create mode 100644 polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/utils/LimitExampleUtils.java create mode 100644 polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/log4j2.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/polaris.yml create mode 100644 polaris-ratelimit/polaris-ratelimit-factory/pom.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-factory/src/main/java/com/tencent/polaris/ratelimit/factory/LimitAPIFactory.java create mode 100644 polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java create mode 100644 polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/LocalTest.java create mode 100644 polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/log4j2.xml create mode 100644 polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/polaris.yml create mode 100644 polaris-ratelimit/pom.xml create mode 100644 polaris-router/polaris-router-api/pom.xml create mode 100644 polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/core/RouterAPI.java create mode 100644 polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceRequest.java create mode 100644 polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceResponse.java create mode 100644 polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java create mode 100644 polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersResponse.java create mode 100644 polaris-router/polaris-router-client/pom.xml create mode 100644 polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/api/DefaultRouterAPI.java create mode 100644 polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/util/RouterValidator.java create mode 100644 polaris-router/polaris-router-examples/pom.xml create mode 100644 polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/ExampleUtils.java create mode 100644 polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/RouterExample.java create mode 100644 polaris-router/polaris-router-examples/src/main/resources/log4j2.xml create mode 100644 polaris-router/polaris-router-examples/src/main/resources/polaris.yml create mode 100644 polaris-router/polaris-router-factory/pom.xml create mode 100644 polaris-router/polaris-router-factory/src/main/java/com/tencent/polaris/factory/api/RouterAPIFactory.java create mode 100644 polaris-router/pom.xml create mode 100644 polaris-test/polaris-test-common/pom.xml create mode 100644 polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/Consts.java create mode 100644 polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/TestUtils.java create mode 100644 polaris-test/polaris-test-mock-discovery/pom.xml create mode 100644 polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/HeaderInterceptor.java create mode 100644 polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/LocationInfo.java create mode 100644 polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingServer.java create mode 100644 polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingService.java create mode 100644 polaris-test/pom.xml create mode 100644 pom.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ba517bb32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Eclipse project files +.project +.classpath +.settings + +# IntelliJ IDEA project files and directories +*.iml +*.ipr +*.iws +.idea/ + +# Build targets +**/target/* + +# Mac-specific directory that no other operating system needs. +.DS_Store + +# JVM crash logs +hs_err_pid*.log + +dependency-reduced-pom.xml + +*/.unison.* + +# exclude docker-sync stuff +.docker-sync +*/.docker-sync + +# exclude vscode files +.vscode/ +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.factorypath + +# Maven ignore +.flattened-pom.xml + +# misc +*classes +*.class +.svn +logs/ +lib/ +backup/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..b50fb7514 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,26 @@ +# Contributing +--- +If you have good comments or suggestions, welcome to create [Issues](https://github.com/Tencent/polaris-java/issues) or [Pull Requests](https://github.com/Tencent/polaris-java/pulls),contribute to the polaris open source community.TBase continues to recruit contributors, even if it is answering questions in the issue, or doing some simple bugfixes, it will be of great help to polaris-java. + +[Tencent Open Source Incentive Program](https://opensource.tencent.com/contribution) Encourage developers to participate and contribute, and look forward to your joining. + +## Issue +#### For contributors + +Please ensure that the following conditions are met before submitting an issue: + +* Must be a bug or new feature +* Have searched in the issue, and did not find a similar issue or solution +* When creating a new issue, please provide a detailed description, screenshot or short video to help us locate the problem + +## Pull Request +We welcome everyone to contribute code to make our product more powerful. The code team will monitor all pull requests, and we will do the corresponding code inspection and testing. After the test passes, we will accept the PR, but will not immediately merge into the master branch. + +Please confirm before completing a PR: + +1. Fork your own branch from the master branch. +2. Please modify the corresponding documents and comments after modifying the code. +3. Please add License and Copyright declarations in the newly created file. +4. Ensure a consistent code style. +5. Do adequate testing. +6. Then, you can submit your code to the dev branch. \ No newline at end of file diff --git a/Code-of-Conduct.md b/Code-of-Conduct.md new file mode 100644 index 000000000..1dd4b4da8 --- /dev/null +++ b/Code-of-Conduct.md @@ -0,0 +1,5 @@ +# TBase Community Code of Conduct +TBase follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). + + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the PolarisMesh Code of Conduct Committee via email: PolarisMesh_Community@qq.com \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..93c05ece0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,892 @@ +Tencent is pleased to support the open source community by making polaris-java available. + +Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved. + +A polaris-java is licensed under the BSD 3-Clause License. A copy of the BSD 3-Clause License is included in this file. + + +Terms of BSD 3-Clause License +--------------------------------------------------- +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Other dependencies and licenses: + + +Open Source Software Licensed under the BSD 2-Clause License: +-------------------------------------------------------------------- +1. errors +Copyright (c) 2015, Dave Cheney + +2. go-check +Copyright (c) 2010-2013 Gustavo Niemeyer + +Terms of the BSD 2-Clause License: +-------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Open Source Software Licensed under the BSD 3-Clause License: +-------------------------------------------------------------------- +1. protobuf +Copyright (c) 2013, The GoGo Authors. All rights reserved. + +2. protobuf +Copyright 2010 The Go Authors. All rights reserved. + +3. uuid +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +4. Go Networking +Copyright (c) 2009 The Go Authors. All rights reserved. + +5. Go Time +Copyright (c) 2009 The Go Authors. All rights reserved. + +6. Gonum Stat +Copyright ©2013 The Gonum Authors. All rights reserved. + +7. murmur3 +Copyright 2013, Sébastien Paolacci. +All rights reserved. + +8. protobuf +Copyright 2014, Google Inc. All rights reserved. + +The BSD 3-Clause license applies to all parts of Protocol Buffers except the following: + +- Atomicops support for generic gcc, located in + src/google/protobuf/stubs/atomicops_internals_generic_gcc.h. + This file is copyrighted by Red Hat Inc. + +- Atomicops support for AIX/POWER, located in + src/google/protobuf/stubs/atomicops_internals_power.h. + This file is copyrighted by Bloomberg Finance LP. + +9. re2 +Copyright (c) 2009 The RE2 Authors. All rights reserved. + +10. googletest +Copyright 2008, Google Inc. + + + +Terms of the BSD 3-Clause License: +-------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Open Source Software Licensed under the Permissive Type License: +-------------------------------------------------------------------- +1. GoConvey +Copyright (c) 2016 SmartyStreets, LLC + +Terms of the Permissive Type License: +-------------------------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +NOTE: Various optional and subordinate components carry their own licensing requirements and restrictions. Use of those components is subject to the terms and conditions outlined the respective license of each component. + + +Open Source Software Licensed under the Apache License Version 2.0: +-------------------------------------------------------------------- +1. gomock +Copyright (c) gomock authors and contributors + +2. Redigo +Copyright (c) Redigo authors and contributors + +3. Cobra +Copyright (c) Cobra authors and contributors + +4. YAML +Copyright (c) YAML authors and contributors + +5. reflect2 +Copyright (c) reflect2 authors and contributors + +6. gRPC-Go +Copyright (c) gRPC-Go authors and contributors + +7. gRPC-Java +Copyright (c) gRPC-Java authors and contributors + +8. Log4J +Copyright 1999-2005 The Apache Software Foundation + +9. benchmark +Copyright (c) benchmark authors and contributors. + +10. spring-boot +Copyright (c) spring-boot authors and contributors. + +11. spring-framework +Copyright (c) spring-framework authors and contributors. + +12. spring-cloud-netflix +Copyright (c) spring-cloud-netflix authors and contributors. + +13. spring-cloud config +Copyright (c) spring-cloud-config authors and contributors. + +14. guava +Copyright (c) guava authors and contributors. + +15. reactor +Copyright (c) reactor authors and contributors. + +16. powermock +Copyright 2007-2017 PowerMock Contributors + + +Terms of the Apache v2.0 License: +-------------------------------------------------------------------- +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + + +Open Source Software Licensed Under the MIT License: +The below software in this distribution may have been modified by THL A29 Limited +-------------------------------------------------------------------- +1. go-restful +Copyright (c) 2012,2013 Ernest Micklei + +2. mapstructure +Copyright (c) 2013 Mitchell Hashimoto + +3. lumberjack +Copyright (c) 2014 Nate Finch + +4. Testify +Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. + +5. zap +Copyright (c) 2016-2017 Uber Technologies, Inc. + +6. gomonkey +Copyright (c) 2018 Zhang Xiaolong + +7. go-homedir +Copyright (c) 2013 Mitchell Hashimoto + +8. SLF4J +Copyright (c) 2004-2017 QOS.ch + +9. nghttp2 +Copyright (c) 2012, 2014, 2015, 2016 Tatsuhiro Tsujikawa +Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors + +10. yaml-cpp +Copyright (c) 2008 Jesse Beder. + +11. murmurhash +Copyright murmurhash authors and contributors + +Terms of the MIT License: +--------------------------------------------------- +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Open Source Software Licensed under the Mozilla Public License, version 2.0 License: +-------------------------------------------------------------------- +1. Go-MySQL-Driver +Copyright (c) 2013, The GoGo Authors. All rights reserved. + +2. golang-lru +Copyright (c) golang-lru authors and contributors + +3. go-multierror +go-multierror (c) authors and contributors + +Terms of the Mozilla Public License, version 2.0 License: +--------------------------------------------------- +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. “Contributor” + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. “Contributor Version” + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor’s Contribution. + +1.3. “Contribution” + + means Covered Software of a particular Contributor. + +1.4. “Covered Software” + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. “Incompatible With Secondary Licenses” + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of version + 1.1 or earlier of the License, but not also under the terms of a + Secondary License. + +1.6. “Executable Form” + + means any form of the work other than Source Code Form. + +1.7. “Larger Work” + + means a work that combines Covered Software with other material, in a separate + file or files, that is not Covered Software. + +1.8. “License” + + means this document. + +1.9. “Licensable” + + means having the right to grant, to the maximum extent possible, whether at the + time of the initial grant or subsequently, any and all of the rights conveyed by + this License. + +1.10. “Modifications” + + means any of the following: + + a. any file in Source Code Form that results from an addition to, deletion + from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. “Patent Claims” of a Contributor + + means any patent claim(s), including without limitation, method, process, + and apparatus claims, in any patent Licensable by such Contributor that + would be infringed, but for the grant of the License, by the making, + using, selling, offering for sale, having made, import, or transfer of + either its Contributions or its Contributor Version. + +1.12. “Secondary License” + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. “Source Code Form” + + means the form of the work preferred for making modifications. + +1.14. “You” (or “Your”) + + means an individual or a legal entity exercising rights under this + License. For legal entities, “You” includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, “control” means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or as + part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its Contributions + or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution become + effective for each Contribution on the date the Contributor first distributes + such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under this + License. No additional rights or licenses will be implied from the distribution + or licensing of Covered Software under this License. Notwithstanding Section + 2.1(b) above, no patent license is granted by a Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party’s + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of its + Contributions. + + This License does not grant any rights in the trademarks, service marks, or + logos of any Contributor (except as may be necessary to comply with the + notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this License + (see Section 10.2) or under the terms of a Secondary License (if permitted + under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its Contributions + are its original creation(s) or it has sufficient rights to grant the + rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under applicable + copyright doctrines of fair use, fair dealing, or other equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under the + terms of this License. You must inform recipients that the Source Code Form + of the Covered Software is governed by the terms of this License, and how + they can obtain a copy of this License. You may not attempt to alter or + restrict the recipients’ rights in the Source Code Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this License, + or sublicense it under different terms, provided that the license for + the Executable Form does not attempt to limit or alter the recipients’ + rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for the + Covered Software. If the Larger Work is a combination of Covered Software + with a work governed by one or more Secondary Licenses, and the Covered + Software is not Incompatible With Secondary Licenses, this License permits + You to additionally distribute such Covered Software under the terms of + such Secondary License(s), so that the recipient of the Larger Work may, at + their option, further distribute the Covered Software under the terms of + either this License or such Secondary License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices (including + copyright notices, patent notices, disclaimers of warranty, or limitations + of liability) contained within the Source Code Form of the Covered + Software, except that You may alter any license notices to the extent + required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on behalf + of any Contributor. You must make it absolutely clear that any such + warranty, support, indemnity, or liability obligation is offered by You + alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, judicial + order, or regulation then You must: (a) comply with the terms of this License + to the maximum extent possible; and (b) describe the limitations and the code + they affect. Such description must be placed in a text file included with all + distributions of the Covered Software under this License. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing basis, + if such Contributor fails to notify You of the non-compliance by some + reasonable means prior to 60 days after You have come back into compliance. + Moreover, Your grants from a particular Contributor are reinstated on an + ongoing basis if such Contributor notifies You of the non-compliance by + some reasonable means, this is the first time You have received notice of + non-compliance with this License from such Contributor, and You become + compliant prior to 30 days after Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, counter-claims, + and cross-claims) alleging that a Contributor Version directly or + indirectly infringes any patent, then the rights granted to You by any and + all Contributors for the Covered Software under Section 2.1 of this License + shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an “as is” basis, without + warranty of any kind, either expressed, implied, or statutory, including, + without limitation, warranties that the Covered Software is free of defects, + merchantable, fit for a particular purpose or non-infringing. The entire + risk as to the quality and performance of the Covered Software is with You. + Should any Covered Software prove defective in any respect, You (not any + Contributor) assume the cost of any necessary servicing, repair, or + correction. This disclaimer of warranty constitutes an essential part of this + License. No use of any Covered Software is authorized under this License + except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from such + party’s negligence to the extent applicable law prohibits such limitation. + Some jurisdictions do not allow the exclusion or limitation of incidental or + consequential damages, so this exclusion and limitation may not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts of + a jurisdiction where the defendant maintains its principal place of business + and such litigation shall be governed by laws of that jurisdiction, without + reference to its conflict-of-law provisions. Nothing in this Section shall + prevent a party’s ability to bring cross-claims or counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject matter + hereof. If any provision of this License is held to be unenforceable, such + provision shall be reformed only to the extent necessary to make it + enforceable. Any law or regulation which provides that the language of a + contract shall be construed against the drafter shall not be used to construe + this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version of + the License under which You originally received the Covered Software, or + under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a modified + version of this License if you rename the license and remove any + references to the name of the license steward (except to note that such + modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses + If You choose to distribute Source Code Form that is Incompatible With + Secondary Licenses under the terms of this version of the License, the + notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, then +You may include the notice in a location (such as a LICENSE file in a relevant +directory) where a recipient would be likely to look for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - “Incompatible With Secondary Licenses” Notice + + This Source Code Form is “Incompatible + With Secondary Licenses”, as defined by + the Mozilla Public License, v. 2.0. + + +Open Source Software Licensed under the Eclipse Public License, version 1.0 License: +-------------------------------------------------------------------- +1. Junit4 +Copyright (c) junit4 authors and contributors + + +Terms of the Eclipse Public License, version 1.0 License: +--------------------------------------------------- +Eclipse Public License - v 1.0 + +THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC +LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM +CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + +1. DEFINITIONS + +"Contribution" means: + + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + + where such changes and/or additions to the Program originate from and are +distributed by that particular Contributor. A Contribution 'originates' from a +Contributor if it was added to the Program by such Contributor itself or anyone +acting on such Contributor's behalf. Contributions do not include additions to +the Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) are +not derivative works of the Program. + +"Contributor" means any person or entity that distributes the Program. + +"Licensed Patents " mean patent claims licensable by a Contributor which are +necessarily infringed by the use or sale of its Contribution alone or when +combined with the Program. + +"Program" means the Contributions distributed in accordance with this Agreement. + +"Recipient" means anyone who receives the Program under this Agreement, +including all Contributors. + +2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free copyright license to +reproduce, prepare derivative works of, publicly display, publicly perform, +distribute and sublicense the Contribution of such Contributor, if any, and +such derivative works, in source code and object code form. + + b) Subject to the terms of this Agreement, each Contributor hereby grants +Recipient a non-exclusive, worldwide, royalty-free patent license under +Licensed Patents to make, use, sell, offer to sell, import and otherwise +transfer the Contribution of such Contributor, if any, in source code and +object code form. This patent license shall apply to the combination of the +Contribution and the Program if, at the time the Contribution is added by the +Contributor, such addition of the Contribution causes such combination to be +covered by the Licensed Patents. The patent license shall not apply to any +other combinations which include the Contribution. No hardware per se is +licensed hereunder. + + c) Recipient understands that although each Contributor grants the +licenses to its Contributions set forth herein, no assurances are provided by +any Contributor that the Program does not infringe the patent or other +intellectual property rights of any other entity. Each Contributor disclaims +any liability to Recipient for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a condition to +exercising the rights and licenses granted hereunder, each Recipient hereby +assumes sole responsibility to secure any other intellectual property rights +needed, if any. For example, if a third party patent license is required to +allow Recipient to distribute the Program, it is Recipient's responsibility to +acquire that license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has sufficient +copyright rights in its Contribution, if any, to grant the copyright license +set forth in this Agreement. + +3. REQUIREMENTS + +A Contributor may choose to distribute the Program in object code form under +its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all warranties and +conditions, express and implied, including warranties or conditions of title +and non-infringement, and implied warranties or conditions of merchantability +and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all liability for +damages, including direct, indirect, special, incidental and consequential +damages, such as lost profits; + + iii) states that any provisions which differ from this Agreement are +offered by that Contributor alone and not by any other party; and + + iv) states that source code for the Program is available from such +Contributor, and informs licensees how to obtain it in a reasonable manner on +or through a medium customarily used for software exchange. + +When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of the +Program. + +Contributors may not remove or alter any copyright notices contained within the +Program. + +Each Contributor must identify itself as the originator of its Contribution, if +any, in a manner that reasonably allows subsequent Recipients to identify the +originator of the Contribution. + +4. COMMERCIAL DISTRIBUTION + +Commercial distributors of software may accept certain responsibilities with +respect to end users, business partners and the like. While this license is +intended to facilitate the commercial use of the Program, the Contributor who +includes the Program in a commercial product offering should do so in a manner +which does not create potential liability for other Contributors. Therefore, if +a Contributor includes the Program in a commercial product offering, such +Contributor ("Commercial Contributor") hereby agrees to defend and indemnify +every other Contributor ("Indemnified Contributor") against any losses, damages +and costs (collectively "Losses") arising from claims, lawsuits and other legal +actions brought by a third party against the Indemnified Contributor to the +extent caused by the acts or omissions of such Commercial Contributor in +connection with its distribution of the Program in a commercial product +offering. The obligations in this section do not apply to any claims or Losses +relating to any actual or alleged intellectual property infringement. In order +to qualify, an Indemnified Contributor must: a) promptly notify the Commercial +Contributor in writing of such claim, and b) allow the Commercial Contributor +to control, and cooperate with the Commercial Contributor in, the defense and +any related settlement negotiations. The Indemnified Contributor may +participate in any such claim at its own expense. + +For example, a Contributor might include the Program in a commercial product +offering, Product X. That Contributor is then a Commercial Contributor. If that +Commercial Contributor then makes performance claims, or offers warranties +related to Product X, those performance claims and warranties are such +Commercial Contributor's responsibility alone. Under this section, the +Commercial Contributor would have to defend claims against the other +Contributors related to those performance claims and warranties, and if a court +requires any other Contributor to pay any damages as a result, the Commercial +Contributor must pay those damages. + +5. NO WARRANTY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR +IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each +Recipient is solely responsible for determining the appropriateness of using +and distributing the Program and assumes all risks associated with its exercise +of rights under this Agreement, including but not limited to the risks and +costs of program errors, compliance with applicable laws, damage to or loss of +data, programs or equipment, and unavailability or interruption of operations. + +6. DISCLAIMER OF LIABILITY + +EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY +CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST +PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS +GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. GENERAL + +If any provision of this Agreement is invalid or unenforceable under applicable +law, it shall not affect the validity or enforceability of the remainder of the +terms of this Agreement, and without further action by the parties hereto, such +provision shall be reformed to the minimum extent necessary to make such +provision valid and enforceable. + +If Recipient institutes patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other software or +hardware) infringes such Recipient's patent(s), then such Recipient's rights +granted under Section 2(b) shall terminate as of the date such litigation is +filed. + +All Recipient's rights under this Agreement shall terminate if it fails to +comply with any of the material terms or conditions of this Agreement and does +not cure such failure in a reasonable period of time after becoming aware of +such noncompliance. If all Recipient's rights under this Agreement terminate, +Recipient agrees to cease use and distribution of the Program as soon as +reasonably practicable. However, Recipient's obligations under this Agreement +and any licenses granted by Recipient relating to the Program shall continue +and survive. + +Everyone is permitted to copy and distribute copies of this Agreement, but in +order to avoid inconsistency the Agreement is copyrighted and may only be +modified in the following manner. The Agreement Steward reserves the right to +publish new versions (including revisions) of this Agreement from time to time. +No one other than the Agreement Steward has the right to modify this Agreement. +The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to +serve as the Agreement Steward to a suitable separate entity. Each new version +of the Agreement will be given a distinguishing version number. The Program +(including Contributions) may always be distributed subject to the version of +the Agreement under which it was received. In addition, after a new version of +the Agreement is published, Contributor may elect to distribute the Program +(including its Contributions) under the new version. Except as expressly stated +in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to +the intellectual property of any Contributor under this Agreement, whether +expressly, by implication, estoppel or otherwise. All rights in the Program not +expressly granted under this Agreement are reserved. + +This Agreement is governed by the laws of the State of New York and the +intellectual property laws of the United States of America. No party to this +Agreement will bring a legal action under this Agreement more than one year +after the cause of action arose. Each party waives its rights to a jury trial +in any resulting litigation. \ No newline at end of file diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 000000000..3371c4c3e --- /dev/null +++ b/README-zh.md @@ -0,0 +1,60 @@ +polaris-java +======================================== +北极星polaris是一个支持多种开发语言、兼容主流开发框架的服务治理中心。polaris-java是北极星的Java语言嵌入式服务治理SDK + +## 快速入门 + +### 包依赖 + +#### 大前提:依赖管理 +在工程根目录的pom中的添加如下配置,即可在项目中引用需要的polaris-java子模块依赖。 +```xml + + + + com.tencent.nameservice + polaris-dependencies + ${version} + pom + import + + + +``` +#### 使用全量功能 + ```xml + + com.tencent.nameservice + polaris-factory + + ``` +#### 仅服务注册发现 + ```xml + + com.tencent.nameservice + polaris-discovery-factory + + ``` +#### 仅使用熔断降级 + ```xml + + com.tencent.nameservice + polaris-circuitbreaker-factory + + ``` +#### 仅使用服务限流 + ```xml + + com.tencent.nameservice + polaris-ratelimit-factory + + ``` + +### 功能使用 + +各组件功能以及接口使用方式可参考对外开源文档:[使用polaris-java](https://github.com/PolarisMesh/website/blob/main/docs/zh/doc/%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/%E4%BD%BF%E7%94%A8polaris-java.md) + +## License + +The polaris-java is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE) + diff --git a/README.md b/README.md index cf0e4ba8e..25f702278 100644 --- a/README.md +++ b/README.md @@ -1 +1,60 @@ -# polaris-java +polaris-java +======================================== +Polaris is an operation centre that supports multiple programming languages, with high compatibility to different application framework. Polaris - java is Polaris's Java embedded operation SDK. + +## Quick Start + +### Package Dependencies + +#### PreCondition: DependencyManagement +Add the below script into the root pom element , after that you can refer all the polaris dependency freely. +```xml + + + + com.tencent.nameservice + polaris-dependencies + ${version} + pom + import + + + +``` +#### Use all functions + ```xml + + com.tencent.nameservice + polaris-factory + + ``` +#### Use discovery only + ```xml + + com.tencent.nameservice + polaris-discovery-factory + + ``` +#### Use circuitbreak and degrade only + ```xml + + com.tencent.nameservice + polaris-circuitbreaker-factory + + ``` +#### Use ratelimit + ```xml + + com.tencent.nameservice + polaris-ratelimit-factory + + ``` + +### User manual + +You can find detail in:[using polaris-java](https://github.com/PolarisMesh/website/blob/main/docs/zh/doc/%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8/%E4%BD%BF%E7%94%A8polaris-java.md) + +## License + +The polaris-java is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE) + diff --git a/checkstyle/checkstyle.xml b/checkstyle/checkstyle.xml new file mode 100644 index 000000000..df892b31a --- /dev/null +++ b/checkstyle/checkstyle.xml @@ -0,0 +1,560 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/checkstyle/suppressions.xml b/checkstyle/suppressions.xml new file mode 100644 index 000000000..9c7fcb8eb --- /dev/null +++ b/checkstyle/suppressions.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-api/pom.xml b/polaris-circuitbreaker/polaris-circuitbreaker-api/pom.xml new file mode 100644 index 000000000..3b29a83f7 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-api/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-api + + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-api/src/main/java/com/tencent/polaris/circuitbreak/api/CircuitBreakAPI.java b/polaris-circuitbreaker/polaris-circuitbreaker-api/src/main/java/com/tencent/polaris/circuitbreak/api/CircuitBreakAPI.java new file mode 100644 index 000000000..9374a0ba4 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-api/src/main/java/com/tencent/polaris/circuitbreak/api/CircuitBreakAPI.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.api; + +public interface CircuitBreakAPI { + +// /** +// * 检查服务的熔断器状态 +// * +// * @return 假如一个服务下所有的实例或者实例分组都被熔断,则状态为OPEN;存在半开的实例或者分组,状态为HALF_OPEN,否则状态为CLOSE +// */ +// Status checkServiceStatus(); +// +// /** +// * 检查服务接口的熔断器状态 +// * +// * @return 假如一个接口下所有的实例或者实例分组都被熔断,则状态为OPEN;存在半开的实例或者分组,状态为HALF_OPEN,否则状态为CLOSE +// */ +// Status checkMethodStatus(); +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/pom.xml b/polaris-circuitbreaker/polaris-circuitbreaker-client/pom.xml new file mode 100644 index 000000000..8aa861272 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-client + + + + com.tencent.nameservice + polaris-client + ${project.version} + + + com.tencent.nameservice + polaris-circuitbreaker-api + ${project.version} + + + \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/DefaultCircuitBreakAPI.java b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/DefaultCircuitBreakAPI.java new file mode 100644 index 000000000..615083e40 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/DefaultCircuitBreakAPI.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.client.api; + +import com.tencent.polaris.api.config.consumer.CircuitBreakerConfig; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.client.api.BaseEngine; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.api.ServiceCallResultListener; +import java.util.List; + +public class DefaultCircuitBreakAPI extends BaseEngine implements CircuitBreakAPI { + + private ServiceCallResultChecker checker; + + public DefaultCircuitBreakAPI(SDKContext sdkContext) { + super(sdkContext); + } + + @Override + protected void subInit() { + CircuitBreakerConfig cbConfig = sdkContext.getConfig().getConsumer().getCircuitBreaker(); + if (!cbConfig.isEnable()) { + return; + } + List serviceCallResultListeners = ServiceCallResultListener + .getServiceCallResultListeners(sdkContext); + for (ServiceCallResultListener listener : serviceCallResultListeners) { + if (listener instanceof ServiceCallResultChecker) { + checker = (ServiceCallResultChecker) listener; + break; + } + } + } + + @Override + protected void doDestroy() { + if (null != checker) { + checker.destroy(); + } + super.doDestroy(); + } +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/ServiceCallResultChecker.java b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/ServiceCallResultChecker.java new file mode 100644 index 000000000..11a5ad6fc --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/api/ServiceCallResultChecker.java @@ -0,0 +1,158 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.client.api; + +import com.tencent.polaris.api.config.consumer.CircuitBreakerConfig; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig.When; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.circuitbreak.client.task.InstancesCircuitBreakTask; +import com.tencent.polaris.circuitbreak.client.task.InstancesDetectTask; +import com.tencent.polaris.circuitbreak.client.task.PriorityTaskScheduler; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.api.ServiceCallResultListener; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 数据上报时用于检查下是否已经熔断 + */ +public class ServiceCallResultChecker implements ServiceCallResultListener { + + private PriorityTaskScheduler priorityTaskScheduler; + + private ScheduledExecutorService cbTaskExecutors; + + private ScheduledExecutorService detectTaskExecutors; + + private Extensions extensions; + + private InstancesDetectTask detectTask; + + private final AtomicInteger state = new AtomicInteger(0); + + @Override + public synchronized void init(SDKContext sdkContext) { + if (!state.compareAndSet(0, 1)) { + return; + } + extensions = sdkContext.getExtensions(); + CircuitBreakerConfig cbConfig = sdkContext.getConfig().getConsumer().getCircuitBreaker(); + if (cbConfig.isEnable()) { + priorityTaskScheduler = new PriorityTaskScheduler(); + cbTaskExecutors = Executors.newSingleThreadScheduledExecutor(); + CheckServicesCircuitBreak checker = new CheckServicesCircuitBreak( + sdkContext.getExtensions(), priorityTaskScheduler); + long checkPeriodMs = cbConfig.getCheckPeriod(); + cbTaskExecutors.scheduleAtFixedRate( + checker, checkPeriodMs, checkPeriodMs / 2, TimeUnit.MILLISECONDS); + } + OutlierDetectionConfig outlierDetection = sdkContext.getConfig().getConsumer().getOutlierDetection(); + if (outlierDetection.getWhen() != When.never) { + detectTaskExecutors = Executors.newSingleThreadScheduledExecutor(); + long checkPeriodMs = outlierDetection.getCheckPeriod(); + detectTask = new InstancesDetectTask(extensions); + detectTaskExecutors.scheduleAtFixedRate(detectTask, checkPeriodMs, checkPeriodMs, TimeUnit.MILLISECONDS); + } + } + + @Override + public void onServiceCallResult(InstanceGauge result) { + if (null == priorityTaskScheduler) { + return; + } + if (CollectionUtils.isEmpty(extensions.getCircuitBreakers())) { + return; + } + InstancesCircuitBreakTask rtTask = null; + for (CircuitBreaker circuitBreaker : extensions.getCircuitBreakers()) { + String cbName = circuitBreaker.getName(); + boolean rtLimit = circuitBreaker.stat(result); + String instId = result.getInstanceId(); + if (rtLimit && StringUtils.isNotEmpty(instId)) { + ServiceKey svcKey = new ServiceKey(result.getNamespace(), result.getService()); + rtTask = new InstancesCircuitBreakTask( + svcKey, cbName, null, instId, extensions, InstancesCircuitBreakTask.TaskPriority.HIGH); + break; + } + } + if (null == rtTask) { + return; + } + priorityTaskScheduler.addCircuitBreakTask(rtTask); + } + + @Override + public synchronized void destroy() { + if (!state.compareAndSet(1, 0)) { + return; + } + if (null != priorityTaskScheduler) { + priorityTaskScheduler.destroy(); + } + if (null != cbTaskExecutors) { + cbTaskExecutors.shutdown(); + } + if (null != detectTask) { + detectTask.destroy(); + } + if (null != detectTaskExecutors) { + detectTaskExecutors.shutdown(); + } + } + + private static class CheckServicesCircuitBreak implements Runnable { + + private final Extensions extensions; + + private final PriorityTaskScheduler priorityTaskScheduler; + + public CheckServicesCircuitBreak(Extensions extensions, PriorityTaskScheduler priorityTaskScheduler) { + this.extensions = extensions; + this.priorityTaskScheduler = priorityTaskScheduler; + } + + @Override + public void run() { + Set services = extensions.getLocalRegistry().getServices(); + for (ServiceKey service : services) { + ServiceEventKey svcEventKey = new ServiceEventKey(service, EventType.INSTANCE); + ServiceInstances svcInstances = extensions.getLocalRegistry() + .getInstances(new ResourceFilter(svcEventKey, true, true)); + if (!svcInstances.isInitialized() || svcInstances.getInstances().isEmpty()) { + continue; + } + priorityTaskScheduler.addCircuitBreakTask( + new InstancesCircuitBreakTask(service, "", svcInstances.getInstances(), "", + extensions, InstancesCircuitBreakTask.TaskPriority.LOW)); + } + } + } +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesCircuitBreakTask.java b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesCircuitBreakTask.java new file mode 100644 index 000000000..1c16b4035 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesCircuitBreakTask.java @@ -0,0 +1,188 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.client.task; + +import static com.tencent.polaris.api.plugin.registry.InstanceProperty.PROPERTY_CIRCUIT_BREAKER_STATUS; + +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreakResult; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreakResult.ResultKey; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.registry.InstanceProperty; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.plugin.registry.ServiceUpdateRequest; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.pojo.ServiceInstancesByProto; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 熔断单个实例的任务 + */ +public class InstancesCircuitBreakTask implements Runnable, Comparable { + + @Override + public int compareTo(InstancesCircuitBreakTask o) { + return this.priority.ordinal() - o.priority.ordinal(); + } + + /** + * 任务优先级 + */ + public enum TaskPriority { + HIGH, + LOW, + } + + private static final Logger LOG = LoggerFactory.getLogger(InstancesCircuitBreakTask.class); + + private final ServiceKey serviceKey; + + private final String cbName; + + private final Collection instances; + + private final String instId; + + private final Extensions extensions; + + private final TaskPriority priority; + + public InstancesCircuitBreakTask(ServiceKey serviceKey, String cbName, Collection instances, + String instId, Extensions extensions, TaskPriority priority) { + this.serviceKey = serviceKey; + this.cbName = cbName; + this.instId = instId; + this.instances = instances; + this.extensions = extensions; + this.priority = priority; + } + + private Instance getInstance() { + ServiceEventKey serviceEventKey = new ServiceEventKey(serviceKey, EventType.INSTANCE); + ResourceFilter resourceFilter = new ResourceFilter(serviceEventKey, true, true); + ServiceInstancesByProto instances = (ServiceInstancesByProto) extensions.getLocalRegistry() + .getInstances(resourceFilter); + if (!instances.isInitialized()) { + return null; + } + return instances.getInstance(instId); + } + + @Override + public void run() { + Map allResults = new HashMap<>(); + Set statusChangedInstances = new HashSet<>(); + Collection targetInstances = instances; + if (StringUtils.isNotEmpty(instId)) { + Instance instance = getInstance(); + if (null != instance) { + targetInstances = new ArrayList<>(); + targetInstances.add(instance); + } + } + if (CollectionUtils.isEmpty(targetInstances)) { + return; + } + for (CircuitBreaker circuitBreaker : extensions.getCircuitBreakers()) { + if (StringUtils.isNotBlank(cbName) && !cbName.equals(circuitBreaker.getName())) { + continue; + } + CircuitBreakResult circuitBreakResult = circuitBreaker.checkInstance(targetInstances); + if (null == circuitBreakResult || circuitBreakResult.isEmptyResult()) { + continue; + } + cleanInstanceSet(circuitBreakResult.getInstancesToOpen(), statusChangedInstances); + cleanInstanceSet(circuitBreakResult.getInstancesToHalfOpen(), statusChangedInstances); + cleanInstanceSet(circuitBreakResult.getInstancesToClose(), statusChangedInstances); + allResults.put(circuitBreaker.getName(), circuitBreakResult); + } + ServiceUpdateRequest updateRequest = buildServiceUpdateRequest(serviceKey, allResults); + if (CollectionUtils.isEmpty(updateRequest.getProperties())) { + return; + } + LOG.info("update cache for circuitbreaker, value is {}", updateRequest); + extensions.getLocalRegistry().updateInstances(updateRequest); + } + + private void cleanInstanceSet(Map instanceSet, Set allInstances) { + Set instIdsToRemove = new HashSet<>(); + for (Map.Entry entry : instanceSet.entrySet()) { + ResultKey resultKey = entry.getKey(); + if (allInstances.contains(resultKey)) { + instIdsToRemove.add(resultKey); + } else { + allInstances.add(resultKey); + } + } + instIdsToRemove.forEach(instanceSet::remove); + } + + @SuppressWarnings("unchecked") + private void buildInstanceProperty(long now, Map results, int maxRequestAfterHalfOpen, + Map instanceProperties, String cbName, CircuitBreakerStatus.Status status) { + if (MapUtils.isEmpty(results)) { + return; + } + for (Map.Entry entry : results.entrySet()) { + ResultKey resultKey = entry.getKey(); + Instance instance = entry.getValue(); + String instId = resultKey.getInstId(); + InstanceProperty instanceProperty = instanceProperties.get(instId); + if (null == instanceProperty) { + Map properties = new HashMap<>(); + properties.put(PROPERTY_CIRCUIT_BREAKER_STATUS, new HashMap()); + instanceProperty = new InstanceProperty(instance, properties); + instanceProperties.put(instId, instanceProperty); + } + Map statusMap = (Map) + instanceProperty.getProperties().get(PROPERTY_CIRCUIT_BREAKER_STATUS); + statusMap.put(resultKey.getStatusDimension(), + new CircuitBreakerStatus(cbName, status, now, maxRequestAfterHalfOpen)); + } + } + + private ServiceUpdateRequest buildServiceUpdateRequest( + ServiceKey serviceKey, Map allResults) { + Map properties = new HashMap<>(); + allResults.forEach((cbName, result) -> { + buildInstanceProperty(result.getCreateTimeMs(), result.getInstancesToOpen(), + result.getMaxRequestCountAfterHalfOpen(), properties, cbName, CircuitBreakerStatus.Status.OPEN); + buildInstanceProperty(result.getCreateTimeMs(), result.getInstancesToHalfOpen(), + result.getMaxRequestCountAfterHalfOpen(), properties, cbName, + CircuitBreakerStatus.Status.HALF_OPEN); + buildInstanceProperty(result.getCreateTimeMs(), result.getInstancesToClose(), + result.getMaxRequestCountAfterHalfOpen(), properties, cbName, CircuitBreakerStatus.Status.CLOSE); + }); + return new ServiceUpdateRequest(serviceKey, properties.values()); + } +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesDetectTask.java b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesDetectTask.java new file mode 100644 index 000000000..104870868 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/InstancesDetectTask.java @@ -0,0 +1,134 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.client.task; + +import static com.tencent.polaris.api.plugin.registry.InstanceProperty.PROPERTY_OUTLIER_DETECTOR_STATUS; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.detect.OutlierDetector; +import com.tencent.polaris.api.plugin.registry.InstanceProperty; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.plugin.registry.ServiceUpdateRequest; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.OutlierDetectionStatus; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.MapUtils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class InstancesDetectTask implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(InstancesDetectTask.class); + + private final Extensions extensions; + + private final AtomicBoolean destroy = new AtomicBoolean(false); + + public InstancesDetectTask(Extensions extensions) { + this.extensions = extensions; + } + + @Override + public void run() { + Set services = extensions.getLocalRegistry().getServices(); + for (ServiceKey serviceKey : services) { + try { + doInstanceDetectForService(serviceKey); + } catch (PolarisException e) { + LOG.error("fail to do instance detect for {}, e:{}", serviceKey, e); + } + } + } + + /** + * 注销任务 + */ + public void destroy() { + destroy.set(true); + } + + private void doInstanceDetectForService(ServiceKey serviceKey) throws PolarisException { + ServiceEventKey svcEventKey = new ServiceEventKey(serviceKey, EventType.INSTANCE); + ServiceInstances instances = extensions.getLocalRegistry().getInstances( + new ResourceFilter(svcEventKey, true, true)); + if (!instances.isInitialized() || instances.getInstances().size() == 0) { + return; + } + + Map aliveResults = new HashMap<>(); + for (Instance instance : instances.getInstances()) { + if (destroy.get()) { + // 如果要停止定时任务,则剩下的实例探测也没必要进行下去了 + break; + } + DetectResult result = detectInstance(instance); + if (result == null || result.getRetStatus() != RetStatus.RetSuccess) { + continue; + } + aliveResults.put(instance, result); + + } + if (MapUtils.isNotEmpty(aliveResults)) { + ServiceUpdateRequest updateRequest = buildInstanceUpdateResult(serviceKey, aliveResults); + LOG.info("update cache for outlier detect, value is {}", updateRequest); + extensions.getLocalRegistry().updateInstances(updateRequest); + + } + } + + private ServiceUpdateRequest buildInstanceUpdateResult(ServiceKey serviceKey, + Map aliveResults) { + List instances = new ArrayList<>(); + for (Map.Entry entry : aliveResults.entrySet()) { + Map properties = new HashMap<>(); + + properties.put(PROPERTY_OUTLIER_DETECTOR_STATUS, + new OutlierDetectionStatus(entry.getValue().getDetectType(), OutlierDetectionStatus.Status.HEALTHY, + entry.getValue().getLastDetectTime().getTime())); + InstanceProperty instanceProperty = new InstanceProperty(entry.getKey(), properties); + instances.add(instanceProperty); + } + + return new ServiceUpdateRequest(serviceKey, instances); + } + + private DetectResult detectInstance(Instance instance) throws PolarisException { + for (OutlierDetector detector : extensions.getOutlierDetectors()) { + DetectResult result = detector.detectInstance(instance); + if (result == null) { + continue; + } + if (result.getRetStatus() == RetStatus.RetSuccess) { + return result; + } + } + return null; + } +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/PriorityTaskScheduler.java b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/PriorityTaskScheduler.java new file mode 100644 index 000000000..acf9fc735 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/java/com/tencent/polaris/circuitbreak/client/task/PriorityTaskScheduler.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.client.task; + +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * 优先级队列调度器 + */ +public class PriorityTaskScheduler { + + private final ExecutorService priorityJobScheduler; + + public PriorityTaskScheduler() { + priorityJobScheduler = new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new PriorityBlockingQueue<>()); + } + + public void addCircuitBreakTask(InstancesCircuitBreakTask task) { + priorityJobScheduler.execute(task); + } + + public void destroy() { + ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{priorityJobScheduler}); + } + +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener new file mode 100644 index 000000000..9c5201a75 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-client/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener @@ -0,0 +1 @@ +com.tencent.polaris.circuitbreak.client.api.ServiceCallResultChecker \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-examples/pom.xml b/polaris-circuitbreaker/polaris-circuitbreaker-examples/pom.xml new file mode 100644 index 000000000..d220de9ed --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-examples/pom.xml @@ -0,0 +1,58 @@ + + + + polaris-circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-examples + + + + com.tencent.nameservice + polaris-client + ${project.version} + + + com.tencent.nameservice + polaris-discovery-factory + ${project.version} + + + com.tencent.nameservice + polaris-circuitbreaker-factory + ${project.version} + + + commons-cli + commons-cli + 1.4 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExample.java b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExample.java new file mode 100644 index 000000000..9b0051d77 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExample.java @@ -0,0 +1,102 @@ +package com.tencent.polaris.circuitbreak.examples; + +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterResponse; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.circuitbreak.examples.CircuitBreakExampleUtils.InitResult; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +/** + * Example for how to use polaris circuit breaking + * + * @author liaochuntao + */ +public class CircuitBreakExample { + + public static void main(String[] args) throws Throwable { + InitResult initResult = CircuitBreakExampleUtils.initConfiguration(args); + //由于需要用到多个API对象,因此可以使用SDKContext使得多个API对象的资源都可以共享 + try (SDKContext sdkContext = SDKContext.initContext()) { + ProviderAPI providerAPI = DiscoveryAPIFactory.createProviderAPIByContext(sdkContext); + //1. 先注册服务实例 + registerInstances(providerAPI, initResult); + //2. 获取可用的服务实例列表 + ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext); + Instance[] availableInstances = getAvailableInstances(consumerAPI, initResult); + System.out.println("available instances count is " + availableInstances.length); + //3. 针对某个服务实例上报调用结果失败,默认连续10次失败,或者1分钟错误率50%,就会对实例进行熔断 + Instance targetInstance = availableInstances[0]; + System.out.printf("target instance is %s:%d%n", targetInstance.getHost(), targetInstance.getPort()); + for (int i = 0; i < 120; i++) { + //这里要进行服务调用,用户可以写自己的服务调用代码 + for (Instance instance : availableInstances) { + ServiceCallResult serviceCallResult = new ServiceCallResult(); + serviceCallResult.setInstance(targetInstance); + if (instance.getHost().equals(targetInstance.getHost()) && instance.getPort() == targetInstance + .getPort()) { + serviceCallResult.setRetStatus(RetStatus.RetFail); + serviceCallResult.setDelay(2000); + } else { + serviceCallResult.setRetStatus(RetStatus.RetSuccess); + serviceCallResult.setDelay(20); + } + consumerAPI.updateServiceCallResult(serviceCallResult); + } + System.out.printf("report call result %d%n", i); + CircuitBreakExampleUtils.doSleep(500); + } + //4. 判断服务实例是否还在可用列表中,已经熔断的实例是不会再次返回 + availableInstances = getAvailableInstances(consumerAPI, initResult); + System.out.println("available instances count is " + availableInstances.length); + for (Instance instance : availableInstances) { + System.out.printf("available instance is %s:%d%n", instance.getHost(), instance.getPort()); + } + //4. 反注册服务实例 + System.out.println("start to deregister instances"); + deregisterInstances(providerAPI, initResult); + } + } + + private static Instance[] getAvailableInstances(ConsumerAPI consumerAPI, InitResult initResult) { + GetInstancesRequest getInstancesRequest = new GetInstancesRequest(); + getInstancesRequest.setNamespace(initResult.getNamespace()); + getInstancesRequest.setService(initResult.getService()); + InstancesResponse instances = consumerAPI.getInstances(getInstancesRequest); + return instances.getInstances(); + } + + private static void registerInstances(ProviderAPI providerAPI, InitResult initResult) { + for (int i = 0; i < initResult.getInstanceCount(); i++) { + InstanceRegisterRequest instanceRegisterRequest = new InstanceRegisterRequest(); + instanceRegisterRequest.setNamespace(initResult.getNamespace()); + instanceRegisterRequest.setService(initResult.getService()); + instanceRegisterRequest.setHost("127.0.0.1"); + instanceRegisterRequest.setPort(13000 + i); + instanceRegisterRequest.setToken(initResult.getToken()); + InstanceRegisterResponse instanceRegisterResponse = providerAPI.register(instanceRegisterRequest); + System.out.println("response after register is " + instanceRegisterResponse); + } + CircuitBreakExampleUtils.doSleep(5000); + } + + private static void deregisterInstances(ProviderAPI providerAPI, InitResult initResult) { + for (int i = 0; i < initResult.getInstanceCount(); i++) { + InstanceDeregisterRequest instanceDeregisterRequest = new InstanceDeregisterRequest(); + instanceDeregisterRequest.setNamespace(initResult.getNamespace()); + instanceDeregisterRequest.setService(initResult.getService()); + instanceDeregisterRequest.setHost("127.0.0.1"); + instanceDeregisterRequest.setPort(13000 + i); + instanceDeregisterRequest.setToken(initResult.getToken()); + providerAPI.deRegister(instanceDeregisterRequest); + } + } + +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExampleUtils.java b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExampleUtils.java new file mode 100644 index 000000000..edf78172a --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/java/com/tencent/polaris/circuitbreak/examples/CircuitBreakExampleUtils.java @@ -0,0 +1,94 @@ +package com.tencent.polaris.circuitbreak.examples; + +import com.tencent.polaris.api.utils.StringUtils; +import java.util.concurrent.TimeUnit; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +/** + * Simple basic operation encapsulation. + * + * @author liaochuntao + */ +public class CircuitBreakExampleUtils { + + public static class InitResult { + + private final String namespace; + private final String service; + private final int instanceCount; + private final String token; + + public InitResult(String namespace, String service, int instanceCount, String token) { + this.namespace = namespace; + this.service = service; + this.instanceCount = instanceCount; + this.token = token; + } + + public String getNamespace() { + return namespace; + } + + public String getService() { + return service; + } + + public int getInstanceCount() { + return instanceCount; + } + + public String getToken() { + return token; + } + } + + public static void doSleep(long mill) { + try { + TimeUnit.MILLISECONDS.sleep(mill); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * 初始化配置对象 + * + * @param args 命名行参数 + * @return 配置对象 + * @throws ParseException 解析异常 + */ + public static InitResult initConfiguration(String[] args) throws ParseException { + CommandLineParser parser = new DefaultParser(); + Options options = new Options(); + options.addOption("namespace", "service namespace", true, "namespace for service"); + options.addOption("service", "service name", true, "service name"); + options.addOption("instCount", "instance count", true, "instance count"); + options.addOption("token", "token", true, "token"); + + CommandLine commandLine = parser.parse(options, args); + String namespace = commandLine.getOptionValue("namespace"); + String service = commandLine.getOptionValue("service"); + String token = commandLine.getOptionValue("token"); + String instCountStr = commandLine.getOptionValue("instCount"); + int instCount; + if (StringUtils.isNotBlank(instCountStr)) { + try { + instCount = Integer.parseInt(instCountStr); + } catch (NumberFormatException e) { + e.printStackTrace(); + instCount = 1; + } + } else { + instCount = 1; + } + if (StringUtils.isBlank(namespace) || StringUtils.isBlank(service)) { + System.out.println("namespace or service is required"); + System.exit(1); + } + return new InitResult(namespace, service, instCount, token); + } +} diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/log4j2.xml b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/log4j2.xml new file mode 100644 index 000000000..4eab2d955 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/polaris.yml b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/polaris.yml new file mode 100644 index 000000000..ce1db469a --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-examples/src/main/resources/polaris.yml @@ -0,0 +1,6 @@ +global: + # configuration for connecting the polaris server + serverConnector: + # target server address + addresses: + - 127.0.0.1:8091 \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-factory/pom.xml b/polaris-circuitbreaker/polaris-circuitbreaker-factory/pom.xml new file mode 100644 index 000000000..acad640be --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-factory/pom.xml @@ -0,0 +1,103 @@ + + + + polaris-circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-factory + + + + com.tencent.nameservice + polaris-circuitbreaker-client + ${project.version} + + + com.tencent.nameservice + polaris-client + ${project.version} + + + + com.tencent.nameservice + connector-polaris-grpc + ${project.version} + + + + com.tencent.nameservice + resource-cache-memory + ${project.version} + + + + com.tencent.nameservice + flow-cache-expired + ${project.version} + + + + com.tencent.nameservice + router-isolated + ${project.version} + + + com.tencent.nameservice + router-healthy + ${project.version} + + + com.tencent.nameservice + router-nearby + ${project.version} + + + com.tencent.nameservice + router-metadata + ${project.version} + + + + com.tencent.nameservice + loadbalancer-random + ${project.version} + + + + com.tencent.nameservice + circuitbreaker-errrate + ${project.version} + + + com.tencent.nameservice + circuitbreaker-errcount + ${project.version} + + + + + com.tencent.nameservice + polaris-test-common + ${project.version} + test + + + com.tencent.nameservice + polaris-test-mock-discovery + ${project.version} + test + + + com.tencent.nameservice + polaris-discovery-factory + ${project.version} + test + + + \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/main/java/com/tencent/polaris/circuitbreak/factory/CircuitBreakAPIFactory.java b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/main/java/com/tencent/polaris/circuitbreak/factory/CircuitBreakAPIFactory.java new file mode 100644 index 000000000..174a31a7d --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/main/java/com/tencent/polaris/circuitbreak/factory/CircuitBreakAPIFactory.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreak.factory; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.circuitbreak.api.CircuitBreakAPI; +import com.tencent.polaris.circuitbreak.client.api.DefaultCircuitBreakAPI; +import com.tencent.polaris.client.api.SDKContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CircuitBreakAPIFactory { + + private static final Logger LOG = LoggerFactory.getLogger(CircuitBreakAPIFactory.class); + + /** + * 创建服务熔断的API对象 + * + * @param sdkContext SDK上下文信息 + * @return 熔断API + * @throws PolarisException 校验失败 + */ + public static CircuitBreakAPI createLimitAPIByContext(SDKContext sdkContext) throws PolarisException { + DefaultCircuitBreakAPI defaultCircuitBreakAPI = new DefaultCircuitBreakAPI(sdkContext); + defaultCircuitBreakAPI.init(); + return defaultCircuitBreakAPI; + } +} \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/java/com/tencent/polaris/circuitbreaker/factory/test/CircuitBreakerTest.java b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/java/com/tencent/polaris/circuitbreaker/factory/test/CircuitBreakerTest.java new file mode 100644 index 000000000..b2c3d5381 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/java/com/tencent/polaris/circuitbreaker/factory/test/CircuitBreakerTest.java @@ -0,0 +1,228 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.circuitbreaker.factory.test; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_CIRCUIT_BREAKER; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import java.io.IOException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * CircuitBreakerTest.java + * + * @author andrewshan + * @date 2019/8/30 + */ +public class CircuitBreakerTest { + + private static final Logger LOG = LoggerFactory.getLogger(CircuitBreakerTest.class); + + private static final int MAX_COUNT = 10; + + private NamingServer namingServer; + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, SERVICE_CIRCUIT_BREAKER); + InstanceParameter parameter = new InstanceParameter(); + parameter.setHealthy(true); + parameter.setIsolated(false); + parameter.setWeight(100); + namingServer.getNamingService().batchAddInstances(serviceKey, 10010, MAX_COUNT, parameter); + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + private ServiceCallResult instanceToResult(Instance instance) { + ServiceCallResult result = new ServiceCallResult(); + result.setNamespace(instance.getNamespace()); + result.setService(instance.getService()); + result.setHost(instance.getHost()); + result.setPort(instance.getPort()); + //result.setInstance(instance); + return result; + } + + @Test + public void testUpdateServiceCallResult() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + int index = 1; + GetInstancesRequest req = new GetInstancesRequest(); + req.setNamespace(NAMESPACE_TEST); + req.setService(SERVICE_CIRCUIT_BREAKER); + InstancesResponse instances = consumerAPI.getInstances(req); + Assert.assertEquals(MAX_COUNT, instances.getInstances().length); + Instance instanceToLimit = instances.getInstances()[index]; + ServiceCallResult result = instanceToResult(instanceToLimit); + result.setRetCode(-1); + result.setDelay(1000L); + result.setRetStatus(RetStatus.RetFail); + consumerAPI.updateServiceCallResult(result); + } + } + + @Test + public void testCircuitBreakByErrorCount() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + Utils.sleepUninterrupted(10000); + Assert.assertNotNull(consumerAPI); + GetInstancesRequest getInstancesRequest = new GetInstancesRequest(); + getInstancesRequest.setNamespace(NAMESPACE_TEST); + getInstancesRequest.setService(SERVICE_CIRCUIT_BREAKER); + InstancesResponse instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT, instances.getInstances().length); + Instance instanceToLimit = instances.getInstances()[1]; + //report 60 fail in 500ms + for (int i = 0; i < 60; ++i) { + ServiceCallResult result = instanceToResult(instanceToLimit); + result.setRetCode(-1); + result.setDelay(1000L); + result.setRetStatus(RetStatus.RetFail); + consumerAPI.updateServiceCallResult(result); + if (i % 10 == 0) { + Utils.sleepUninterrupted(1); + } + } + Utils.sleepUninterrupted(1000); + instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT - 1, instances.getInstances().length); + Instance[] instanceArray = instances.getInstances(); + boolean exists = false; + for (int i = 0; i < instanceArray.length; ++i) { + if (instanceArray[i].getId().equals(instanceToLimit.getId())) { + exists = true; + } + } + Assert.assertFalse(exists); + LOG.info("start to test half open by error rate"); + Utils.sleepUninterrupted(10000); + instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT, instances.getInstances().length); + for (Instance instance : instances.getInstances()) { + CircuitBreakerStatus circuitBreakerStatus = instance.getCircuitBreakerStatus(); + if (null != circuitBreakerStatus + && circuitBreakerStatus.getStatus() == CircuitBreakerStatus.Status.HALF_OPEN) { + LOG.info("half open instance is {}", instance); + } + } + //default halfopen pass 3 success + int requestCountAfterHalfOpen = configuration.getConsumer().getCircuitBreaker() + .getRequestCountAfterHalfOpen(); + for (int i = 0; i < requestCountAfterHalfOpen; i++) { + ServiceCallResult result = instanceToResult(instanceToLimit); + result.setRetCode(-1); + result.setRetStatus(RetStatus.RetSuccess); + consumerAPI.updateServiceCallResult(result); + Utils.sleepUninterrupted(200); + consumerAPI.updateServiceCallResult(result); + } + LOG.info("start to test half open to close"); + Utils.sleepUninterrupted(1000); + instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT, instances.getInstances().length); + } + } + + @Test + public void testCircuitBreakByErrorRate() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + GetInstancesRequest getInstancesRequest = new GetInstancesRequest(); + getInstancesRequest.setNamespace(NAMESPACE_TEST); + getInstancesRequest.setService(SERVICE_CIRCUIT_BREAKER); + InstancesResponse instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT, instances.getInstances().length); + Instance instanceToLimit = instances.getInstances()[1]; + //report 60 fail in 500ms + for (int i = 0; i < 60; ++i) { + ServiceCallResult result = instanceToResult(instanceToLimit); + result.setDelay(1000L); + if (i % 2 == 0) { + result.setRetCode(0); + result.setRetStatus(RetStatus.RetSuccess); + Utils.sleepUninterrupted(1); + } else { + result.setRetCode(-1); + result.setRetStatus(RetStatus.RetFail); + } + consumerAPI.updateServiceCallResult(result); + Utils.sleepUninterrupted(1); + } + Utils.sleepUninterrupted(1000); + instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT - 1, instances.getInstances().length); + Instance[] instanceArray = instances.getInstances(); + boolean exists = false; + for (int i = 0; i < instanceArray.length; ++i) { + if (instanceArray[i].getId().equals(instanceToLimit.getId())) { + exists = true; + } + } + Assert.assertFalse(exists); + Utils.sleepUninterrupted(10000); + //default halfopen pass 3 success + int requestCountAfterHalfOpen = configuration.getConsumer().getCircuitBreaker() + .getRequestCountAfterHalfOpen(); + for (int i = 0; i < requestCountAfterHalfOpen; i++) { + ServiceCallResult result = instanceToResult(instanceToLimit); + result.setRetCode(-1); + result.setRetStatus(RetStatus.RetSuccess); + consumerAPI.updateServiceCallResult(result); + Utils.sleepUninterrupted(200); + consumerAPI.updateServiceCallResult(result); + } + LOG.info("start to test half open to close"); + Utils.sleepUninterrupted(1000); + instances = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MAX_COUNT, instances.getInstances().length); + } + } + +} \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/log4j2.xml b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/polaris.yml b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/polaris.yml new file mode 100644 index 000000000..83de41b57 --- /dev/null +++ b/polaris-circuitbreaker/polaris-circuitbreaker-factory/src/test/resources/polaris.yml @@ -0,0 +1,17 @@ +global: + #描述:对接polaris server的相关配置 + serverConnector: + addresses: + - 127.0.0.1:10081 +#描述:主调端配置 +consumer: + localCache: + persistEnable: false + #描述:节点熔断相关配置 + circuitBreaker: + #描述:是否启用节点熔断功能 + enable: true + #描述:定时熔断检测周期 + checkPeriod: 1s + #描述:熔断周期 + sleepWindow: 5s \ No newline at end of file diff --git a/polaris-circuitbreaker/pom.xml b/polaris-circuitbreaker/pom.xml new file mode 100644 index 000000000..997c22a4b --- /dev/null +++ b/polaris-circuitbreaker/pom.xml @@ -0,0 +1,21 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker + pom + + polaris-circuitbreaker-api + polaris-circuitbreaker-client + polaris-circuitbreaker-examples + polaris-circuitbreaker-factory + + \ No newline at end of file diff --git a/polaris-client/pom.xml b/polaris-client/pom.xml new file mode 100644 index 000000000..4f4f2eb89 --- /dev/null +++ b/polaris-client/pom.xml @@ -0,0 +1,108 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-client + + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-protobuf + ${project.version} + + + + com.tencent.nameservice + polaris-config-tencent + ${project.version} + + + com.tencent.nameservice + polaris-config-opensource + ${project.version} + + + + io.grpc + grpc-netty-shaded + ${grpc.version} + + + io.grpc + grpc-protobuf + ${grpc.version} + + + io.grpc + grpc-stub + ${grpc.version} + + + io.grpc + grpc-context + ${grpc.version} + + + + + + + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.google.protobuf + protobuf-java-util + ${protobuf.version} + + + protobuf-java + com.google.protobuf + + + guava + com.google.guava + + + gson + com.google.code.gson + + + + + + javax.annotation + javax.annotation-api + ${javax.annotation.version} + provided + + + \ No newline at end of file diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/api/BaseEngine.java b/polaris-client/src/main/java/com/tencent/polaris/client/api/BaseEngine.java new file mode 100644 index 000000000..65ff3236b --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/api/BaseEngine.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.api; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.rpc.RequestBaseEntity; +import com.tencent.polaris.api.control.Destroyable; + +/** + * 基础构建引擎,API会基于该类进行实现 + * + * @author andrewshan + * @date 2019/8/21 + */ +public abstract class BaseEngine extends Destroyable { + + protected final SDKContext sdkContext; + + public BaseEngine(SDKContext sdkContext) { + this.sdkContext = sdkContext; + } + + public void init() throws PolarisException { + sdkContext.init(); + subInit(); + } + + public SDKContext getSDKContext() { + return sdkContext; + } + + /** + * 子类实现,用于做次级初始化 + */ + protected abstract void subInit() throws PolarisException; + + protected void checkAvailable(String apiName) throws PolarisException { + if (isDestroyed()) { + throw new PolarisException(ErrorCode.INVALID_STATE, + String.format("%s: api instance has been destroyed", apiName)); + } + } + + /** + * 获取API超时时间 + * + * @param entity entity + * @return 超时时间,单位毫秒 + */ + protected long getTimeout(RequestBaseEntity entity) { + return entity.getTimeoutMs() == 0 ? sdkContext.getConfig().getGlobal().getAPI().getTimeout() + : entity.getTimeoutMs(); + } + + @Override + protected void doDestroy() { + sdkContext.doDestroy(); + } +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/api/SDKContext.java b/polaris-client/src/main/java/com/tencent/polaris/client/api/SDKContext.java new file mode 100644 index 000000000..0a246a233 --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/api/SDKContext.java @@ -0,0 +1,273 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.api; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.ClusterConfig; +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.config.global.SystemConfig; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Manager; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.Supplier; +import com.tencent.polaris.api.plugin.TypeProvider; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.ValueContext; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; +import com.tencent.polaris.api.plugin.impl.PluginManager; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.factory.ConfigAPIFactory; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import java.io.Closeable; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.ServiceLoader; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * SDK初始化相关的上下文信息 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class SDKContext extends Destroyable implements InitContext, AutoCloseable, Closeable { + + private static final Logger LOG = LoggerFactory.getLogger(SDKContext.class); + + /** + * 配置对象 + */ + private final Configuration configuration; + + /** + * 初始化标识 + */ + private final AtomicBoolean initialized = new AtomicBoolean(false); + + /** + * 插件管理器 + */ + private final Manager plugins; + + private final ValueContext valueContext; + + private final Extensions extensions = new Extensions(); + + private final Object lock = new Object(); + + private List destroyHooks = new ArrayList<>(); + + private final Collection serverServices; + + public ValueContext getValueContext() { + return valueContext; + } + + /** + * 构造器 + * + * @param configuration 配置 + * @param plugins 插件工厂 + * @param valueContext 上下文 + */ + public SDKContext(Configuration configuration, Manager plugins, ValueContext valueContext) { + this.configuration = configuration; + this.plugins = plugins; + this.valueContext = valueContext; + this.valueContext.setClientId(UUID.randomUUID().toString()); + List services = new ArrayList<>(); + //加载系统服务配置 + SystemConfig system = configuration.getGlobal().getSystem(); + ClusterConfig discoverCluster = system.getDiscoverCluster(); + if (null != discoverCluster && !discoverCluster.isSameAsBuiltin()) { + services.add(new ServerServiceInfo(ClusterType.SERVICE_DISCOVER_CLUSTER, discoverCluster)); + } + ClusterConfig healthCheckCluster = system.getHealthCheckCluster(); + if (null != healthCheckCluster && !healthCheckCluster.isSameAsBuiltin()) { + services.add(new ServerServiceInfo(ClusterType.HEALTH_CHECK_CLUSTER, healthCheckCluster)); + } + ClusterConfig monitorCluster = system.getMonitorCluster(); + if (null != monitorCluster && !monitorCluster.isSameAsBuiltin()) { + services.add(new ServerServiceInfo(ClusterType.MONITOR_CLUSTER, monitorCluster)); + } + this.serverServices = Collections.unmodifiableCollection(services); + } + + public synchronized void init() throws PolarisException { + if (!initialized.compareAndSet(false, true)) { + return; + } + extensions.init(configuration, plugins, valueContext); + plugins.postContextInitPlugins(extensions); + } + + + public Extensions getExtensions() { + return extensions; + } + + public Configuration getConfig() { + return configuration; + } + + public Supplier getPlugins() { + return plugins; + } + + @Override + protected void doDestroy() { + synchronized (lock) { + for (Destroyable destroyable : destroyHooks) { + destroyable.destroy(); + } + } + plugins.destroyPlugins(); + } + + /** + * 通过默认配置初始化SDKContext + * + * @return SDKContext对象 + * @throws PolarisException 初始化异常 + */ + public static SDKContext initContext() throws PolarisException { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + return initContextByConfig(configuration); + } + + /** + * 通过配置对象初始化SDK上下文 + * + * @param config 配置对象 + * @return SDK上下文 + * @throws PolarisException 初始化过程的异常 + */ + public static SDKContext initContextByConfig(Configuration config) throws PolarisException { + try { + ((ConfigurationImpl) config).setDefault(); + config.verify(); + } catch (IllegalArgumentException e) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, "fail to verify configuration", e); + } + ServiceLoader providers = ServiceLoader.load(TypeProvider.class); + List types = new ArrayList<>(); + for (TypeProvider provider : providers) { + types.addAll(provider.getTypes()); + } + PluginManager manager = new PluginManager(types); + ValueContext valueContext = new ValueContext(); + valueContext.setHost(parseHost(config)); + SDKContext initContext = new SDKContext(config, manager, valueContext); + + try { + manager.initPlugins(initContext); + } catch (Throwable e) { + manager.destroyPlugins(); + if (e instanceof PolarisException) { + throw e; + } + throw new PolarisException(ErrorCode.PLUGIN_ERROR, "plugin error", e); + } + return initContext; + } + + @Override + public Collection getServerServices() { + return serverServices; + } + + public static String parseHost(Configuration configuration) { + String hostAddress = configuration.getGlobal().getAPI().getBindIP(); + if (!StringUtils.isBlank(hostAddress)) { + return hostAddress; + } + String nic = configuration.getGlobal().getAPI().getBindIf(); + return resolveAddress(nic); + } + + private static NetworkInterface resolveNetworkInterface(String nic) { + NetworkInterface ni = null; + try { + if (StringUtils.isNotBlank(nic)) { + ni = NetworkInterface.getByName(nic); + } + } catch (SocketException e) { + LOG.error("[ReportClient]get nic failed, nic:{}", nic, e); + } + if (null != nic) { + return ni; + } + //获取第一张网卡 + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + if (!networkInterface.isLoopback() && networkInterface.getInetAddresses().hasMoreElements()) { + return networkInterface; + } + } + } catch (SocketException e) { + LOG.error("[ReportClient]get all network interfaces failed", e); + } + return null; + } + + private static final String DEFAULT_ADDRESS = "127.0.0.1"; + + /** + * 解析网卡IP + * + * @param nic 网卡标识,如eth1 + * @return 地址信息 + */ + private static String resolveAddress(String nic) { + NetworkInterface ni = resolveNetworkInterface(nic); + if (null == ni) { + return DEFAULT_ADDRESS; + } + Enumeration inetAddresses = ni.getInetAddresses(); + if (inetAddresses.hasMoreElements()) { + InetAddress inetAddress = inetAddresses.nextElement(); + return inetAddress.getCanonicalHostName(); + } + return DEFAULT_ADDRESS; + } + + public void registerDestroyHook(Destroyable destroyable) { + synchronized (lock) { + destroyHooks.add(destroyable); + } + } + + @Override + public void close() { + destroy(); + } +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/api/ServiceCallResultListener.java b/polaris-client/src/main/java/com/tencent/polaris/client/api/ServiceCallResultListener.java new file mode 100644 index 000000000..9478d1cce --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/api/ServiceCallResultListener.java @@ -0,0 +1,77 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.api; + +import com.tencent.polaris.api.pojo.InstanceGauge; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +/** + * 服务上报数据处理器统一接口 + */ +public interface ServiceCallResultListener { + + /** + * 作为KEY存放在context中进行索引 + */ + String CONTEXT_KEY_RESULT_LISTENERS = "serviceCallResultListeners"; + + /** + * 初始化handler + * + * @param context SDK上下文 + */ + void init(SDKContext context); + + /** + * 收到服务上报数据后的回调处理 + * + * @param result 上报数据 + */ + void onServiceCallResult(InstanceGauge result); + + /** + * 停机释放资源 + */ + void destroy(); + + /** + * 从全局变量中获取监听器数组 + * + * @param sdkContext 全局上下文 + * @return 监听器数组 + */ + static List getServiceCallResultListeners(SDKContext sdkContext) { + synchronized (ServiceCallResultListener.class) { + List serviceCallResultListeners = sdkContext.getValueContext() + .getValue(CONTEXT_KEY_RESULT_LISTENERS); + if (null != serviceCallResultListeners) { + return serviceCallResultListeners; + } + serviceCallResultListeners = new ArrayList<>(); + ServiceLoader listeners = ServiceLoader.load(ServiceCallResultListener.class); + for (ServiceCallResultListener listener : listeners) { + listener.init(sdkContext); + serviceCallResultListeners.add(listener); + } + sdkContext.getValueContext().setValue(CONTEXT_KEY_RESULT_LISTENERS, serviceCallResultListeners); + return serviceCallResultListeners; + } + } +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java b/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java new file mode 100644 index 000000000..9f105a7df --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/flow/BaseFlow.java @@ -0,0 +1,309 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.flow; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.RetriableException; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.DefaultRouterChainGroup; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.compose.RouterChainGroup; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceInstancesWrap; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.rpc.Criteria; +import com.tencent.polaris.api.rpc.RequestBaseEntity; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.client.util.Utils; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 同步调用流程 + * + * @author andrewshan + * @date 2019/8/24 + */ +public class BaseFlow { + + + private static final Logger LOG = LoggerFactory.getLogger(BaseFlow.class); + + /** + * 通用获取单个服务实例的方法,用于SDK内部调用 + * + * @param extensions 插件上下文 + * @param serviceKey 服务信息 + * @param coreRouterNames 核心路由插件链 + * @param lbPolicy 负载均衡策略 + * @param protocol 协议信息 + * @param hashKey 一致性hash的key + * @return 过滤后的实例 + */ + public static Instance commonGetOneInstance(Extensions extensions, ServiceKey serviceKey, + List coreRouterNames, String lbPolicy, String protocol, String hashKey) { + ServiceEventKey svcEventKey = new ServiceEventKey(serviceKey, EventType.INSTANCE); + LOG.info("[ConnectionManager]start to discover service {}", svcEventKey); + DefaultServiceEventKeysProvider provider = new DefaultServiceEventKeysProvider(); + provider.setSvcEventKey(svcEventKey); + //为性能考虑,优先使用本地缓存 + provider.setUseCache(true); + FlowControlParam flowControlParam = new DefaultFlowControlParam(); + APIConfig apiConfig = extensions.getConfiguration().getGlobal().getAPI(); + flowControlParam.setTimeoutMs(apiConfig.getTimeout()); + flowControlParam.setMaxRetry(apiConfig.getMaxRetryTimes()); + flowControlParam.setRetryIntervalMs(apiConfig.getRetryInterval()); + //执行服务路由 + ServiceInfo dstSvcInfo = new ServiceInfo(); + Map metadata = new HashMap<>(); + metadata.put("protocol", protocol); + dstSvcInfo.setMetadata(metadata); + RouteInfo routeInfo = new RouteInfo( + null, null, dstSvcInfo, null, ""); + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(extensions, false, provider, flowControlParam); + LOG.info("[ConnectionManager]success to discover service {}", svcEventKey); + ServiceInstances serviceInstances = resourcesResponse.getServiceInstances(svcEventKey); + RouterChainGroup sysRouterChainGroup = extensions.getSysRouterChainGroup(); + List coreRouters = Extensions.loadServiceRouters(coreRouterNames, extensions.getPlugins()); + RouterChainGroup routerChainGroup = new DefaultRouterChainGroup(sysRouterChainGroup.getBeforeRouters(), + coreRouters, sysRouterChainGroup.getAfterRouters()); + ServiceInstances instancesAfterRoute = BaseFlow + .processServiceRouters(routeInfo, serviceInstances, routerChainGroup); + + //执行负载均衡 + LoadBalancer loadBalancer = (LoadBalancer) extensions.getPlugins() + .getPlugin(PluginTypes.LOAD_BALANCER.getBaseType(), lbPolicy); + Criteria criteria = new Criteria(); + criteria.setHashKey(hashKey); + return BaseFlow.processLoadBalance(loadBalancer, criteria, instancesAfterRoute); + } + + /** + * 处理服务路由 + * + * @param routeInfo 路由信息 + * @param dstInstances 目标实例列表 + * @param routerChainGroup 插件链 + * @return 过滤后的实例 + * @throws PolarisException 异常 + */ + public static ServiceInstances processServiceRouters(RouteInfo routeInfo, ServiceInstances dstInstances, + RouterChainGroup routerChainGroup) throws PolarisException { + if (CollectionUtils.isEmpty(dstInstances.getInstances())) { + return dstInstances; + } + boolean processed = false; + ServiceInstancesWrap serviceInstancesWrap = new ServiceInstancesWrap( + dstInstances, dstInstances.getInstances(), dstInstances.getTotalWeight()); + //先走前置路由 + if (processRouterChain(routerChainGroup.getBeforeRouters(), routeInfo, serviceInstancesWrap)) { + processed = true; + } + //再走业务路由 + if (processRouterChain(routerChainGroup.getCoreRouters(), routeInfo, serviceInstancesWrap)) { + processed = true; + } + //最后走后置路由 + if (processRouterChain(routerChainGroup.getAfterRouters(), routeInfo, serviceInstancesWrap)) { + processed = true; + } + if (processed) { + serviceInstancesWrap.reloadTotalWeight(); + } + return serviceInstancesWrap; + } + + private static boolean processRouterChain(List routers, + RouteInfo routeInfo, ServiceInstancesWrap serviceInstances) throws PolarisException { + if (CollectionUtils.isEmpty(routers)) { + return false; + } + boolean processed = false; + for (ServiceRouter router : routers) { + if (CollectionUtils.isEmpty(serviceInstances.getInstances())) { + //实例为空,则退出路由 + break; + } + if (!router.enable(routeInfo, serviceInstances)) { + continue; + } + processed = true; + do { + RouteResult filteredInstances = router.getFilteredInstances(routeInfo, serviceInstances); + RouteResult.NextRouterInfo nextRouterInfo = filteredInstances.getNextRouterInfo(); + if (nextRouterInfo.getState() == RouteResult.State.Next) { + serviceInstances.setInstances(filteredInstances.getInstances()); + break; + } + //重试获取 + routeInfo.setNextRouterInfo(nextRouterInfo); + } while (true); + } + return processed; + } + + + /** + * 同步拉取资源数据 + * + * @param extensions 插件集合 + * @param internalRequest 是否内部请求 + * @param paramProvider 参数提供器 + * @param controlParam 控制参数 + * @return 多资源应答 + * @throws PolarisException 获取异常 + */ + public static ResourcesResponse syncGetResources(Extensions extensions, boolean internalRequest, + ServiceEventKeysProvider paramProvider, FlowControlParam controlParam) + throws PolarisException { + + if (CollectionUtils.isEmpty(paramProvider.getSvcEventKeys()) && null == paramProvider.getSvcEventKey()) { + return new ResourcesResponse(); + } + long currentTime = System.currentTimeMillis(); + long deadline = currentTime + controlParam.getTimeoutMs(); + int retryTime = 0; + while (currentTime < deadline) { + if (retryTime > controlParam.getMaxRetry()) { + break; + } + retryTime++; + long diffTimeout = deadline - currentTime; + try { + GetResourcesInvoker invoker = new GetResourcesInvoker(paramProvider, extensions, internalRequest, + paramProvider.isUseCache()); + return invoker.get(diffTimeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + //重试 + currentTime = Utils.sleepUninterrupted(controlParam.getRetryIntervalMs()); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + LOG.error(String.format("syncGetInstances fail for services %s", paramProvider.toString()), cause); + if (cause instanceof RetriableException) { + //可重试异常,尝试进行重试 + currentTime = Utils.sleepUninterrupted(controlParam.getRetryIntervalMs()); + } else { + throw (PolarisException) cause; + } + } catch (TimeoutException e) { + break; + } + } + //远程访问超时,从本地缓存读取数据 + ResourcesResponse resourcesResponse = new ResourcesResponse(); + if (readResourcesFromLocalCache(paramProvider, extensions, resourcesResponse)) { + return resourcesResponse; + } + String errMsg = String.format("timeout while waiting response for svcEventKeys %s, svcEventKey %s", + paramProvider.getSvcEventKeys(), paramProvider.getSvcEventKey()); + LOG.warn(errMsg); + throw new PolarisException(ErrorCode.API_TIMEOUT, errMsg); + } + + private static boolean readResourcesFromLocalCache(ServiceEventKeysProvider paramProvider, + Extensions extensions, ResourcesResponse resourcesResponse) { + LocalRegistry localRegistry = extensions.getLocalRegistry(); + if (null != paramProvider.getSvcEventKey()) { + if (loadLocalResources(paramProvider.getSvcEventKey(), resourcesResponse, localRegistry)) { + return false; + } + } + if (CollectionUtils.isNotEmpty(paramProvider.getSvcEventKeys())) { + for (ServiceEventKey svcEventKey : paramProvider.getSvcEventKeys()) { + if (loadLocalResources(svcEventKey, resourcesResponse, localRegistry)) { + return false; + } + } + } + return true; + } + + private static boolean loadLocalResources(ServiceEventKey svcEventKey, ResourcesResponse resourcesResponse, + LocalRegistry localRegistry) { + ResourceFilter filter = new ResourceFilter(svcEventKey, false, true); + if (svcEventKey.getEventType() == EventType.INSTANCE) { + ServiceInstances instances = localRegistry.getInstances(filter); + if (instances.isInitialized()) { + resourcesResponse.addServiceInstances(svcEventKey, instances); + return false; + } else { + return true; + } + } + ServiceRule serviceRule = localRegistry.getServiceRule(filter); + if (serviceRule.isInitialized()) { + resourcesResponse.addServiceRule(svcEventKey, serviceRule); + return false; + } else { + return true; + } + } + + public static Instance processLoadBalance(LoadBalancer loadBalancer, Criteria criteria, + ServiceInstances dstInstances) throws PolarisException { + Instance instance = loadBalancer.chooseInstance(criteria, dstInstances); + if (null == instance) { + throw new PolarisException(ErrorCode.INSTANCE_NOT_FOUND, + String.format("no suitable instance for service %s after loadbanlancer %s", + dstInstances.getNamespace() + "-" + dstInstances.getService(), loadBalancer.getName())); + } + return instance; + } + + /** + * 构建流程控制参数 + * + * @param entity 请求对象 + * @param config 配置对象 + * @param controlParam 控制参数 + */ + public static void buildFlowControlParam(RequestBaseEntity entity, Configuration config, + FlowControlParam controlParam) { + long timeoutMs = entity.getTimeoutMs(); + if (timeoutMs == 0) { + timeoutMs = config.getGlobal().getAPI().getTimeout(); + } + controlParam.setTimeoutMs(timeoutMs); + controlParam.setMaxRetry(config.getGlobal().getAPI().getMaxRetryTimes()); + controlParam.setRetryIntervalMs(config.getGlobal().getAPI().getRetryInterval()); + } + +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/flow/DefaultFlowControlParam.java b/polaris-client/src/main/java/com/tencent/polaris/client/flow/DefaultFlowControlParam.java new file mode 100644 index 000000000..be8772495 --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/flow/DefaultFlowControlParam.java @@ -0,0 +1,68 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.flow; + +import com.tencent.polaris.api.config.global.APIConfig; + +public class DefaultFlowControlParam implements FlowControlParam { + + private long timeoutMs; + + private long retryIntervalMs; + + private int maxRetry; + + public DefaultFlowControlParam() { + } + + public DefaultFlowControlParam(APIConfig apiConfig) { + this.timeoutMs = apiConfig.getTimeout(); + this.retryIntervalMs = apiConfig.getRetryInterval(); + this.maxRetry = apiConfig.getMaxRetryTimes(); + } + + @Override + public long getTimeoutMs() { + return timeoutMs; + } + + @Override + public void setTimeoutMs(long timeoutMs) { + this.timeoutMs = timeoutMs; + } + + @Override + public long getRetryIntervalMs() { + return retryIntervalMs; + } + + @Override + public void setRetryIntervalMs(long retryIntervalMs) { + this.retryIntervalMs = retryIntervalMs; + } + + @Override + public int getMaxRetry() { + return maxRetry; + } + + @Override + public void setMaxRetry(int maxRetry) { + this.maxRetry = maxRetry; + } +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/flow/FlowControlParam.java b/polaris-client/src/main/java/com/tencent/polaris/client/flow/FlowControlParam.java new file mode 100644 index 000000000..66ec08120 --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/flow/FlowControlParam.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.flow; + +/** + * 流程控制参数 + */ +public interface FlowControlParam { + + /** + * 流程超时时间 + * + * @return ms + */ + long getTimeoutMs(); + + /** + * 设置流程超时时间 + * + * @param timeoutMs 超时毫秒 + */ + void setTimeoutMs(long timeoutMs); + + /** + * 重试间隔时间 + * + * @return ms + */ + long getRetryIntervalMs(); + + /** + * 设置重试间隔 + * + * @param retryIntervalMs 重试间隔 + */ + void setRetryIntervalMs(long retryIntervalMs); + + /** + * 流程最大重试次数 + * + * @return int + */ + int getMaxRetry(); + + /** + * 设置最大重试次数 + * + * @param maxRetry 最大重试次数 + */ + void setMaxRetry(int maxRetry); +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/flow/GetResourcesInvoker.java b/polaris-client/src/main/java/com/tencent/polaris/client/flow/GetResourcesInvoker.java new file mode 100644 index 000000000..fe6c28f7a --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/flow/GetResourcesInvoker.java @@ -0,0 +1,255 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.flow; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.RetriableException; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.registry.EventCompleteNotifier; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.utils.CollectionUtils; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 获取资源的回调 + */ +public class GetResourcesInvoker implements EventCompleteNotifier, Future { + + private static final Logger LOG = LoggerFactory.getLogger(GetResourcesInvoker.class); + + private final ResourcesResponse resourcesResponse = new ResourcesResponse(); + + private final Set listeningServices = new HashSet<>(); + + private final int totalCallback; + + private final AtomicInteger responseIncrement = new AtomicInteger(); + + private final Object notifier = new Object(); + + private final Extensions extensions; + + private final boolean internalRequest; + + private final boolean useCache; + + public GetResourcesInvoker(ServiceEventKeysProvider paramProvider, + Extensions extensions, boolean internalRequest, boolean useCache) throws PolarisException { + this.extensions = extensions; + this.internalRequest = internalRequest; + this.totalCallback = init(paramProvider); + this.useCache = useCache; + } + + /** + * 初始化invoker + * + * @param paramProvider + * @return 等待的数量 + * @throws PolarisException + */ + private int init(ServiceEventKeysProvider paramProvider) throws PolarisException { + LocalRegistry localRegistry = extensions.getLocalRegistry(); + int callbacks = 0; + if (!CollectionUtils.isEmpty(paramProvider.getSvcEventKeys())) { + for (ServiceEventKey svcEventKey : paramProvider.getSvcEventKeys()) { + listeningServices.add(svcEventKey); + callbacks = processSvcEventKey(localRegistry, callbacks, svcEventKey); + } + } + if (null != paramProvider.getSvcEventKey()) { + listeningServices.add(paramProvider.getSvcEventKey()); + callbacks = processSvcEventKey(localRegistry, callbacks, paramProvider.getSvcEventKey()); + } + return callbacks; + } + + private int processSvcEventKey(LocalRegistry localRegistry, int callbacks, ServiceEventKey svcEventKey) { + ResourceFilter filter = new ResourceFilter(svcEventKey, internalRequest, useCache); + switch (svcEventKey.getEventType()) { + case INSTANCE: + ServiceInstances instances = localRegistry.getInstances(filter); + if (instances.isInitialized()) { + resourcesResponse.addServiceInstances(svcEventKey, instances); + } else { + localRegistry.loadInstances(svcEventKey, this); + callbacks++; + } + break; + default: + ServiceRule serviceRule = localRegistry.getServiceRule(filter); + if (serviceRule.isInitialized()) { + resourcesResponse.addServiceRule(svcEventKey, serviceRule); + } else { + localRegistry.loadServiceRule(svcEventKey, this); + callbacks++; + } + break; + } + return callbacks; + } + + + @Override + public void complete(ServiceEventKey svcEventKey) { + LocalRegistry localRegistry = extensions.getLocalRegistry(); + ResourceFilter filter = new ResourceFilter(svcEventKey, internalRequest, useCache); + if (svcEventKey.getEventType() == ServiceEventKey.EventType.INSTANCE) { + ServiceInstances instances = localRegistry.getInstances(filter); + if (instances.isInitialized()) { + resourcesResponse.addServiceInstances(svcEventKey, instances); + } else { + resourcesResponse.addError( + svcEventKey, new RetriableException(ErrorCode.INVALID_STATE, "services not initialized")); + } + } else { + ServiceRule serviceRule = localRegistry.getServiceRule(filter); + if (serviceRule.isInitialized()) { + resourcesResponse.addServiceRule(svcEventKey, serviceRule); + } else { + resourcesResponse.addError( + svcEventKey, new RetriableException(ErrorCode.INVALID_STATE, "service rule not initialized")); + } + } + synchronized (notifier) { + int curTotal = responseIncrement.addAndGet(1); + if (totalCallback == curTotal) { + notifier.notifyAll(); + } + } + } + + @Override + public void completeExceptionally(ServiceEventKey svcEventKey, Throwable throwable) { + resourcesResponse.addError(svcEventKey, throwable); + synchronized (notifier) { + int curTotal = responseIncrement.addAndGet(1); + if (totalCallback == curTotal) { + notifier.notifyAll(); + } + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return totalCallback == 0 || responseIncrement.get() >= totalCallback; + } + + @Override + public ResourcesResponse get() throws InterruptedException, ExecutionException { + if (!isDone()) { + synchronized (notifier) { + if (!isDone()) { + notifier.wait(); + } + } + } + Map errors = resourcesResponse.getErrors(); + if (!errors.isEmpty()) { + throw new ExecutionException(combineErrors(errors.values())); + } + return resourcesResponse; + } + + @Override + public ResourcesResponse get( + long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + long timeoutMs = TimeUnit.MILLISECONDS.convert(timeout, unit); + if (!isDone()) { + synchronized (notifier) { + if (!isDone()) { + LOG.debug("start to wait for {}", this.listeningServices); + notifier.wait(timeoutMs); + } + LOG.debug("end to wait for {}", this.listeningServices); + if (!isDone()) { + LOG.debug("timeout to wait for {}", this.listeningServices); + throw new TimeoutException(); + } + } + } + Map errors = resourcesResponse.getErrors(); + if (!errors.isEmpty()) { + throw new ExecutionException(combineErrors(errors.values())); + } + return resourcesResponse; + } + + /** + * 多个错误集成一个错误 + * + * @return 集成的异常 + */ + private PolarisException combineErrors(Collection errors) { + StringBuilder builder = new StringBuilder(); + int retryCount = 0; + for (Throwable err : errors) { + if (err instanceof RetriableException) { + retryCount++; + } + builder.append(err.toString()); + builder.append("\n"); + } + if (retryCount == errors.size()) { + //全部都是重试,才进行重试 + return new RetriableException(ErrorCode.SERVER_USER_ERROR, builder.toString()); + } + return new PolarisException(ErrorCode.SERVER_USER_ERROR, builder.toString()); + } + + /** + * 资源回调监听 + */ + public interface ResourcesListener { + + /** + * 当调用完成后回调 + * + * @param response 应答信息 + */ + void onComplete(ResourcesResponse response); + } + + ; +} diff --git a/polaris-client/src/main/java/com/tencent/polaris/client/flow/ResourcesResponse.java b/polaris-client/src/main/java/com/tencent/polaris/client/flow/ResourcesResponse.java new file mode 100644 index 000000000..566eb986d --- /dev/null +++ b/polaris-client/src/main/java/com/tencent/polaris/client/flow/ResourcesResponse.java @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 - 2020. THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.flow; + +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceRule; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Response for resources query request. + * + * @author andrewshan + */ +public class ResourcesResponse { + + private final Map services = new ConcurrentHashMap<>(); + + private final Map rules = new ConcurrentHashMap<>(); + + private final Map errors = new ConcurrentHashMap<>(); + + /** + * 添加服务实例应答 + * + * @param svcEventKey 服务标识 + * @param instances 实例列表 + */ + public void addServiceInstances(ServiceEventKey svcEventKey, ServiceInstances instances) { + services.put(svcEventKey, instances); + } + + /** + * 获取服务实例应答对象 + * + * @param svcEventKey 服务标识 + * @return ServiceRuleResponse + */ + public ServiceInstances getServiceInstances(ServiceEventKey svcEventKey) { + return services.get(svcEventKey); + } + + /** + * 获取所有的实例应答缓存 + * + * @return services + */ + public Map getAllServiceInstances() { + return services; + } + + /** + * 获取所有的规则缓应答缓存 + * + * @return rules + */ + public Map getAllServiceRules() { + return rules; + } + + /** + * 添加规则应答 + * + * @param svcEventKey 服务标识 + * @param rule 规则数据 + */ + public void addServiceRule(ServiceEventKey svcEventKey, ServiceRule rule) { + rules.put(svcEventKey, rule); + } + + /** + * 获取规则应答对象 + * + * @param svcEventKey 服务标识 + * @return ServiceRuleResponse + */ + public ServiceRule getServiceRule(ServiceEventKey svcEventKey) { + return rules.get(svcEventKey); + } + + /** + * 添加错误信息 + * + * @param svcEventKey 服务标识 + * @param error 异常信息 + */ + public void addError(ServiceEventKey svcEventKey, Throwable error) { + errors.put(svcEventKey, error); + } + + /** + * 返回所有的错误 + * + * @return 错我列表 + */ + public Map getErrors() { + return errors; + } +} diff --git a/polaris-client/src/test/java/MessagePersistHandlerTest.java b/polaris-client/src/test/java/MessagePersistHandlerTest.java new file mode 100644 index 000000000..94297b0a1 --- /dev/null +++ b/polaris-client/src/test/java/MessagePersistHandlerTest.java @@ -0,0 +1,63 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.Base64; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * MessagePersistHandlerTest.java + * + * @author andrewshan + * @date 2019/9/6 + */ +public class MessagePersistHandlerTest { + + // @Test + public void testLoadPersistedServices() { + String regexPattern = "^svc#.+#.+\\.json$"; + Pattern compilePattern = Pattern.compile(regexPattern); + Matcher matcher = compilePattern.matcher("svc#nsa#svc1.json"); + System.out.println("result is " + matcher.matches()); + byte[] encodeBytes = Base64.getEncoder().encode("a.b".getBytes()); + System.out.println("encode for a.b is " + new String(encodeBytes)); + String persistDirPath = "E:\\cl5\\task"; + Path curDir = Paths.get(persistDirPath); + try { + Files.walkFileTree(curDir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) throws IOException { + Path fileName = filePath.getFileName(); + FileTime fileTime = attrs.creationTime(); + System.out.println("file name is " + fileName + ", fileTime is " + fileTime); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/polaris-client/src/test/resources/testdata/contact.yml b/polaris-client/src/test/resources/testdata/contact.yml new file mode 100644 index 000000000..579ba332a --- /dev/null +++ b/polaris-client/src/test/resources/testdata/contact.yml @@ -0,0 +1,15 @@ +name: this +age: 111 +sex: male +mode: 0 +plugin: + errorRate: + # 只有请求数达到某个阈值才执行熔断计算,默认10 + requestVolumeThreshold: 10 + # 触发熔断的错误率阈值,默认0.5 + errorRateThreshold: 0.5 + errorCount: + # 只有请求数达到某个阈值才执行熔断计算,默认10 + requestVolumeThreshold: 10 + # 触发熔断的错误率阈值,默认0.5 + errorRateThreshold: 0.5 \ No newline at end of file diff --git a/polaris-common/polaris-config-opensource/pom.xml b/polaris-common/polaris-config-opensource/pom.xml new file mode 100644 index 000000000..6575281d5 --- /dev/null +++ b/polaris-common/polaris-config-opensource/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-common + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-config-opensource + + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-common/polaris-config-opensource/src/main/java/com/tencent/polaris/config/opensource/DefaultOpensourceConfigProvider.java b/polaris-common/polaris-config-opensource/src/main/java/com/tencent/polaris/config/opensource/DefaultOpensourceConfigProvider.java new file mode 100644 index 000000000..f2317af5c --- /dev/null +++ b/polaris-common/polaris-config-opensource/src/main/java/com/tencent/polaris/config/opensource/DefaultOpensourceConfigProvider.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.config.opensource; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.DefaultConfigProvider; +import com.tencent.polaris.factory.ConfigAPIFactory; +import java.io.InputStream; + +public class DefaultOpensourceConfigProvider implements DefaultConfigProvider { + + private static final String CONF_PATH = "conf/opensource-config.yml"; + + private Configuration configuration; + + private final Object lock = new Object(); + + @Override + public String getName() { + return Configuration.DEFAULT_CONFIG_OPENSOURCE; + } + + @Override + public Configuration getDefaultConfig() { + synchronized (lock) { + if (null != configuration) { + return configuration; + } + InputStream resourceAsStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(CONF_PATH); + configuration = ConfigAPIFactory.loadConfig(resourceAsStream); + return configuration; + } + } +} \ No newline at end of file diff --git a/polaris-common/polaris-config-opensource/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider b/polaris-common/polaris-config-opensource/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider new file mode 100644 index 000000000..860dfe583 --- /dev/null +++ b/polaris-common/polaris-config-opensource/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.config.opensource.DefaultOpensourceConfigProvider \ No newline at end of file diff --git a/polaris-common/polaris-config-opensource/src/main/resources/conf/opensource-config.yml b/polaris-common/polaris-config-opensource/src/main/resources/conf/opensource-config.yml new file mode 100644 index 000000000..5390ea3eb --- /dev/null +++ b/polaris-common/polaris-config-opensource/src/main/resources/conf/opensource-config.yml @@ -0,0 +1,194 @@ +global: + #描述: 系统相关配置 + system: + #描述: 服务发现集群 + discoverCluster: + namespace: Polaris + service: polaris.discover + #描述: 服务刷新间隔 + refreshInterval: 1m + #描述: 是否使用埋点地址作为服务发现集群 + sameAsBuiltin: false + #描述: 系统服务使用的路由链 + routers: + - metadataRouter + - nearbyBasedRouter + #描述: 系统服务使用的负载均衡方式 + lbPolicy: weightedRandom + #描述: 健康检查集群 + healthCheckCluster: + namespace: Polaris + service: polaris.healthcheck + refreshInterval: 1m + sameAsBuiltin: false + routers: + - metadataRouter + - nearbyBasedRouter + lbPolicy: ringHash + #描述: 监控上报集群 + monitorCluster: + namespace: Polaris + service: polaris.monitor + refreshInterval: 1m + sameAsBuiltin: false + routers: + - metadataRouter + - nearbyBasedRouter + lbPolicy: ringHash + #描述: SDK流程数据缓存配置 + flowCache: + enable: true + name: simpleCache + expireInterval: 60s + #描述: SDK api调用相关配置 + api: + #描述: api超时时间 + timeout: 5s + #描述: 客户端给服务端定时上报自身信息的间隔 + reportInterval: 10m + #描述: api因为网络原因调用失败后的最大重试次数 + maxRetryTimes: 1 + #描述: 2次重试之间的重试间隔 + retryInterval: 500ms + #描述: 对接北极星服务端的相关配置 + serverConnector: + #描述:默认服务端埋点接入地址 + addresses: + - 127.0.0.1:8091 + #描述: 访问server的连接协议,SDK会根据协议名称会加载对应的插件 + protocol: grpc + #描述: 发起连接后的连接超时时间 + connectTimeout: 500ms + #描述: 与服务端发起远程请求超时时间 + messageTimeout: 5s + #描述: 连接空闲时间(以最后一次消息交互时间来算),长连接模式下,当连接空闲超过一定时间后,SDK会主动释放连接 + connectionIdleTimeout: 60s + #描述: server节点的切换周期,为了使得server的压力能够均衡,SDK会定期切换目标服务端节点 + serverSwitchInterval: 10m + #描述:重连间隔时间 + reconnectInterval: 500ms + #描述: 监控及日志数据上报相关配置 + statReporter: + #描述: 是否启用上报 + enable: false + plugin: + pushgatewayConfig: + #描述: 上报给prometheus pushgateway的任务名称 + jobName: "defaultJob" + #描述: pushgateway的服务名称 + serviceName: "polaris.monitor" + #描述: 上报给pushgateway的时间间隔 + pushInterval: 10 +#描述: 主调端配置 +consumer: + #描述: 本地服务缓存相关配置 + localCache: + #描述: 缓存插件名 + type: inmemory + #描述: 是否启用服务数据缓存 + serviceExpireEnable: true + #描述: 服务过期淘汰时间 + serviceExpireTime: 24h + #描述: 服务定期同步刷新周期 + serviceRefreshInterval: 2s + #描述: 是否启用服务数据文件缓存 + persistEnable: true + #描述: 服务缓存持久化目录,SDK在实例数据更新后,按照服务维度将数据持久化到磁盘 + persistDir: ./polaris/backup + #描述: 缓存写盘失败的最大重试次数 + persistMaxWriteRetry: 1 + #描述: 缓存从磁盘读取失败的最大重试次数 + persistMaxReadRetry: 0 + #描述: 缓存读写磁盘的重试间隔 + persistRetryInterval: 500ms + #描述: 服务路由相关配置 + serviceRouter: + #描述: 前置路由链 + beforeChain: + # 隔离路由 + - isolatedRouter + #描述: 服务路由链 + chain: + # 元数据路由 + - metadataRouter + # 规则路由 + - ruleBasedRouter + # 就近路由 + - nearbyBasedRouter + #描述: 后置路由链 + afterChain: + # 兜底(全死全活)路由 + - recoverRouter + #描述:服务路由插件的配置 + plugin: + metadataRouter: + #描述: 元数据路由降级策略。none(不降级), all(降级返回所有的节点), others(降级返回其他节点) + metadataFailOverType: none + nearbyBasedRouter: + #描述: 就近路由的最小匹配级别。region(大区)、zone(区域)、campus(园区) + matchLevel: zone + #描述: 最大匹配级别 + maxMatchLevel: all + #描述: 强制就近 + strictNearby: false + #描述: 全部实例不健康时是否降级其他地域 + enableDegradeByUnhealthyPercent: false + #描述: 达到降级标准的不健康实例百分比 + unhealthyPercentToDegrade: 100 + #描述: 是否通过上报方式获取地域信息 + enableReportLocalAddress: false + #描述:负载均衡相关配置 + loadbalancer: + #描述: 负载均衡类型(已注册的负载均衡插件名) + type: weightedRandom + #描述:节点熔断相关配置 + circuitBreaker: + #描述: 是否启用本地节点熔断功能 + enable: false + #描述: 故障检测周期,根据周期内故障进行熔断 + checkPeriod: 1m + #描述: 首次熔断时间,后续熔断时间=重试次数*sleepWindow + sleepWindow: 30s + #描述: 熔断器半开后最大允许的请求数 + requestCountAfterHalfOpen: 3 + #描述: 熔断器半开到关闭所必须的最少成功请求数 + successCountAfterHalfOpen: 3 + #描述:熔断策略,SDK会根据策略名称加载对应的熔断器插件(已注册的熔断器插件名) + chain: + - errorCount + - errorRate + #描述: 熔断插件配置 + plugin: + #描述:基于周期连续错误数熔断策略配置 + errorCount: + #描述: 触发连续错误熔断的阈值 + continuousErrorThreshold: 10 + #描述:基于周期错误率的熔断策略配置 + errorRate: + #描述:触发错误率熔断的阈值百分比 + errorRateThreshold: 50 + #描述: 错误率熔断的滑窗数量 + metricNumBuckets: 5 + #描述: 触发错误率熔断的最低请求阈值 + requestVolumeThreshold: 10 + #描述:主动探测相关配置 + outlierDetection: + #描述:何时开启主动探测。never(永不开启),on_recover(恢复时才开启主动探测),always(一直开启主动探测) + when: never + #描述:主动探测周期 + checkPeriod: 30s + #描述:主动探测插件链 + chain: + - http + - tcp + - udp +# 被调方配置 +provider: + # 限流配置 + rateLimit: + # 是否开启限流功能 + enable: true + # 限流本地存在的最大窗口限制 + maxWindowCount: 10000000 + # 超过最大窗口限制时的策略。pass(放通), reject(限流) + fallbackOnExceedWindowCount: pass \ No newline at end of file diff --git a/polaris-common/polaris-config-tencent/pom.xml b/polaris-common/polaris-config-tencent/pom.xml new file mode 100644 index 000000000..aaa3cddf6 --- /dev/null +++ b/polaris-common/polaris-config-tencent/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-common + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-config-tencent + + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-common/polaris-config-tencent/src/main/java/com/tencent/polaris/config/internal/DefaultInternalConfigProvider.java b/polaris-common/polaris-config-tencent/src/main/java/com/tencent/polaris/config/internal/DefaultInternalConfigProvider.java new file mode 100644 index 000000000..2f42615a1 --- /dev/null +++ b/polaris-common/polaris-config-tencent/src/main/java/com/tencent/polaris/config/internal/DefaultInternalConfigProvider.java @@ -0,0 +1,74 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.config.internal; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.DefaultConfigProvider; +import com.tencent.polaris.factory.ConfigAPIFactory; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.factory.util.IPV4Util; +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class DefaultInternalConfigProvider implements DefaultConfigProvider { + + private static final String CONF_PATH = "conf/default-internal-config.yml"; + + private static final List DEFAULT_ADDRESS = getDefaultAddress(); + + private static List getDefaultAddress() { +// "9.141.66.219","9.141.65.110","9.141.65.29","9.141.121.7","9.146.200.81", +// "9.146.202.35","9.146.205.191","9.146.200.61","9.141.66.244","9.146.202.27" + // 正式环境埋点IP + List res = Arrays.asList(160252635, 160252270, 160252189, 160266503, + 160614481, 160614947, 160615871, 160614461, 160252660, 160614939); + // 测试环境埋点IP +// List res = Arrays.asList(161294666); + return res.stream().map(r -> IPV4Util.intToIp(r) + ":8081").collect(Collectors.toList()); + } + + private Configuration configuration; + + private final Object lock = new Object(); + + @Override + public String getName() { + return Configuration.DEFAULT_CONFIG_TENCENT; + } + + @Override + public Configuration getDefaultConfig() { + synchronized (lock) { + if (null != configuration) { + return configuration; + } + InputStream resourceAsStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(CONF_PATH); + configuration = ConfigAPIFactory.loadConfig(resourceAsStream); + ConfigurationImpl configurationImpl = (ConfigurationImpl) configuration; + List addresses = configurationImpl.getGlobal().getServerConnector().getAddresses(); + // 要求默认写入一批ip,{@see http://polaris.oa.com/#/polaris/service/instance/Polaris/polaris.discover.default} + if (null == addresses || addresses.isEmpty()) { + configurationImpl.getGlobal().getServerConnector().setAddresses(DEFAULT_ADDRESS); + } + return configuration; + } + } +} diff --git a/polaris-common/polaris-config-tencent/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider b/polaris-common/polaris-config-tencent/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider new file mode 100644 index 000000000..daa7e12a7 --- /dev/null +++ b/polaris-common/polaris-config-tencent/src/main/resources/META-INF/services/com.tencent.polaris.api.config.DefaultConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.config.internal.DefaultInternalConfigProvider \ No newline at end of file diff --git a/polaris-common/polaris-config-tencent/src/main/resources/conf/default-internal-config.yml b/polaris-common/polaris-config-tencent/src/main/resources/conf/default-internal-config.yml new file mode 100644 index 000000000..2d94863de --- /dev/null +++ b/polaris-common/polaris-config-tencent/src/main/resources/conf/default-internal-config.yml @@ -0,0 +1,183 @@ +global: + #描述: 系统相关配置 + system: + #描述: 服务发现集群 + discoverCluster: + namespace: Polaris + service: polaris.discover + #描述: 服务刷新间隔 + refreshInterval: 1m + #描述: 是否使用埋点地址作为服务发现集群 + sameAsBuiltin: false + #描述: 系统服务使用的路由链 + routers: + - metadataRouter + - nearbyBasedRouter + #描述: 系统服务使用的负载均衡方式 + lbPolicy: weightedRandom + #描述: 健康检查集群 + healthCheckCluster: + namespace: Polaris + service: polaris.healthcheck + refreshInterval: 1m + sameAsBuiltin: false + routers: + - metadataRouter + - nearbyBasedRouter + lbPolicy: ringHash + #描述: 监控上报集群 + monitorCluster: + namespace: Polaris + service: polaris.monitor + refreshInterval: 1m + sameAsBuiltin: false + routers: + - metadataRouter + - nearbyBasedRouter + lbPolicy: ringHash + #描述: SDK流程数据缓存配置 + flowCache: + enable: true + name: simpleCache + expireInterval: 60s + #描述: SDK api调用相关配置 + api: + #描述: api超时时间 + timeout: 5s + #描述: 客户端给服务端定时上报自身信息的间隔 + reportInterval: 10m + #描述: api因为网络原因调用失败后的最大重试次数 + maxRetryTimes: 1 + #描述: 2次重试之间的重试间隔 + retryInterval: 500ms + #描述: 对接北极星服务端的相关配置 + serverConnector: + #描述: 访问server的连接协议,SDK会根据协议名称会加载对应的插件 + protocol: grpc + #描述: 发起连接后的连接超时时间 + connectTimeout: 500ms + #描述: 与服务端发起远程请求超时时间 + messageTimeout: 5s + #描述: 连接空闲时间(以最后一次消息交互时间来算),长连接模式下,当连接空闲超过一定时间后,SDK会主动释放连接 + connectionIdleTimeout: 60s + #描述: server节点的切换周期,为了使得server的压力能够均衡,SDK会定期切换目标服务端节点 + serverSwitchInterval: 10m + #描述:重连间隔时间 + reconnectInterval: 500ms + #描述: 监控及日志数据上报相关配置 + statReporter: + #描述: 是否启用上报 + enable: false +#描述: 主调端配置 +consumer: + #描述: 本地服务缓存相关配置 + localCache: + #描述: 缓存插件名 + type: inmemory + #描述: 是否启用服务数据缓存 + serviceExpireEnable: true + #描述: 服务过期淘汰时间 + serviceExpireTime: 24h + #描述: 服务定期同步刷新周期 + serviceRefreshInterval: 2s + #描述: 是否启用服务数据文件缓存 + persistEnable: true + #描述: 服务缓存持久化目录,SDK在实例数据更新后,按照服务维度将数据持久化到磁盘 + persistDir: ./polaris/backup + #描述: 缓存写盘失败的最大重试次数 + persistMaxWriteRetry: 1 + #描述: 缓存从磁盘读取失败的最大重试次数 + persistMaxReadRetry: 0 + #描述: 缓存读写磁盘的重试间隔 + persistRetryInterval: 500ms + #描述: 服务路由相关配置 + serviceRouter: + #描述: 前置路由链 + beforeChain: + # 隔离路由 + - isolatedRouter + #描述: 服务路由链 + chain: + # 元数据路由 + - metadataRouter + # 规则路由 + - ruleBasedRouter + # 就近路由 + - nearbyBasedRouter + #描述: 后置路由链 + afterChain: + # 兜底(全死全活)路由 + - recoverRouter + #描述:服务路由插件的配置 + plugin: + metadataRouter: + #描述: 元数据路由降级策略。none(不降级), all(降级返回所有的节点), others(降级返回其他节点) + metadataFailOverType: none + nearbyBasedRouter: + #描述: 就近路由的最小匹配级别。region(大区)、zone(区域)、campus(园区) + matchLevel: zone + #描述: 最大匹配级别 + maxMatchLevel: all + #描述: 强制就近 + strictNearby: false + #描述: 全部实例不健康时是否降级其他地域 + enableDegradeByUnhealthyPercent: false + #描述: 达到降级标准的不健康实例百分比 + unhealthyPercentToDegrade: 100 + #描述: 是否通过上报方式获取地域信息 + enableReportLocalAddress: true + #描述:负载均衡相关配置 + loadbalancer: + #描述: 负载均衡类型(已注册的负载均衡插件名) + type: weightedRandom + #描述:节点熔断相关配置 + circuitBreaker: + #描述: 是否启用本地节点熔断功能 + enable: true + #描述: 故障检测周期,根据周期内故障进行熔断 + checkPeriod: 1m + #描述: 首次熔断时间,后续熔断时间=重试次数*sleepWindow + sleepWindow: 30s + #描述: 熔断器半开后最大允许的请求数 + requestCountAfterHalfOpen: 3 + #描述: 熔断器半开到关闭所必须的最少成功请求数 + successCountAfterHalfOpen: 3 + #描述:熔断策略,SDK会根据策略名称加载对应的熔断器插件(已注册的熔断器插件名) + chain: + - errorCount + - errorRate + #描述: 熔断插件配置 + plugin: + #描述:基于周期连续错误数熔断策略配置 + errorCount: + #描述: 触发连续错误熔断的阈值 + continuousErrorThreshold: 10 + #描述:基于周期错误率的熔断策略配置 + errorRate: + #描述:触发错误率熔断的阈值百分比 + errorRateThreshold: 50 + #描述: 错误率熔断的滑窗数量 + metricNumBuckets: 5 + #描述: 触发错误率熔断的最低请求阈值 + requestVolumeThreshold: 10 + #描述:主动探测相关配置 + outlierDetection: + #描述:何时开启主动探测。never(永不开启),on_recover(恢复时才开启主动探测),always(一直开启主动探测) + when: never + #描述:主动探测周期 + checkPeriod: 30s + #描述:主动探测插件链 + chain: + - http + - tcp + - udp +# 被调方配置 +provider: + # 限流配置 + rateLimit: + # 是否开启限流功能 + enable: true + # 限流本地存在的最大窗口限制 + maxWindowCount: 10000000 + # 超过最大窗口限制时的策略。pass(放通), reject(限流) + fallbackOnExceedWindowCount: pass \ No newline at end of file diff --git a/polaris-common/polaris-config/pom.xml b/polaris-common/polaris-config/pom.xml new file mode 100644 index 000000000..074a798e0 --- /dev/null +++ b/polaris-common/polaris-config/pom.xml @@ -0,0 +1,32 @@ + + + + polaris-common + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-config + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + com.tencent.nameservice + polaris-model + ${project.version} + + + \ No newline at end of file diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/Configuration.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/Configuration.java new file mode 100644 index 000000000..1232bda3a --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/Configuration.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config; + +import com.tencent.polaris.api.config.consumer.ConsumerConfig; +import com.tencent.polaris.api.config.global.GlobalConfig; +import com.tencent.polaris.api.config.provider.ProviderConfig; +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * SDK全量配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface Configuration extends Verifier { + + String DEFAULT_CONFIG_OPENSOURCE = "opensource"; + + String DEFAULT_CONFIG_TENCENT = "tencent"; + + + /** + * services.global前缀开头的所有配置项 + * + * @return GlobalConfig + */ + GlobalConfig getGlobal(); + + /** + * services.consumer前缀开头的所有配置项 + * + * @return ConsumerConfig + */ + ConsumerConfig getConsumer(); + + /** + * services.provider前缀开头的所有配置项 + * + * @return ProviderConfig + */ + ProviderConfig getProvider(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/DefaultConfigProvider.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/DefaultConfigProvider.java new file mode 100644 index 000000000..b65be4432 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/DefaultConfigProvider.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config; + +public interface DefaultConfigProvider { + + /** + * 获取默认配置名字 + * + * @return 默认配置名 + */ + String getName(); + + /** + * 获取默认配置 + * + * @return 默认配置对象 + */ + Configuration getDefaultConfig(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/CircuitBreakerConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/CircuitBreakerConfig.java new file mode 100644 index 000000000..183dbf681 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/CircuitBreakerConfig.java @@ -0,0 +1,73 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.consumer; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.List; + +/** + * 熔断相关的配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface CircuitBreakerConfig extends PluginConfig, Verifier { + + /** + * 是否启用熔断 + * + * @return boolean + */ + boolean isEnable(); + + /** + * 熔断器插件链 + * + * @return 插件链名字 + */ + List getChain(); + + /** + * 熔断器定时检测时间 + * + * @return 检测时间间隔 + */ + long getCheckPeriod(); + + /** + * 熔断周期,被熔断后多久可以变为半开 + * + * @return 熔断周期 + */ + long getSleepWindow(); + + /** + * 半开状态后最多分配多少个探测请求 + * + * @return 探测请求数 + */ + int getRequestCountAfterHalfOpen(); + + /** + * 半开状态后多少个成功请求则恢复 + * + * @return 半开成功数 + */ + int getSuccessCountAfterHalfOpen(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ConsumerConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ConsumerConfig.java new file mode 100644 index 000000000..d8e3183b5 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ConsumerConfig.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.consumer; + +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 调用者配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface ConsumerConfig extends Verifier { + + /** + * services.consumer.localCache前缀开头的所有配置 + * + * @return LocalCacheConfig + */ + LocalCacheConfig getLocalCache(); + + /** + * services.consumer.serviceRouter前缀开头的所有配置 + * + * @return ServiceRouterConfig + */ + ServiceRouterConfig getServiceRouter(); + + /** + * services.consumer.loadbalancer前缀开头的所有配置 + * + * @return LoadBalanceConfig + */ + LoadBalanceConfig getLoadbalancer(); + + /** + * services.consumer.circuitbreaker前缀开头的所有配置 + * + * @return CircuitBreakerConfig + */ + CircuitBreakerConfig getCircuitBreaker(); + + /** + * services.consumer.outlierDetection前缀开头的所有配置 + * + * @return OutlierDetectionConfig + */ + OutlierDetectionConfig getOutlierDetection(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LoadBalanceConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LoadBalanceConfig.java new file mode 100644 index 000000000..4a9a448bc --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LoadBalanceConfig.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.consumer; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 负载均衡相关配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface LoadBalanceConfig extends PluginConfig, Verifier { + + /** + * 权重随机负载均衡插件名 + */ + String LOAD_BALANCE_WEIGHTED_RANDOM = "weightedRandom"; + + /** + * 权重一致性负载均衡插件名 + */ + String LOAD_BALANCE_RING_HASH = "ringHash"; + + /** + * 负载均衡类型 + * + * @return String + */ + String getType(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LocalCacheConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LocalCacheConfig.java new file mode 100644 index 000000000..db6bfdf78 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/LocalCacheConfig.java @@ -0,0 +1,103 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.consumer; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 本地缓存相关配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface LocalCacheConfig extends PluginConfig, Verifier { + + /** + * services.consumer.localCache.service.expireTime + * 服务的超时淘汰时间 + * + * @return long, 毫秒 + */ + long getServiceExpireTime(); + + /** + * services.consumer.localCache.service.refreshInterval + * 服务的定期刷新时间 + * + * @return long, 毫秒 + */ + long getServiceRefreshInterval(); + + /** + * services.consumer.localCache.type + * 本地缓存类型,可修改成具体的缓存插件名 + * + * @return String + */ + String getType(); + + /** + * services.consumer.localCache.persistEnable + * 是否启用本地持久化缓存机制 + * + * @return boolean + */ + boolean isPersistEnable(); + + /** + * services.consumer.localCache.persistDir + * 本地缓存持久化目录 + * + * @return String + */ + String getPersistDir(); + + /** + * services.consumer.localCache.persistMaxWriteRetry + * 本地缓存持久化最大写重试次数, 默认5次 + * + * @return int + */ + int getPersistMaxWriteRetry(); + + /** + * services.consumer.localCache.persistMaxReadRetry + * 本地缓存持久化最大读重试次数, 默认1次 + * + * @return int + */ + int getPersistMaxReadRetry(); + + /** + * services.consumer.localCache.persistRetryInterval + * 本地缓存更新重试间隔 + * + * @return long + */ + long getPersistRetryInterval(); + + /** + * services.consumer.localCache.serviceExpireEnable + * 是否启用服务缓存淘汰 + * + * @return 淘汰 + */ + boolean isServiceExpireEnable(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/OutlierDetectionConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/OutlierDetectionConfig.java new file mode 100644 index 000000000..60248fcd4 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/OutlierDetectionConfig.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.consumer; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.List; + +/** + * OutlierDetectionConfig.java + * + * @author andrewshan + * @date 2019/9/18 + */ +public interface OutlierDetectionConfig extends PluginConfig, Verifier { + + enum When { + never, on_recover, always + } + + /** + * 何时启用探测 + * + * @return when + */ + When getWhen(); + + /** + * 探测器插件链 + * + * @return 探测插件列表 + */ + List getChain(); + + /** + * 探测器定时检测时间 + * + * @return long, 毫秒 + */ + long getCheckPeriod(); + +} \ No newline at end of file diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java new file mode 100644 index 000000000..fb7df99d4 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/consumer/ServiceRouterConfig.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.consumer; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.List; + +/** + * 服务路由相关配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface ServiceRouterConfig extends PluginConfig, Verifier { + + String DEFAULT_ROUTER_ISOLATED = "isolatedRouter"; + + String DEFAULT_ROUTER_RECOVER = "recoverRouter"; + + String DEFAULT_ROUTER_METADATA = "metadataRouter"; + + String DEFAULT_ROUTER_RULE = "ruleBasedRouter"; + + String DEFAULT_ROUTER_NEARBY = "nearbyBasedRouter"; + + /** + * services.consumer.serviceRouter.beforeChain + * 前置路由链配置 + * + * @return 前置路由链列表 + */ + List getBeforeChain(); + + /** + * services.consumer.serviceRouter.chain + * 路由核心链配置 + * + * @return 核心路由链列表 + */ + List getChain(); + + /** + * services.consumer.serviceRouter.afterChain + * 后置路由链配置 + * + * @return 后置路由链列表 + */ + List getAfterChain(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/APIConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/APIConfig.java new file mode 100644 index 000000000..54caaf1b4 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/APIConfig.java @@ -0,0 +1,73 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * api相关的配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface APIConfig extends Verifier { + + /** + * services.global.api.timeout + * 默认调用超时时间 + * + * @return long, 毫秒 + */ + long getTimeout(); + + /** + * 最大重试次数,设置为0则不重试,默认10次 + * + * @return 最大重试次数 + */ + int getMaxRetryTimes(); + + /** + * 调用失败后,自动重试的时间间隔,默认5ms + * + * @return long + */ + long getRetryInterval(); + + /** + * 获取监听的网卡名 + * + * @return 网卡名 + */ + String getBindIf(); + + /** + * 获取监听的IP地址 + * + * @return IP地址 + */ + String getBindIP(); + + /** + * 客户端上报周期,默认30s + * + * @return long + */ + long getReportInterval(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterConfig.java new file mode 100644 index 000000000..dd0cfa8c3 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterConfig.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.List; + +/** + * 集群配置 + */ +public interface ClusterConfig extends Verifier { + + /** + * 获取命名空间 + * + * @return namespace + */ + String getNamespace(); + + /** + * 获取服务名 + * + * @return service + */ + String getService(); + + /** + * 获取服务刷新间隔 + * + * @return refreshInterval + */ + long getRefreshInterval(); + + /** + * 获取系统服务路由链 + * + * @return routers + */ + List getRouters(); + + /** + * 获取系统服务负载均衡器 + * + * @return loadBalancer + */ + String getLbPolicy(); + + /** + * 是否与埋点地址一致,如果一致则无需填写服务地址信息 + * + * @return boolean + */ + boolean isSameAsBuiltin(); +} \ No newline at end of file diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterType.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterType.java new file mode 100644 index 000000000..be02a698d --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ClusterType.java @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +/** + * 集群类型 + */ +public enum ClusterType { + //埋点集群 + BUILTIN_CLUSTER, + //服务注册中心 + SERVICE_DISCOVER_CLUSTER, + //健康检查集群 + HEALTH_CHECK_CLUSTER, + //监控集群 + MONITOR_CLUSTER, +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/FlowCacheConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/FlowCacheConfig.java new file mode 100644 index 000000000..5769c3cfe --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/FlowCacheConfig.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 流程缓存配置对象 + */ +public interface FlowCacheConfig extends Verifier { + + String DEFAULT_FLOW_CACHE_NAME = "simpleCache"; + + /** + * 是否启用流程缓存 + * + * @return boolean + */ + boolean isEnable(); + + /** + * 获取缓存对象名 + * + * @return 对象名 + */ + String getName(); + + /** + * 获取缓存对象过期淘汰时间 + * + * @return long + */ + long getExpireInterval(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/GlobalConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/GlobalConfig.java new file mode 100644 index 000000000..d161dcf82 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/GlobalConfig.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 全局配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface GlobalConfig extends Verifier { + + /** + * 获取系统配置 + * + * @return SystemConfig + */ + SystemConfig getSystem(); + + /** + * services.global.api前缀开头的所有配置项 + * + * @return APIConfig + */ + APIConfig getAPI(); + + /** + * services.global.serverConnector前缀开头的所有配置项 + * + * @return ServerConnectorConfig + */ + ServerConnectorConfig getServerConnector(); + + /** + * services.global.statReporter前缀开头的所有配置项 + * + * @return StatReporterConfig + */ + StatReporterConfig getStatReporter(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/RunMode.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/RunMode.java new file mode 100644 index 000000000..afadba23f --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/RunMode.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +/** + * SDK运行模式 + * + * @author andrewshan + * @date 2019/8/20 + */ +public enum RunMode { + + //以no agent模式运行 + ModeNoAgent, + + //带agent模式运行 + ModeWithAgent +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ServerConnectorConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ServerConnectorConfig.java new file mode 100644 index 000000000..e446eb1ab --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/ServerConnectorConfig.java @@ -0,0 +1,79 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.List; + +/** + * 与名字服务服务端的连接配置 + * + * @author andrewshan + */ +public interface ServerConnectorConfig extends PluginConfig, Verifier { + + /** + * 远端server地址 + * + * @return 地址列表 + */ + List getAddresses(); + + /** + * 与server对接的协议,默认GRPC + * + * @return 协议名称 + */ + String getProtocol(); + + /** + * 与server的连接超时时间 + * + * @return long, 毫秒 + */ + long getConnectTimeout(); + + /** + * server的切换时延 + * + * @return long, 毫秒 + */ + long getServerSwitchInterval(); + + /** + * 获取消息等待最长超时时间 + * + * @return long, 毫秒 + */ + long getMessageTimeout(); + + /** + * 空闲连接过期时间 + * + * @return long, 毫秒 + */ + long getConnectionIdleTimeout(); + + /** + * 获取重连间隔 + * + * @return long, 毫秒 + */ + long getReconnectInterval(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/StatReporterConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/StatReporterConfig.java new file mode 100644 index 000000000..d4edd540c --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/StatReporterConfig.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.List; + +/** + * 数据上报配置 + */ +public interface StatReporterConfig extends PluginConfig, Verifier { + + /** + * 是否启用数据上报 + * + * @return 启用开关 + */ + boolean isEnable(); + + /** + * 启用的数据上报插件链 + * + * @return 插件名 + */ + List getChain(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/SystemConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/SystemConfig.java new file mode 100644 index 000000000..efc2c82a2 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/global/SystemConfig.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.global; + +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.Map; + +/** + * api相关的配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface SystemConfig extends Verifier { + + /** + * 获取流程缓存配置 + * + * @return flowCacheConfig + */ + FlowCacheConfig getFlowCache(); + + /** + * 获取服务发现集群地址 + * + * @return discover + */ + ClusterConfig getDiscoverCluster(); + + /** + * 获取心跳集群地址 + * + * @return healthCheck + */ + ClusterConfig getHealthCheckCluster(); + + /** + * 获取monitor集群地址 + * + * @return monitor + */ + ClusterConfig getMonitorCluster(); + + /** + * 获取变量列表 + * + * @return variables map + */ + Map getVariables(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java new file mode 100644 index 000000000..920b3b755 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/DefaultPlugins.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.plugin; + +/** + * 默认插件名 + * + * @author andrewshann + * @date 2019/8/27 + */ +public interface DefaultPlugins { + + /** + * GRPC连接器插件名 + */ + String SERVER_CONNECTOR_GRPC = "grpc"; + + /** + * 基于内存的本地缓存插件名 + */ + String LOCAL_REGISTRY_IN_MEMORY = "inmemory"; + + /** + * 基于连续错误数的熔断插件名 + */ + String CIRCUIT_BREAKER_ERROR_COUNT = "errorCount"; + + /** + * 基于错误率的熔断插件名 + */ + String CIRCUIT_BREAKER_ERROR_RATE = "errorRate"; + + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfig.java new file mode 100644 index 000000000..9bb584757 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfig.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.plugin; + +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.exception.PolarisException; +import java.util.Map; + +/** + * 插件配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface PluginConfig { + + /** + * 获取插件配置 + * + * @param pluginName 插件名 + * @param clazz 目标对象的类实例 + * @param 反序列化的目标类型 + * @return 插件配置对象 + * @throws PolarisException 异常 + */ + T getPluginConfig(String pluginName, Class clazz) throws PolarisException; + + /** + * 获取所有的插件配置 + * + * @return 所有插件配合 + * @throws PolarisException 解码异常 + */ + Map getPluginConfigs() throws PolarisException; +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfigProvider.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfigProvider.java new file mode 100644 index 000000000..0b63618b7 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/plugin/PluginConfigProvider.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.api.config.plugin; + +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 插件配置提供器 + */ +public interface PluginConfigProvider { + + /** + * 插件名 + * + * @return name + */ + String getName(); + + /** + * 获取插件配置类型 + * + * @return config clazz + */ + Class getPluginConfigClazz(); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java new file mode 100644 index 000000000..104daa9ca --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/ProviderConfig.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.provider; + +import com.tencent.polaris.api.config.verify.Verifier; + +/** + * 被调端配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface ProviderConfig extends Verifier { + + + /** + * 服务注册相关配置 + * + * @return registerConfig + */ + RegisterConfig getRegister(); + + /** + * 获取限流配置 + * + * @return 配置 + */ + RateLimitConfig getRateLimit(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RateLimitConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RateLimitConfig.java new file mode 100644 index 000000000..22a38e804 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RateLimitConfig.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.provider; + +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.verify.Verifier; + +public interface RateLimitConfig extends PluginConfig, Verifier { + + enum Fallback { + pass, reject, + } + + /** + * 是否开启限流功能 + * + * @return boolean + */ + boolean isEnable(); + + /** + * 最大限流窗口数量 + * + * @return int + */ + int getMaxWindowCount(); + + /** + * 限流窗口超标后的降级策略 + * + * @return fallback + */ + Fallback getFallbackOnExceedWindowCount(); +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RegisterConfig.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RegisterConfig.java new file mode 100644 index 000000000..2f6ad3a82 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/provider/RegisterConfig.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.provider; + +import com.tencent.polaris.api.config.verify.Verifier; + +public interface RegisterConfig extends Verifier { + + /** + * 获取命名空间 + * + * @return namespace + */ + String getNamespace(); + + /** + * 获取服务名 + * + * @return service + */ + String getService(); + + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/DefaultValues.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/DefaultValues.java new file mode 100644 index 000000000..39e55c53d --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/DefaultValues.java @@ -0,0 +1,256 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.verify; + + +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import java.time.Duration; + +/** + * 默认值定义 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface DefaultValues { + + /** + * 默认API调用的超时时间, 1s + */ + long DEFAULT_API_INVOKE_TIMEOUT_MS = Duration.parse("PT5S").toMillis(); + + /** + * 默认API重试间隔, 500ms + */ + long DEFAULT_API_RETRY_INTERVAL_MS = 500; + + + /** + * 默认API上报间隔, 30s + */ + long DEFAULT_API_REPORT_INTERVAL_MS = Duration.parse("PT30S").toMillis(); + + /** + * 默认的服务超时淘汰时间,1H + */ + long DEFAULT_SERVICE_EXPIRE_TIME_MS = Duration.parse("PT1H").toMillis(); + + /** + * 最小服务超时淘汰时间,5s + */ + long MIN_SERVICE_EXPIRE_TIME_MS = Duration.parse("PT5S").toMillis(); + + /** + * 默认的服务刷新间隔, 2s + */ + long MIN_SERVICE_REFRESH_INTERVAL_MS = Duration.parse("PT2S").toMillis(); + + /** + * 默认消息的超时时间 + */ + long DEFAULT_SERVER_MSG_TIMEOUT_MS = 1000; + + /** + * 默认消息的超时时间 + */ + long DEFAULT_SERVER_SERVICE_REFRESH_INTERVAL_MS = 60000; + + /** + * 默认SDK往Server连接超时时间间隔 + */ + long DEFAULT_SERVER_CONNECT_TIMEOUT_MS = 50; + + /** + * 默认发送队列的buffer大小,支持的最大瞬时并发度,默认10W + */ + int DEFAULT_REQUEST_QUEUE_SIZE = 100000; + + /** + * 默认server的切换时间时间 + */ + long DEFAULT_SERVER_SWITCH_INTERVAL_MS = Duration.parse("PT10M").toMillis(); + + /** + * 默认空闲连接过期时间 + */ + long DEFAULT_CONNECTION_IDLE_TIMEOUT_MS = Duration.parse("PT60S").toMillis(); + + /** + * 默认缓存持久化目录 + */ + String DEFAULT_CACHE_PERSIST_DIR = "./polaris/backup"; + + /** + * 是否打开熔断能力,默认true + */ + boolean DEFAULT_CIRCUIT_BREAKER_ENABLE = true; + + /** + * 是否打开健康探测能力,默认true + */ + boolean DEFAULT_OUTLIER_DETECT_ENABLE = true; + + /** + * 默认熔断节点检查周期 + */ + long DEFAULT_CIRCUIT_BREAKER_CHECK_PERIOD_MS = 30 * 1000; + + /** + * 最小节点检查周期 + */ + long MIN_CIRCUIT_BREAKER_CHECK_PERIOD_MS = 1000; + + /** + * 默认熔断周期,被熔断后多久变为半开 + */ + long DEFAULT_SLEEP_WINDOW_MS = 30 * 1000; + + /** + * 最小熔断周期 + */ + long MIN_SLEEP_WINDOW_MS = 1000; + + /** + * 默认恢复周期,半开后按多久的统计窗口进行恢复统计 + */ + long DEFAULT_RECOVER_WINDOW_MS = 60 * 1000; + + /** + * 最小恢复周期 + */ + long MIN_RECOVER_WINDOW_MS = 10 * 6000; + + /** + * 默认恢复统计的滑桶数 + */ + int DEFAULT_RECOVER_NUM_BUCKETS = 10; + + /** + * 最小恢复统计的滑桶数 + */ + int MIN_RECOVER_NUM_BUCKETS = 1; + + /** + * 半开状态后分配的探测请求数 + */ + int DEFAULT_REQUEST_COUNT_AFTER_HALF_OPEN = 10; + + /** + * 半开状态后恢复的成功请求数 + */ + int DEFAULT_SUCCESS_COUNT_AFTER_HALF_OPEN = 8; + + /** + * 默认的服务端连接器插件 + */ + String DEFAULT_SERVERCONNECTOR = DefaultPlugins.SERVER_CONNECTOR_GRPC; + + /** + * 默认本地缓存策略 + */ + String DEFAULT_LOCALCACHE = DefaultPlugins.LOCAL_REGISTRY_IN_MEMORY; + + /** + * 默认负载均衡器 + */ + String DEFAULT_LOADBALANCER = LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM; + + /** + * 默认错误率熔断器 + */ + String DEFAULT_CIRCUITBREAKER_ERRRATE = DefaultPlugins.CIRCUIT_BREAKER_ERROR_RATE; + + /** + * 默认持续错误熔断器 + */ + String DEFAULT_CIRCUITBREAKER_ERRCOUNT = DefaultPlugins.CIRCUIT_BREAKER_ERROR_COUNT; + + /** + * 默认健康探测手段,tcp + */ + String DEFAULT_TCP_OUTLIER_DETECT = "tcp"; + + /** + * 默认健康探测手段,udp + */ + String DEFAULT_UDP_OUTLIER_DETECT = "udp"; + + /** + * 默认健康探测手段,http + */ + String DEFAULT_HTTP_OUTLIER_DETECT = "http"; + + /** + * 默认权重值 + */ + int DEFAULT_WEIGHT = 100; + + /** + * 默认负载均衡放开限制的故障节点上限阈值 + */ + double DEFAULT_MAX_EJECT_PRECENT_THRESHOLD = 0.9; + + /** + * 默认最大重试次数,默认不重试 + */ + int DEFAULT_MAX_RETRY_TIMES = 0; + + /** + * 默认缓存最大写重试次数 + */ + int DEFAULT_PERSIST_MAX_WRITE_RETRY = 1; + + /** + * 默认缓存最大读重试次数 + */ + int DEFAULT_PERSIST_MAX_READ_RETRY = 0; + + /** + * 默认缓存持久化时间间隔 + */ + long DEFAULT_PERSIST_RETRY_INTERVAL_MS = 500; + + /** + * 默认健康探测周期 + */ + long DEFAULT_OUTLIER_DETECT_INTERVAL_MS = Duration.parse("PT10S").toMillis(); + + /** + * 最小定时任务轮询周期 + */ + long MIN_TIMING_INTERVAL_MS = 100; + + String DEFAULT_SYSTEM_NAMESPACE = "Polaris"; + String DEFAULT_SYSTEM_REFRESH_INTERVAL = "10m"; + + String DEFAULT_DISCOVER_SERVICE = "polaris.discover"; + String DEFAULT_HEALTH_CHECK_SERVICE = "polaris.healthcheck"; + String DEFAULT_MONITOR_SERVICE = "polaris.monitor"; + + /** + * 默认连接器协议 + */ + String DEFAULT_DISCOVER_PROTOCOL = "grpc"; + + /** + * 默认启用本地缓存机制 + */ + boolean DEFAULT_PERSIST_ENABLE = true; + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/Verifier.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/Verifier.java new file mode 100644 index 000000000..d5bfe0591 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/api/config/verify/Verifier.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.config.verify; + +/** + * 检验SDK配置 + * + * @author andrewshan + * @date 2019/8/20 + */ +public interface Verifier { + + /** + * 执行校验操作,参数校验失败会抛出IllegalArgumentException + */ + void verify(); + + /** + * 设置默认值信息 + * + * @param defaultObject 默认值对象 + */ + void setDefault(Object defaultObject); + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/ConfigAPIFactory.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/ConfigAPIFactory.java new file mode 100644 index 000000000..b32f4abee --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/ConfigAPIFactory.java @@ -0,0 +1,91 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.TreeTraversingParser; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLParser; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import java.io.InputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConfigAPIFactory { + + private static final Logger LOG = LoggerFactory.getLogger(ConfigAPIFactory.class); + + /** + * 通过配置文件加载配置对象 + * + * @param configStream 配置文件流 + * @return 配置对象 + * @throws PolarisException 文件加载异常 + */ + public static Configuration loadConfig(InputStream configStream) throws PolarisException { + YAMLFactory yamlFactory = new YAMLFactory(); + ObjectMapper mapper = new ObjectMapper(); + + try { + YAMLParser yamlParser = yamlFactory.createParser(configStream); + final JsonNode node = mapper.readTree(yamlParser); + TreeTraversingParser treeTraversingParser = new TreeTraversingParser(node); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper.readValue(treeTraversingParser, ConfigurationImpl.class); + } catch (Exception e) { + throw new PolarisException( + ErrorCode.INVALID_CONFIG, "fail to load config from stream", e); + } + } + + public static final String DEFAULT_CONFIG_PATH = "polaris.yml"; + + /** + * 默认配置对象,优先获取当前目录下polaris.yml配置文件,假如没有,则创建默认的配置对象 + * + * @return 默认配置对象 + * @throws PolarisException 加载异常 + */ + public static Configuration defaultConfig() throws PolarisException { + return defaultConfig(""); + } + + /** + * 默认配置对象,优先获取当前目录下polaris.yml配置文件,假如没有,则创建默认的配置对象 + * + * @param defaultConfigName 默认配置名 + * @return 默认配置对象 + * @throws PolarisException 加载异常 + */ + public static Configuration defaultConfig(String defaultConfigName) throws PolarisException { + InputStream inputStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream(DEFAULT_CONFIG_PATH); + if (null != inputStream) { + LOG.info("[Configuration]success to load config stream from {}", DEFAULT_CONFIG_PATH); + return loadConfig(inputStream); + } + ConfigurationImpl configuration = new ConfigurationImpl(defaultConfigName); + configuration.setDefault(); + return configuration; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/ConfigurationImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/ConfigurationImpl.java new file mode 100644 index 000000000..96dd2806b --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/ConfigurationImpl.java @@ -0,0 +1,150 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.DefaultConfigProvider; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.factory.config.consumer.ConsumerConfigImpl; +import com.tencent.polaris.factory.config.global.GlobalConfigImpl; +import com.tencent.polaris.factory.config.provider.ProviderConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; + +/** + * SDK全量配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class ConfigurationImpl implements Configuration { + + private static final Map configProviders = new HashMap<>(); + + static { + ServiceLoader providers = ServiceLoader.load(DefaultConfigProvider.class); + for (DefaultConfigProvider provider : providers) { + configProviders.put(provider.getName(), provider.getDefaultConfig()); + } + } + + @JsonIgnore + private final String defaultConfigName; + + @JsonProperty + private GlobalConfigImpl global; + + @JsonProperty + private ConsumerConfigImpl consumer; + + @JsonProperty + private ProviderConfigImpl provider; + + @Override + public GlobalConfigImpl getGlobal() { + return global; + } + + public void setGlobal(GlobalConfigImpl global) { + this.global = global; + } + + @Override + public ConsumerConfigImpl getConsumer() { + return consumer; + } + + public void setConsumer(ConsumerConfigImpl consumer) { + this.consumer = consumer; + } + + @Override + public ProviderConfigImpl getProvider() { + return provider; + } + + public void setProvider(ProviderConfigImpl provider) { + this.provider = provider; + } + + @Override + public void verify() { + ConfigUtils.validateNull(global, "global"); + ConfigUtils.validateNull(consumer, "consumer"); + ConfigUtils.validateNull(provider, "provider"); + global.verify(); + consumer.verify(); + provider.verify(); + } + + public ConfigurationImpl() { + defaultConfigName = Configuration.DEFAULT_CONFIG_TENCENT; + } + + public ConfigurationImpl(String defaultConfigName) { + this.defaultConfigName = defaultConfigName; + } + + private Configuration getDefaultConfig() { + Configuration configuration = null; + if (StringUtils.isNotBlank(defaultConfigName)) { + configuration = configProviders.get(defaultConfigName); + } + if (null == configuration) { + return configProviders.get(Configuration.DEFAULT_CONFIG_TENCENT); + } + return configuration; + } + + public void setDefault() { + setDefault(getDefaultConfig()); + } + + @Override + public void setDefault(Object defaultObject) { + if (null == global) { + global = new GlobalConfigImpl(); + } + if (null == consumer) { + consumer = new ConsumerConfigImpl(); + } + if (null == provider) { + provider = new ProviderConfigImpl(); + } + if (null != defaultObject) { + Configuration configuration = (Configuration) defaultObject; + global.setDefault(configuration.getGlobal()); + consumer.setDefault(configuration.getConsumer()); + provider.setDefault(configuration.getProvider()); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ConfigurationImpl{" + + "global=" + global + + ", consumer=" + consumer + + ", provider=" + provider + + '}'; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/CircuitBreakerConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/CircuitBreakerConfigImpl.java new file mode 100644 index 000000000..c6971957f --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/CircuitBreakerConfigImpl.java @@ -0,0 +1,176 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.consumer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.consumer.CircuitBreakerConfig; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; +import java.util.List; + +/** + * 熔断相关的配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class CircuitBreakerConfigImpl extends PluginConfigImpl implements CircuitBreakerConfig { + + @JsonProperty + private Boolean enable; + + @JsonProperty + private List chain; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long checkPeriod; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long sleepWindow; + + @JsonProperty + private Integer requestCountAfterHalfOpen; + + @JsonProperty + private Integer successCountAfterHalfOpen; + + @Override + public boolean isEnable() { + if (null == enable) { + return false; + } + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + @Override + public List getChain() { + return chain; + } + + public void setChain(List chain) { + this.chain = chain; + } + + public void setRequestCountAfterHalfOpen(int requestCountAfterHalfOpen) { + this.requestCountAfterHalfOpen = requestCountAfterHalfOpen; + } + + public void setSuccessCountAfterHalfOpen(int successCountAfterHalfOpen) { + this.successCountAfterHalfOpen = successCountAfterHalfOpen; + } + + @Override + public int getRequestCountAfterHalfOpen() { + return requestCountAfterHalfOpen; + } + + @Override + public int getSuccessCountAfterHalfOpen() { + return successCountAfterHalfOpen; + } + + @Override + public long getCheckPeriod() { + if (null == checkPeriod) { + return 0; + } + return checkPeriod; + } + + public void setCheckPeriod(long checkPeriod) { + this.checkPeriod = checkPeriod; + } + + @Override + public long getSleepWindow() { + if (null == sleepWindow) { + return 0; + } + return sleepWindow; + } + + public void setSleepWindow(long sleepWindow) { + this.sleepWindow = sleepWindow; + } + + @Override + public void verify() { + ConfigUtils.validateNull(enable, "circuitBreaker.enable"); + if (!enable) { + return; + } + if (CollectionUtils.isEmpty(chain)) { + throw new IllegalArgumentException("circuitBreaker.chain cannot be empty"); + } + ConfigUtils.validateInterval(checkPeriod, "circuitBreaker.checkPeriod"); + ConfigUtils.validateInterval(sleepWindow, "circuitBreaker.sleepWindow"); + ConfigUtils.validatePositive(requestCountAfterHalfOpen, "circuitBreaker.requestCountAfterHalfOpen"); + ConfigUtils.validatePositive(successCountAfterHalfOpen, "circuitBreaker.successCountAfterHalfOpen"); + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + CircuitBreakerConfig circuitBreakerConfig = (CircuitBreakerConfig) defaultObject; + if (null == enable) { + setEnable(circuitBreakerConfig.isEnable()); + } + if (CollectionUtils.isEmpty(chain)) { + setChain(circuitBreakerConfig.getChain()); + } + if (null == checkPeriod) { + setCheckPeriod(circuitBreakerConfig.getCheckPeriod()); + } + if (null == sleepWindow) { + setSleepWindow(circuitBreakerConfig.getSleepWindow()); + } + if (null == requestCountAfterHalfOpen) { + setRequestCountAfterHalfOpen(circuitBreakerConfig.getRequestCountAfterHalfOpen()); + } + if (null == successCountAfterHalfOpen) { + setSuccessCountAfterHalfOpen(circuitBreakerConfig.getSuccessCountAfterHalfOpen()); + } + if (enable) { + setDefaultPluginConfig(circuitBreakerConfig); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "CircuitBreakerConfigImpl{" + + "enable=" + enable + + ", chain=" + chain + + ", checkPeriod=" + checkPeriod + + ", sleepWindow=" + sleepWindow + + ", requestCountAfterHalfOpen=" + requestCountAfterHalfOpen + + ", successCountAfterHalfOpen=" + successCountAfterHalfOpen + + "} " + super.toString(); + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ConsumerConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ConsumerConfigImpl.java new file mode 100644 index 000000000..8907e31bb --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ConsumerConfigImpl.java @@ -0,0 +1,123 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.consumer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.consumer.ConsumerConfig; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 调用者配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class ConsumerConfigImpl implements ConsumerConfig { + + @JsonProperty + private LocalCacheConfigImpl localCache; + + @JsonProperty + private ServiceRouterConfigImpl serviceRouter; + + @JsonProperty + private LoadBalanceConfigImpl loadbalancer; + + @JsonProperty + private CircuitBreakerConfigImpl circuitBreaker; + + @JsonProperty + private OutlierDetectionConfigImpl outlierDetection; + + @Override + public LocalCacheConfigImpl getLocalCache() { + return localCache; + } + + @Override + public ServiceRouterConfigImpl getServiceRouter() { + return serviceRouter; + } + + @Override + public LoadBalanceConfigImpl getLoadbalancer() { + return loadbalancer; + } + + public CircuitBreakerConfigImpl getCircuitBreaker() { + return circuitBreaker; + } + + @Override + public OutlierDetectionConfigImpl getOutlierDetection() { + return outlierDetection; + } + + @Override + public void verify() { + ConfigUtils.validateNull(localCache, "localCache"); + ConfigUtils.validateNull(serviceRouter, "serviceRouter"); + ConfigUtils.validateNull(loadbalancer, "loadbalancer"); + ConfigUtils.validateNull(circuitBreaker, "circuitBreaker"); + ConfigUtils.validateNull(outlierDetection, "outlierDetection"); + localCache.verify(); + serviceRouter.verify(); + loadbalancer.verify(); + circuitBreaker.verify(); + outlierDetection.verify(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null == localCache) { + localCache = new LocalCacheConfigImpl(); + } + if (null == serviceRouter) { + serviceRouter = new ServiceRouterConfigImpl(); + } + if (null == loadbalancer) { + loadbalancer = new LoadBalanceConfigImpl(); + } + if (null == circuitBreaker) { + circuitBreaker = new CircuitBreakerConfigImpl(); + } + if (null == outlierDetection) { + outlierDetection = new OutlierDetectionConfigImpl(); + } + if (null != defaultObject) { + ConsumerConfig consumerConfig = (ConsumerConfig) defaultObject; + localCache.setDefault(consumerConfig.getLocalCache()); + serviceRouter.setDefault(consumerConfig.getServiceRouter()); + loadbalancer.setDefault(consumerConfig.getLoadbalancer()); + circuitBreaker.setDefault(consumerConfig.getCircuitBreaker()); + outlierDetection.setDefault(consumerConfig.getOutlierDetection()); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ConsumerConfigImpl{" + + "localCache=" + localCache + + ", serviceRouter=" + serviceRouter + + ", loadbalancer=" + loadbalancer + + ", circuitBreaker=" + circuitBreaker + + ", outlierDetection=" + outlierDetection + + '}'; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LoadBalanceConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LoadBalanceConfigImpl.java new file mode 100644 index 000000000..afca951df --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LoadBalanceConfigImpl.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.consumer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 负载均衡相关配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class LoadBalanceConfigImpl extends PluginConfigImpl implements LoadBalanceConfig { + + @JsonProperty + private String type; + + @Override + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public void verify() throws IllegalArgumentException { + ConfigUtils.validateString(type, "loadbalancer.type"); + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + LoadBalanceConfig loadBalanceConfig = (LoadBalanceConfig) defaultObject; + if (null == type) { + setType(loadBalanceConfig.getType()); + } + setDefaultPluginConfig(loadBalanceConfig); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "LoadBalanceConfigImpl{" + + "type='" + type + '\'' + + "} " + super.toString(); + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LocalCacheConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LocalCacheConfigImpl.java new file mode 100644 index 000000000..ea9d96771 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/LocalCacheConfigImpl.java @@ -0,0 +1,237 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.consumer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.consumer.LocalCacheConfig; +import com.tencent.polaris.api.config.verify.DefaultValues; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; + +/** + * 本地缓存相关配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class LocalCacheConfigImpl extends PluginConfigImpl implements LocalCacheConfig { + + @JsonProperty + private Boolean serviceExpireEnable; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long serviceExpireTime; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long serviceRefreshInterval; + + @JsonProperty + private Boolean persistEnable; + + @JsonProperty + private String persistDir; + + @JsonProperty + private String type; + + @JsonProperty + private Integer persistMaxWriteRetry; + + @JsonProperty + private Integer persistMaxReadRetry; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long persistRetryInterval; + + @Override + public String getPersistDir() { + return persistDir; + } + + public void setPersistDir(String persistDir) { + this.persistDir = persistDir; + } + + @Override + public String getType() { + return type; + } + + @Override + public boolean isPersistEnable() { + if (null == persistEnable) { + return false; + } + return persistEnable; + } + + public void setPersistEnable(Boolean persistEnable) { + this.persistEnable = persistEnable; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public int getPersistMaxWriteRetry() { + if (null == persistMaxWriteRetry) { + return 0; + } + return persistMaxWriteRetry; + } + + public void setPersistMaxWriteRetry(int persistMaxWriteRetry) { + this.persistMaxWriteRetry = persistMaxWriteRetry; + } + + @Override + public int getPersistMaxReadRetry() { + if (null == persistMaxReadRetry) { + return 0; + } + return persistMaxReadRetry; + } + + public void setPersistMaxReadRetry(int persistMaxReadRetry) { + this.persistMaxReadRetry = persistMaxReadRetry; + } + + @Override + public boolean isServiceExpireEnable() { + if (null == serviceExpireEnable) { + return false; + } + return serviceExpireEnable; + } + + public void setServiceExpireEnable(boolean serviceExpireEnable) { + this.serviceExpireEnable = serviceExpireEnable; + } + + @Override + public long getServiceExpireTime() { + if (null == serviceExpireTime) { + return 0; + } + return serviceExpireTime; + } + + public void setServiceExpireTime(long serviceExpireTime) { + this.serviceExpireTime = serviceExpireTime; + } + + @Override + public long getServiceRefreshInterval() { + if (null == serviceRefreshInterval) { + return 0; + } + return serviceRefreshInterval; + } + + public void setServiceRefreshInterval(long serviceRefreshInterval) { + this.serviceRefreshInterval = serviceRefreshInterval; + } + + @Override + public long getPersistRetryInterval() { + if (null == persistRetryInterval) { + return 0; + } + return persistRetryInterval; + } + + public void setPersistRetryInterval(long persistRetryInterval) { + this.persistRetryInterval = persistRetryInterval; + } + + @Override + public void verify() { + ConfigUtils.validateNull(serviceExpireEnable, "localCache.serviceExpireEnable"); + ConfigUtils.validateIntervalWithMin(serviceExpireTime, DefaultValues.MIN_SERVICE_EXPIRE_TIME_MS, + "localCache.serviceExpireTime"); + ConfigUtils.validateIntervalWithMin(serviceRefreshInterval, DefaultValues.MIN_SERVICE_REFRESH_INTERVAL_MS, + "localCache.serviceRefreshInterval"); + ConfigUtils.validateNull(persistEnable, "localCache.persistEnable"); + if (persistEnable) { + ConfigUtils.validateString(persistDir, "localCache.persistDir"); + } + ConfigUtils.validateString(type, "localCache.type"); + ConfigUtils.validateTimes(persistMaxWriteRetry, "localCache.persistMaxWriteRetry"); + ConfigUtils.validateTimes(persistMaxReadRetry, "localCache.persistMaxReadRetry"); + ConfigUtils.validateInterval(persistRetryInterval, "localCache.persistRetryInterval"); + verifyPluginConfig(); + } + + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + LocalCacheConfig localCacheConfig = (LocalCacheConfig) defaultObject; + if (null == serviceExpireEnable) { + setServiceExpireEnable(localCacheConfig.isServiceExpireEnable()); + } + if (null == serviceExpireTime) { + setServiceExpireTime(localCacheConfig.getServiceExpireTime()); + } + if (null == serviceRefreshInterval) { + setServiceRefreshInterval(localCacheConfig.getServiceRefreshInterval()); + } + if (null == persistEnable) { + setPersistEnable(localCacheConfig.isPersistEnable()); + } + if (null == persistDir) { + setPersistDir(localCacheConfig.getPersistDir()); + } + if (null == type) { + setType(localCacheConfig.getType()); + } + if (null == persistMaxWriteRetry) { + setPersistMaxWriteRetry(localCacheConfig.getPersistMaxWriteRetry()); + } + if (null == persistMaxReadRetry) { + setPersistMaxReadRetry(localCacheConfig.getPersistMaxReadRetry()); + } + if (null == persistRetryInterval) { + setPersistRetryInterval(localCacheConfig.getPersistRetryInterval()); + } + setDefaultPluginConfig(localCacheConfig); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "LocalCacheConfigImpl{" + + "serviceExpireEnable=" + serviceExpireEnable + + ", serviceExpireTime=" + serviceExpireTime + + ", serviceRefreshInterval=" + serviceRefreshInterval + + ", persistEnable=" + persistEnable + + ", persistDir='" + persistDir + '\'' + + ", type='" + type + '\'' + + ", persistMaxWriteRetry=" + persistMaxWriteRetry + + ", persistMaxReadRetry=" + persistMaxReadRetry + + ", persistRetryInterval=" + persistRetryInterval + + "} " + super.toString(); + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/OutlierDetectionConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/OutlierDetectionConfigImpl.java new file mode 100644 index 000000000..b893db663 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/OutlierDetectionConfigImpl.java @@ -0,0 +1,121 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.consumer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig; +import com.tencent.polaris.api.config.verify.DefaultValues; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; +import java.util.List; + +/** + * OutlierDetectionConfigImpl.java + * + * @author andrewshan + * @date 2019/9/18 + */ +public class OutlierDetectionConfigImpl extends PluginConfigImpl implements OutlierDetectionConfig { + + @JsonProperty + private When when; + + @JsonProperty + private List chain; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long checkPeriod; + + @Override + public When getWhen() { + return when; + } + + public void setWhen(When when) { + this.when = when; + } + + @Override + public List getChain() { + return chain; + } + + public void setChain(List chain) { + this.chain = chain; + } + + @Override + public long getCheckPeriod() { + if (null == checkPeriod) { + return 0; + } + return checkPeriod; + } + + public void setCheckPeriod(long checkPeriod) { + this.checkPeriod = checkPeriod; + } + + @Override + public void verify() { + ConfigUtils.validateNull(when, "outlierDetection.enable"); + if (when == When.never) { + return; + } + ConfigUtils.validateIntervalWithMin(checkPeriod, DefaultValues.MIN_TIMING_INTERVAL_MS, + "outlierDetection.checkPeriod"); + if (CollectionUtils.isEmpty(chain)) { + throw new IllegalArgumentException("outlierDetection.chain cannot be empty"); + } + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + OutlierDetectionConfig outlierDetectionConfig = (OutlierDetectionConfig) defaultObject; + if (null == when) { + setWhen(outlierDetectionConfig.getWhen()); + } + if (null == checkPeriod) { + setCheckPeriod(outlierDetectionConfig.getCheckPeriod()); + } + if (CollectionUtils.isEmpty(chain)) { + setChain(outlierDetectionConfig.getChain()); + } + if (when != When.never) { + setDefaultPluginConfig(outlierDetectionConfig); + } + } + + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "OutlierDetectionConfigImpl{" + + "when=" + when + + ", chain=" + chain + + ", checkPeriod=" + checkPeriod + + "} " + super.toString(); + } +} \ No newline at end of file diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ServiceRouterConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ServiceRouterConfigImpl.java new file mode 100644 index 000000000..b6260096f --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/consumer/ServiceRouterConfigImpl.java @@ -0,0 +1,106 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.factory.config.consumer; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import java.util.List; + +/** + * 服务路由相关配置项 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class ServiceRouterConfigImpl extends PluginConfigImpl implements ServiceRouterConfig { + + @JsonProperty + private List afterChain; + + @JsonProperty + private List beforeChain; + + @JsonProperty + private List chain; + + @Override + public List getChain() { + return chain; + } + + public void setChain(List chain) { + this.chain = chain; + } + + @Override + public List getAfterChain() { + return afterChain; + } + + public void setAfterChain(List afterChain) { + this.afterChain = afterChain; + } + + @Override + public List getBeforeChain() { + return beforeChain; + } + + public void setBeforeChain(List beforeChain) { + this.beforeChain = beforeChain; + } + + @Override + public void verify() { + if (CollectionUtils.isEmpty(beforeChain)) { + throw new IllegalArgumentException("beforeChain cannot be empty"); + } + if (CollectionUtils.isEmpty(afterChain)) { + throw new IllegalArgumentException("afterChain cannot be empty"); + } + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + ServiceRouterConfig serviceRouterConfig = (ServiceRouterConfig) defaultObject; + if (CollectionUtils.isEmpty(beforeChain)) { + setBeforeChain(serviceRouterConfig.getBeforeChain()); + } + if (CollectionUtils.isEmpty(chain)) { + setChain(serviceRouterConfig.getChain()); + } + if (CollectionUtils.isEmpty(afterChain)) { + setAfterChain(serviceRouterConfig.getAfterChain()); + } + setDefaultPluginConfig(serviceRouterConfig); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceRouterConfigImpl{" + + "afterChain=" + afterChain + + ", beforeChain=" + beforeChain + + ", chain=" + chain + + "} " + super.toString(); + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/APIConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/APIConfigImpl.java new file mode 100644 index 000000000..038e506e4 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/APIConfigImpl.java @@ -0,0 +1,167 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; + +/** + * api相关的配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class APIConfigImpl implements APIConfig { + + @JsonProperty + private Integer maxRetryTimes; + + @JsonProperty + private String bindIf; + + @JsonProperty + private String bindIP; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long timeout; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long reportInterval; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long retryInterval; + + + @Override + public int getMaxRetryTimes() { + if (null == maxRetryTimes) { + return 0; + } + return maxRetryTimes; + } + + public void setMaxRetryTimes(int maxRetryTimes) { + this.maxRetryTimes = maxRetryTimes; + } + + @Override + public String getBindIf() { + return bindIf; + } + + @Override + public String getBindIP() { + return bindIP; + } + + public void setBindIP(String bindIP) { + this.bindIP = bindIP; + } + + public void setBindIf(String bindIf) { + this.bindIf = bindIf; + } + + @Override + public long getTimeout() { + if (null == timeout) { + return 0; + } + return timeout; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + @Override + public long getReportInterval() { + if (null == reportInterval) { + return 0; + } + return reportInterval; + } + + public void setReportInterval(long reportInterval) { + this.reportInterval = reportInterval; + } + + @Override + public long getRetryInterval() { + if (null == retryInterval) { + return 0; + } + return retryInterval; + } + + public void setRetryInterval(long retryInterval) { + this.retryInterval = retryInterval; + } + + @Override + public void verify() { + ConfigUtils.validateTimes(maxRetryTimes, "api.maxRetryTimes"); + ConfigUtils.validateInterval(timeout, "api.timeout"); + ConfigUtils.validateInterval(reportInterval, "api.reportInterval"); + ConfigUtils.validateInterval(retryInterval, "api.retryInterval"); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + APIConfig apiConfig = (APIConfig) defaultObject; + if (null == maxRetryTimes) { + setMaxRetryTimes(apiConfig.getMaxRetryTimes()); + } + if (null == bindIf) { + setBindIf(apiConfig.getBindIf()); + } + if (null == bindIP) { + setBindIP(apiConfig.getBindIP()); + } + if (null == timeout) { + setTimeout(apiConfig.getTimeout()); + } + if (null == reportInterval) { + setReportInterval(apiConfig.getReportInterval()); + } + if (null == retryInterval) { + setRetryInterval(apiConfig.getRetryInterval()); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "APIConfigImpl{" + + "maxRetryTimes=" + maxRetryTimes + + ", bindIf='" + bindIf + '\'' + + ", bindIP='" + bindIP + '\'' + + ", timeout=" + timeout + + ", reportInterval=" + reportInterval + + ", retryInterval=" + retryInterval + + '}'; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ClusterConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ClusterConfigImpl.java new file mode 100644 index 000000000..44f3a32b3 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ClusterConfigImpl.java @@ -0,0 +1,159 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.global.ClusterConfig; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; +import java.util.List; + +public class ClusterConfigImpl implements ClusterConfig { + + @JsonProperty + private String namespace; + + @JsonProperty + private String service; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long refreshInterval; + + @JsonProperty + private Boolean sameAsBuiltin; + + @JsonProperty + private List routers; + + @JsonProperty + private String lbPolicy; + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getService() { + return service; + } + + @Override + public long getRefreshInterval() { + if (null == refreshInterval) { + return 0; + } + return refreshInterval; + } + + public void setRefreshInterval(long refreshInterval) { + this.refreshInterval = refreshInterval; + } + + @Override + public List getRouters() { + return routers; + } + + public void setRouters(List routers) { + this.routers = routers; + } + + @Override + public String getLbPolicy() { + return lbPolicy; + } + + public void setLbPolicy(String lbPolicy) { + this.lbPolicy = lbPolicy; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public boolean isSameAsBuiltin() { + if (null == sameAsBuiltin) { + return false; + } + return sameAsBuiltin; + } + + public void setSameAsBuiltin(boolean sameAsBuiltin) { + this.sameAsBuiltin = sameAsBuiltin; + } + + @Override + public void verify() { + ConfigUtils.validateNull(sameAsBuiltin, "sameAsBuiltin"); + ConfigUtils.validateString(lbPolicy, "lbPolicy"); + if (!sameAsBuiltin) { + ConfigUtils.validateString(namespace, "namespace"); + ConfigUtils.validateString(service, "service"); + if (CollectionUtils.isEmpty(routers)) { + throw new IllegalArgumentException("routers should be not empty"); + } + } + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + ClusterConfig clusterConfig = (ClusterConfig) defaultObject; + if (null == namespace) { + setNamespace(clusterConfig.getNamespace()); + } + if (null == service) { + setService(clusterConfig.getService()); + } + if (null == refreshInterval) { + setRefreshInterval(clusterConfig.getRefreshInterval()); + } + if (null == sameAsBuiltin) { + setSameAsBuiltin(clusterConfig.isSameAsBuiltin()); + } + if (CollectionUtils.isEmpty(routers)) { + setRouters(clusterConfig.getRouters()); + } + if (null == lbPolicy) { + setLbPolicy(clusterConfig.getLbPolicy()); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ClusterConfigImpl{" + + "namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + ", refreshInterval=" + refreshInterval + + ", sameAsBuiltin=" + sameAsBuiltin + + ", routers=" + routers + + ", lbPolicy='" + lbPolicy + '\'' + + '}'; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/FlowCacheConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/FlowCacheConfigImpl.java new file mode 100644 index 000000000..f42bda01c --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/FlowCacheConfigImpl.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.global.FlowCacheConfig; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; + +public class FlowCacheConfigImpl implements FlowCacheConfig { + + @JsonProperty + private Boolean enable; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long expireInterval; + + @JsonProperty + private String name; + + @Override + public boolean isEnable() { + if (null == enable) { + return false; + } + return enable; + } + + @Override + public String getName() { + return name; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public long getExpireInterval() { + if (null == expireInterval) { + return 0; + } + return expireInterval; + } + + public void setExpireInterval(long expireInterval) { + this.expireInterval = expireInterval; + } + + @Override + public void verify() { + ConfigUtils.validateNull(enable, "flowCache.enable"); + ConfigUtils.validateInterval(expireInterval, "flowCache.expireInterval"); + ConfigUtils.validateString(name, "flowCache.name"); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + FlowCacheConfig flowCacheConfig = (FlowCacheConfig) defaultObject; + if (null == enable) { + setEnable(flowCacheConfig.isEnable()); + } + if (null == name) { + setName(flowCacheConfig.getName()); + } + if (null == expireInterval) { + setExpireInterval(flowCacheConfig.getExpireInterval()); + } + } + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/GlobalConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/GlobalConfigImpl.java new file mode 100644 index 000000000..3d6b545c0 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/GlobalConfigImpl.java @@ -0,0 +1,114 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.global.GlobalConfig; +import com.tencent.polaris.api.config.global.StatReporterConfig; +import com.tencent.polaris.api.config.global.SystemConfig; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 全局配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class GlobalConfigImpl implements GlobalConfig { + + @JsonProperty + private SystemConfigImpl system; + + @JsonProperty + private APIConfigImpl api; + + @JsonProperty + private ServerConnectorConfigImpl serverConnector; + + @JsonProperty + private StatReporterConfigImpl statReporter; + + @Override + public SystemConfigImpl getSystem() { + return system; + } + + @Override + public APIConfigImpl getAPI() { + return api; + } + + @Override + public ServerConnectorConfigImpl getServerConnector() { + return serverConnector; + } + + @Override + public StatReporterConfigImpl getStatReporter() { + return statReporter; + } + + public void setServerConnector(ServerConnectorConfigImpl serverConnector) { + this.serverConnector = serverConnector; + } + + @Override + public void verify() { + ConfigUtils.validateNull(system, "system"); + ConfigUtils.validateNull(api, "api"); + ConfigUtils.validateNull(serverConnector, "serverConnector"); + ConfigUtils.validateNull(statReporter, "statReporter"); + system.verify(); + api.verify(); + serverConnector.verify(); + statReporter.verify(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null == system) { + system = new SystemConfigImpl(); + } + if (null == api) { + api = new APIConfigImpl(); + } + if (null == serverConnector) { + serverConnector = new ServerConnectorConfigImpl(); + } + if (null == statReporter) { + statReporter = new StatReporterConfigImpl(); + } + if (null != defaultObject) { + GlobalConfig globalConfig = (GlobalConfig) defaultObject; + system.setDefault(globalConfig.getSystem()); + api.setDefault(globalConfig.getAPI()); + serverConnector.setDefault(globalConfig.getServerConnector()); + statReporter.setDefault(globalConfig.getStatReporter()); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "GlobalConfigImpl{" + + "system=" + system + + "api=" + api + + ", serverConnector=" + serverConnector + + '}'; + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ServerConnectorConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ServerConnectorConfigImpl.java new file mode 100644 index 000000000..d4b5ab9d7 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/ServerConnectorConfigImpl.java @@ -0,0 +1,207 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.tencent.polaris.api.config.global.ServerConnectorConfig; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; +import com.tencent.polaris.factory.util.TimeStrJsonDeserializer; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 与名字服务服务端的连接配置 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class ServerConnectorConfigImpl extends PluginConfigImpl implements ServerConnectorConfig { + + private static final Pattern addressPattern = Pattern.compile("(.*)((?::))((?:[0-9]+))$"); + + @JsonProperty + private List addresses; + + @JsonProperty + private String protocol; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long connectTimeout; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long messageTimeout; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long serverSwitchInterval; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long connectionIdleTimeout; + + @JsonProperty + @JsonDeserialize(using = TimeStrJsonDeserializer.class) + private Long reconnectInterval; + + @Override + + public List getAddresses() { + return addresses; + } + + public void setAddresses(List addresses) { + this.addresses = addresses; + } + + @Override + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + @Override + public long getConnectTimeout() { + if (null == connectTimeout) { + return 0; + } + return connectTimeout; + } + + public void setConnectTimeout(Long connectTimeout) { + this.connectTimeout = connectTimeout; + } + + @Override + public long getMessageTimeout() { + if (null == messageTimeout) { + return 0; + } + return messageTimeout; + } + + public void setMessageTimeout(Long messageTimeout) { + this.messageTimeout = messageTimeout; + } + + @Override + public long getServerSwitchInterval() { + if (null == serverSwitchInterval) { + return 0; + } + return serverSwitchInterval; + } + + public void setServerSwitchInterval(Long serverSwitchInterval) { + this.serverSwitchInterval = serverSwitchInterval; + } + + @Override + public long getConnectionIdleTimeout() { + if (null == connectionIdleTimeout) { + return 0; + } + return connectionIdleTimeout; + } + + public void setConnectionIdleTimeout(Long connectionIdleTimeout) { + this.connectionIdleTimeout = connectionIdleTimeout; + } + + @Override + public long getReconnectInterval() { + if (null == reconnectInterval) { + return 0; + } + return reconnectInterval; + } + + public void setReconnectInterval(Long reconnectInterval) { + this.reconnectInterval = reconnectInterval; + } + + @Override + public void verify() { + if (CollectionUtils.isEmpty(addresses)) { + throw new IllegalArgumentException("addresses must not be empty"); + } + for (String address : addresses) { + boolean matched = addressPattern.matcher(address).find(); + if (!matched) { + throw new IllegalArgumentException(String.format("address %s is invalid", address)); + } + } + ConfigUtils.validateString(protocol, "serverConnector.protocol"); + ConfigUtils.validateInterval(connectTimeout, "serverConnector.connectTimeout"); + ConfigUtils.validateInterval(messageTimeout, "serverConnector.messageTimeout"); + ConfigUtils.validateInterval(serverSwitchInterval, "serverConnector.serverSwitchInterval"); + ConfigUtils.validateInterval(connectionIdleTimeout, "serverConnector.connectionIdleTimeout"); + ConfigUtils.validateInterval(reconnectInterval, "serverConnector.reconnectInterval"); + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + ServerConnectorConfig serverConnectorConfig = (ServerConnectorConfig) defaultObject; + if (null == addresses) { + setAddresses(serverConnectorConfig.getAddresses()); + } + if (null == protocol) { + setProtocol(serverConnectorConfig.getProtocol()); + } + if (null == connectTimeout) { + setConnectTimeout(serverConnectorConfig.getConnectTimeout()); + } + if (null == messageTimeout) { + setMessageTimeout(serverConnectorConfig.getMessageTimeout()); + } + if (null == serverSwitchInterval) { + setServerSwitchInterval(serverConnectorConfig.getServerSwitchInterval()); + } + if (null == connectionIdleTimeout) { + setConnectionIdleTimeout(serverConnectorConfig.getConnectionIdleTimeout()); + } + if (null == reconnectInterval) { + setReconnectInterval(serverConnectorConfig.getReconnectInterval()); + } + setDefaultPluginConfig(serverConnectorConfig); + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServerConnectorConfigImpl{" + + "addresses=" + addresses + + ", protocol='" + protocol + '\'' + + ", connectTimeout=" + connectTimeout + + ", messageTimeout=" + messageTimeout + + ", serverSwitchInterval=" + serverSwitchInterval + + ", connectionIdleTimeout=" + connectionIdleTimeout + + ", reconnectInterval=" + reconnectInterval + + "} " + super.toString(); + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/StatReporterConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/StatReporterConfigImpl.java new file mode 100644 index 000000000..cf5d8b56f --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/StatReporterConfigImpl.java @@ -0,0 +1,77 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.global.StatReporterConfig; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; +import java.util.List; + +public class StatReporterConfigImpl extends PluginConfigImpl implements StatReporterConfig { + + @JsonProperty + private Boolean enable; + + @JsonProperty + private List chain; + + @Override + public boolean isEnable() { + if (null == enable) { + return false; + } + return enable; + } + + @Override + public List getChain() { + return chain; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public void setChain(List chain) { + this.chain = chain; + } + + @Override + public void verify() { + ConfigUtils.validateNull(enable, "statReporter.enable"); + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + StatReporterConfig statReporterConfig = (StatReporterConfig) defaultObject; + if (null == enable) { + setEnable(statReporterConfig.isEnable()); + } + if (CollectionUtils.isEmpty(chain)) { + setChain(statReporterConfig.getChain()); + } + if (enable) { + setDefaultPluginConfig(statReporterConfig); + } + } + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/SystemConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/SystemConfigImpl.java new file mode 100644 index 000000000..a93b68739 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/global/SystemConfigImpl.java @@ -0,0 +1,132 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.global; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.global.ClusterConfig; +import com.tencent.polaris.api.config.global.FlowCacheConfig; +import com.tencent.polaris.api.config.global.SystemConfig; +import com.tencent.polaris.factory.util.ConfigUtils; +import java.util.Collections; +import java.util.Map; + +/** + * api相关的配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class SystemConfigImpl implements SystemConfig { + + @JsonProperty + private FlowCacheConfigImpl flowCache; + + @JsonProperty + private ClusterConfigImpl discoverCluster; + + @JsonProperty + private ClusterConfigImpl healthCheckCluster; + + @JsonProperty + private ClusterConfigImpl monitorCluster; + + @JsonProperty + private Map variables; + + @Override + public ClusterConfigImpl getDiscoverCluster() { + return discoverCluster; + } + + @Override + public ClusterConfigImpl getHealthCheckCluster() { + return healthCheckCluster; + } + + @Override + public ClusterConfigImpl getMonitorCluster() { + return monitorCluster; + } + + @Override + public FlowCacheConfigImpl getFlowCache() { + return flowCache; + } + + @Override + public Map getVariables() { + if (null == variables) { + return Collections.emptyMap(); + } + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } + + @Override + public void verify() { + ConfigUtils.validateNull(flowCache, "system.flowCache"); + ConfigUtils.validateNull(discoverCluster, "system.discoverCluster"); + ConfigUtils.validateNull(healthCheckCluster, "system.healthCheckCluster"); + ConfigUtils.validateNull(monitorCluster, "system.monitorCluster"); + flowCache.verify(); + discoverCluster.verify(); + healthCheckCluster.verify(); + monitorCluster.verify(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null == discoverCluster) { + discoverCluster = new ClusterConfigImpl(); + } + if (null == healthCheckCluster) { + healthCheckCluster = new ClusterConfigImpl(); + } + if (null == monitorCluster) { + monitorCluster = new ClusterConfigImpl(); + } + if (null == flowCache) { + flowCache = new FlowCacheConfigImpl(); + } + if (null != defaultObject) { + SystemConfig systemConfig = (SystemConfig) defaultObject; + discoverCluster.setDefault(systemConfig.getDiscoverCluster()); + healthCheckCluster.setDefault(systemConfig.getHealthCheckCluster()); + monitorCluster.setDefault(systemConfig.getMonitorCluster()); + flowCache.setDefault(systemConfig.getFlowCache()); + if (null == variables) { + setVariables(systemConfig.getVariables()); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "SystemConfigImpl{" + + ", discoverCluster=" + discoverCluster + + ", healthCheckCluster=" + healthCheckCluster + + ", monitorCluster=" + monitorCluster + + ", variables=" + variables + + '}'; + } +} + diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/plugin/PluginConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/plugin/PluginConfigImpl.java new file mode 100644 index 000000000..6adc82ee0 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/plugin/PluginConfigImpl.java @@ -0,0 +1,213 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.plugin; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.tencent.polaris.api.config.plugin.PluginConfig; +import com.tencent.polaris.api.config.plugin.PluginConfigProvider; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.factory.util.ConfigUtils; +import java.util.HashMap; +import java.util.Map; +import java.util.ServiceLoader; + +/** + * 插件配置对象解析 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class PluginConfigImpl implements PluginConfig { + + @JsonProperty + private final Map> plugin = new HashMap<>(); + + private final Object lock = new Object(); + + private static final Map> pluginConfigClazz = new HashMap<>(); + + static { + ServiceLoader providers = ServiceLoader.load(PluginConfigProvider.class); + for (PluginConfigProvider provider : providers) { + pluginConfigClazz.put(provider.getName(), provider.getPluginConfigClazz()); + } + } + + /** + * 用于转换插件配置逻辑 + */ + private final ObjectMapper mapper = new ObjectMapper(); + + public PluginConfigImpl() { + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + } + + @Override + public T getPluginConfig(String pluginName, Class clazz) throws PolarisException { + synchronized (lock) { + Map properties = plugin.get(pluginName); + if (null == properties) { + Verifier config = getConfigByName(clazz); + properties = mutableSetPluginConfig(pluginName, config); + } + T result; + try { + result = mapper.convertValue(properties, clazz); + } catch (IllegalArgumentException e) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("fail to deserialize properties %s to clazz %s for plugin %s", properties, + clazz.getCanonicalName(), pluginName), e); + } + return result; + } + } + + public void setDefaultPluginConfig(PluginConfig pluginConfig) { + if (null == pluginConfig) { + return; + } + Map pluginConfigs = pluginConfig.getPluginConfigs(); + if (MapUtils.isEmpty(pluginConfigs)) { + return; + } + for (Map.Entry entry : pluginConfigs.entrySet()) { + String pluginName = entry.getKey(); + Verifier defaultObject = entry.getValue(); + Class pluginConfigClazz = defaultObject.getClass(); + Verifier existConfig = getPluginConfig(pluginName, pluginConfigClazz); + existConfig.setDefault(defaultObject); + setPluginConfig(pluginName, existConfig); + } + } + + public void verifyPluginConfig() { + Map pluginConfigs = getPluginConfigs(); + if (MapUtils.isEmpty(pluginConfigs)) { + return; + } + for (Verifier verifier : pluginConfigs.values()) { + verifier.verify(); + } + } + + @Override + public Map getPluginConfigs() throws PolarisException { + synchronized (lock) { + Map values = new HashMap<>(); + if (plugin.size() == 0) { + return values; + } + for (Map.Entry> entry : plugin.entrySet()) { + Map properties = entry.getValue(); + if (MapUtils.isEmpty(properties)) { + continue; + } + String pluginName = entry.getKey(); + Class clazz = PluginConfigImpl.pluginConfigClazz.get(pluginName); + if (null == clazz) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("unknown plugin config type for plugin %s", pluginName)); + } + try { + Verifier result = mapper.convertValue(properties, clazz); + values.put(pluginName, result); + } catch (IllegalArgumentException e) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("fail to deserialize properties %s to clazz %s for plugin %s", properties, + clazz.getCanonicalName(), pluginName), e); + } + } + return values; + } + } + + private Map mutableSetPluginConfig(String pluginName, Verifier config) throws PolarisException { + Map configMap; + try { + configMap = ConfigUtils.objectToMap(config); + } catch (Exception e) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("fail to marshal plugin config for %s", pluginName), e); + } + plugin.put(pluginName, configMap); + return configMap; + } + + /** + * 设置特定插件配置 + * + * @param pluginName 插件名 + * @param config 插件配置对象 + * @throws PolarisException 设置过程出现的异常 + */ + public void setPluginConfig(String pluginName, Verifier config) throws PolarisException { + synchronized (lock) { + if (null == config) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("config is null, plugin name %s", pluginName)); + } + mutableSetPluginConfig(pluginName, config); + } + } + + /** + * 通过插件名,初始化配置 + * + * @return 配置对象 + */ + private static Verifier getConfigByName(Class pluginConfigClazz) { +// Class pluginConfigClazz = PluginConfigImpl.pluginConfigClazz.get(pluginName); +// if (null == pluginConfigClazz) { +// return null; +// } + try { + return pluginConfigClazz.newInstance(); + } catch (Exception e) { + throw new RuntimeException(pluginConfigClazz.getCanonicalName() + " create config failed.", e); + } + } + +// protected void setDefault(String routeName) { +// Verifier defaultConfig = getConfigByName(routeName); +// if (null != defaultConfig) { +// overwriteDefaultConfigWithUserConfig(routeName, defaultConfig); +// } +// } +// +// private void overwriteDefaultConfigWithUserConfig(String routerName, +// T defaultRouterConfig) { +// synchronized (lock) { +// Map userConfig = plugin.get(routerName); +// if (userConfig == null) { +// userConfig = new HashMap<>(); +// } +// //使用默认的转成map是为了方便和用户配置的merge +// Map defaultConfigMap = mapper.convertValue(defaultRouterConfig, Map.class); +// //使用用户的配置覆盖默认 +// defaultConfigMap.putAll(userConfig); +// plugin.put(routerName, defaultConfigMap); +// } +// } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java new file mode 100644 index 000000000..ddcabbf34 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/ProviderConfigImpl.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.provider; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.provider.ProviderConfig; +import com.tencent.polaris.api.config.provider.RegisterConfig; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 被调端配置对象 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class ProviderConfigImpl implements ProviderConfig { + + @JsonProperty + private RateLimitConfigImpl rateLimit; + + @JsonProperty + private RegisterConfig register; + + @Override + public RateLimitConfigImpl getRateLimit() { + return rateLimit; + } + + @Override + public RegisterConfig getRegister() { + return register; + } + + @Override + public void verify() { + ConfigUtils.validateNull(rateLimit, "rateLimitConfig"); + rateLimit.verify(); + register.verify(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null == rateLimit) { + rateLimit = new RateLimitConfigImpl(); + } + if (null == register) { + register = new RegisterConfigImpl(); + } + if (null != defaultObject) { + ProviderConfig providerConfig = (ProviderConfig) defaultObject; + rateLimit.setDefault(providerConfig.getRateLimit()); + register.setDefault(providerConfig.getRegister()); + } + + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RateLimitConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RateLimitConfigImpl.java new file mode 100644 index 000000000..a6275b05d --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RateLimitConfigImpl.java @@ -0,0 +1,96 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.provider; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.provider.RateLimitConfig; +import com.tencent.polaris.factory.config.plugin.PluginConfigImpl; +import com.tencent.polaris.factory.util.ConfigUtils; + +public class RateLimitConfigImpl extends PluginConfigImpl implements RateLimitConfig { + + @JsonProperty + private Boolean enable; + + @JsonProperty + private Integer maxWindowCount; + + @JsonProperty + private Fallback fallbackOnExceedWindowCount; + + public boolean isEnable() { + if (null == enable) { + return false; + } + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + @Override + public int getMaxWindowCount() { + if (null == maxWindowCount) { + return 0; + } + return maxWindowCount; + } + + public void setMaxWindowCount(int maxWindowCount) { + this.maxWindowCount = maxWindowCount; + } + + @Override + public Fallback getFallbackOnExceedWindowCount() { + return fallbackOnExceedWindowCount; + } + + public void setFallbackOnExceedWindowCount( + Fallback fallbackOnExceedWindowCount) { + this.fallbackOnExceedWindowCount = fallbackOnExceedWindowCount; + } + + @Override + public void verify() { + ConfigUtils.validateNull(enable, "rateLimit.enable"); + if (!enable) { + return; + } + ConfigUtils.validatePositive(maxWindowCount, "rateLimit.maxWindowCount"); + ConfigUtils.validateNull(fallbackOnExceedWindowCount, "rateLimit.fallbackOnExceedWindowCount"); + verifyPluginConfig(); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + RateLimitConfig rateLimitConfig = (RateLimitConfig) defaultObject; + if (null == enable) { + setEnable(rateLimitConfig.isEnable()); + } + if (null == maxWindowCount) { + setMaxWindowCount(rateLimitConfig.getMaxWindowCount()); + } + if (null == fallbackOnExceedWindowCount) { + setFallbackOnExceedWindowCount(rateLimitConfig.getFallbackOnExceedWindowCount()); + } + setDefaultPluginConfig(rateLimitConfig); + } + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RegisterConfigImpl.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RegisterConfigImpl.java new file mode 100644 index 000000000..7141c63c5 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/config/provider/RegisterConfigImpl.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.config.provider; + +import com.tencent.polaris.api.config.provider.RegisterConfig; + +public class RegisterConfigImpl implements RegisterConfig { + + private String namespace; + + private String service; + + @Override + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Override + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public void verify() { + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + RegisterConfig registerConfig = (RegisterConfig) defaultObject; + setNamespace(registerConfig.getNamespace()); + setService(registerConfig.getService()); + } + } +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/ConfigUtils.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/ConfigUtils.java new file mode 100644 index 000000000..5394c98b0 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/ConfigUtils.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.tencent.polaris.api.utils.StringUtils; +import java.util.Map; + +public class ConfigUtils { + + public static Map objectToMap(Object obj) { + if (obj == null) { + return null; + } + ObjectMapper mapper = new ObjectMapper(); + return mapper.convertValue(obj, Map.class); + } + + public static void validateInterval(Long interval, String name) { + if (null == interval || interval == 0) { + throw new IllegalArgumentException(name + " must not be empty or 0"); + } + } + + public static void validateTimes(Integer times, String name) { + if (null == times || times < 0) { + throw new IllegalArgumentException(name + " must not be empty or 0"); + } + } + + public static void validateIntervalWithMin(Long interval, long minInterval, String name) { + if (null == interval || interval == 0 || interval < minInterval) { + throw new IllegalArgumentException( + name + " must not be empty or 0, and must be greater than " + minInterval); + } + } + + public static void validateString(String value, String name) { + if (StringUtils.isBlank(value)) { + throw new IllegalArgumentException(name + " must not be empty"); + } + } + + public static void validateNull(Object value, String name) { + if (null == value) { + throw new IllegalArgumentException(name + " must not be empty"); + } + } + + public static void validatePositive(Integer value, String name) { + if (null == value || value <= 0) { + throw new IllegalArgumentException(name + " must not be positive"); + } + } + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/DurationUtils.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/DurationUtils.java new file mode 100644 index 000000000..d119a1c8d --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/DurationUtils.java @@ -0,0 +1,101 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.util; + +import com.tencent.polaris.api.utils.StringUtils; +import java.time.Duration; +import java.util.regex.Pattern; + +/** + * 解析时间格式的工具类 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class DurationUtils { + + private static final Pattern PATTERN_DIGITAL = + Pattern.compile("^[0-9]+$", + Pattern.CASE_INSENSITIVE); + + private static final Pattern PATTERN_SECOND = + Pattern.compile("^[0-9]+s$", + Pattern.CASE_INSENSITIVE); + + private static final Pattern PATTERN_HOUR = + Pattern.compile("^[0-9]+h$", + Pattern.CASE_INSENSITIVE); + + private static final Pattern PATTERN_MINUTE = + Pattern.compile("^[0-9]+m$", + Pattern.CASE_INSENSITIVE); + + private static final Pattern PATTERN_MILLIS = + Pattern.compile("^[0-9]+ms$", + Pattern.CASE_INSENSITIVE); + + /** + * 将字符串转换为毫秒值 + * + * @param value 字符串的时间间隔 + * @return long, 毫秒 + */ + public static long parseDurationMillis(String value) { + if (PATTERN_DIGITAL.matcher(value).matches()) { + //纯数字,直接返回毫秒 + return Long.parseLong(value); + } + if (PATTERN_SECOND.matcher(value).matches() + || PATTERN_HOUR.matcher(value).matches() + || PATTERN_MINUTE.matcher(value).matches()) { + return Duration.parse(String.format("pt%s", value)).toMillis(); + } + if (PATTERN_MILLIS.matcher(value).matches()) { + String digitalValue = value.substring(0, value.length() - 2); + return Long.parseLong(digitalValue); + } + return -1; + } + + /** + * 配置值时间格式统一转换逻辑 + * + * @param durationStr 时间格式字符串 + * @param label 配置标签 + * @param originalValue 原始值 + * @param defaultValue 默认值 + * @return 时间间隔, ms + * @throws IllegalArgumentException 格式错误抛出异常 + */ + public static long parseConfigDurationStr(String durationStr, String label, long originalValue, long defaultValue) + throws IllegalArgumentException { + if (originalValue > 0) { + return originalValue; + } + long timeDurationMs = defaultValue; + if (StringUtils.isNotBlank(durationStr)) { + timeDurationMs = parseDurationMillis(durationStr); + if (timeDurationMs <= 0) { + throw new IllegalArgumentException( + String.format("%s has invalid value %s", label, durationStr)); + } + } + return timeDurationMs; + } + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/IPV4Util.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/IPV4Util.java new file mode 100644 index 000000000..8d59a90c8 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/IPV4Util.java @@ -0,0 +1,35 @@ +package com.tencent.polaris.factory.util; + +/** + * IPV4工具类 + * + * @author starkwen + * @date 2020/11/20 10:51 上午 + */ +public class IPV4Util { + + // ip转数字 + public static int ipToInt(String ipAddress) { + int result = 0; + String[] ipAddressInArray = ipAddress.split("\\."); + for (int i = 3; i >= 0; i--) { + long ip = Long.parseLong(ipAddressInArray[3 - i]); + result |= ip << (i * 8); + } + return result; + } + + // 数字转ip + public static String intToIp(int ip) { + StringBuilder result = new StringBuilder(15); + for (int i = 0; i < 4; i++) { + result.insert(0, ip & 0xff); + if (i < 3) { + result.insert(0, '.'); + } + ip = ip >> 8; + } + return result.toString(); + } + +} diff --git a/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/TimeStrJsonDeserializer.java b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/TimeStrJsonDeserializer.java new file mode 100644 index 000000000..661c51013 --- /dev/null +++ b/polaris-common/polaris-config/src/main/java/com/tencent/polaris/factory/util/TimeStrJsonDeserializer.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.util; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import java.io.IOException; + +public class TimeStrJsonDeserializer extends StdDeserializer { + + public TimeStrJsonDeserializer() { + super(Long.class); + } + + @Override + public Long deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JsonProcessingException { + String currentName = jsonParser.getCurrentName(); + if (jsonParser.hasToken(JsonToken.VALUE_NUMBER_INT)) { + return Long.parseLong(jsonParser.getText()); + } else if (jsonParser.hasToken(JsonToken.VALUE_STRING)) { + String text = jsonParser.getText(); + return DurationUtils + .parseConfigDurationStr(text, currentName, 0, 0); + } else if (jsonParser.hasToken(JsonToken.VALUE_NUMBER_FLOAT)) { + double value = Double.parseDouble(jsonParser.getText()); + return (long) value; + } + return null; + } +} diff --git a/polaris-common/polaris-model/pom.xml b/polaris-common/polaris-model/pom.xml new file mode 100644 index 000000000..8f6aa8e04 --- /dev/null +++ b/polaris-common/polaris-model/pom.xml @@ -0,0 +1,71 @@ + + + + polaris-common + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-model + + + + com.tencent.nameservice + polaris-protobuf + ${project.version} + + + + + + + org.codehaus.mojo + buildnumber-maven-plugin + 1.4 + + + bn1 + + create-timestamp + + + + build.time + + yyyyMMddHHmm + + + + + + org.codehaus.mojo + templating-maven-plugin + 1.0.0 + + + filter-src + + filter-sources + + + + src/main/templates + + target/generated-sources/java/com/tencent/polaris/version + + + + + + + + \ No newline at end of file diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/control/Destroyable.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/control/Destroyable.java new file mode 100644 index 000000000..c36acafe8 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/control/Destroyable.java @@ -0,0 +1,76 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.control; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 所有可销毁的对象的基类型 + * + * @author andrewshan + * @date 2019/8/22 + */ +public abstract class Destroyable { + + private final AtomicBoolean destroyed = new AtomicBoolean(false); + + /** + * 子类去实现销毁逻辑 + */ + protected void doDestroy() { + + } + + public void destroy() { + if (destroyed.compareAndSet(false, true)) { + doDestroy(); + } + } + + /** + * 获取插件名字 + * + * @return String + */ + public String getName() { + return "Destroyable"; + } + + /** + * 是否已经销毁 + * + * @return boolean + */ + public boolean isDestroyed() { + return destroyed.get(); + } + + /** + * 检查是否已经销毁,已销毁则抛出异常 + * + * @throws PolarisException 已销毁返回异常 + */ + protected void checkDestroyed() throws PolarisException { + if (isDestroyed()) { + throw new PolarisException(ErrorCode.INVALID_STATE, + String.format("Plugin %s has been destroyed", getName())); + } + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ErrorCode.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ErrorCode.java new file mode 100644 index 000000000..a9679b83d --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ErrorCode.java @@ -0,0 +1,179 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.exception; + +/** + * 错误码 + * + * @author andrewshan + * @date 2019/8/20 + */ +public enum ErrorCode { + + /** + * 操作成功 + */ + Success(0), + + /** + * API参数非法的错误码 + */ + API_INVALID_ARGUMENT(1001), + + /** + * 配置非法的错误码 + */ + INVALID_CONFIG(1002), + + /** + * 获取插件失败 + */ + PLUGIN_ERROR(1003), + + /** + * API超时错误的错误码 + */ + API_TIMEOUT(1004), + + /** + * SDK已经destroy后,继续调API会出现的错误码 + */ + INVALID_STATE(1005), + + /** + * 连接server时,server返回已知错误信息 + */ + SERVER_USER_ERROR(1006), + + /** + * 连接server时所出现的未知网络异常 + */ + NETWORK_ERROR(1007), + + /** + * 服务熔断错误 + */ + CIRCUIT_BREAK_ERROR(1008), + + /** + * 实例信息有误,如服务权重信息为空 + */ + INSTANCE_INFO_ERROR(1009), + + /** + * 负载均衡时发现服务实例为空 + */ + INSTANCE_NOT_FOUND(1010), + + /** + * 规则非法 + */ + INVALID_RULE(1011), + + /** + * 服务规则路由出错 + */ + ROUTE_RULE_NOT_MATCH(1012), + + /** + * 服务规则路由出错 + */ + INVALID_RESPONSE(1013), + + /** + * 内部算法及系统错误 + */ + INTERNAL_ERROR(1014), + + /** + * 服务不存在 + */ + SERVICE_NOT_FOUND(1015), + + /** + * server返回500错误 + */ + SERVER_EXCEPTION(1016), + + /** + * 获取地域信息失败 + */ + LOCATION_NOT_FOUND(1017), + + /** + * 就近路由匹配出错 + */ + LOCATION_MISMATCH(1018), + + /** + * metadata路由匹配出错 + */ + METADATA_MISMATCH(1019), + + /** + * 内部错误:连续错误 + */ + CONNECT_ERROR(2001), + + /** + * 内部错误:服务端500错误 + */ + SERVER_ERROR(2002), + + RPC_ERROR(2003), + + RPC_TIMEOUT(2004), + + /** + * 内部错误:服务端返回应答非法 + */ + INVALID_SERVER_RESPONSE(2005), + + /** + * 内部错误:服务端返回非法请求 + */ + INVALID_REQUEST(2006), + + /** + * 内部错误:未鉴权操作 + */ + UNAUTHORIZED(2007), + + /** + * 内部错误:被限流 + */ + REQUEST_LIMIT(2008), + + /** + * 内部错误:CMDB信息找不到 + */ + CMDB_NOT_FOUND(2009), + + /** + * 内部错误:未知服务端异常 + */ + UNKNOWN_SERVER_ERROR(2100); + + int code; + + ErrorCode(int code) { + this.code = code; + } + + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/PolarisException.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/PolarisException.java new file mode 100644 index 000000000..e33c8ee3b --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/PolarisException.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.exception; + +/** + * 基础异常,通过SDK API所抛出的所有异常的基类型 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class PolarisException extends RuntimeException { + + private final ErrorCode code; + + public PolarisException(ErrorCode code) { + this.code = code; + } + + public PolarisException(ErrorCode code, String message) { + super(message); + this.code = code; + } + + public PolarisException(ErrorCode code, String message, Throwable cause) { + super(message, cause); + this.code = code; + } + + @Override + public String getMessage() { + StringBuilder builder = new StringBuilder(String.format("ERR-%d(%s), ", code.code, code.name())); + builder.append(super.getMessage()); + Throwable cause = getCause(); + if (null != cause) { + builder.append(", cause: ").append(cause.getMessage()); + } + return builder.toString(); + } + + public ErrorCode getCode() { + return code; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/RetriableException.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/RetriableException.java new file mode 100644 index 000000000..831e5a406 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/RetriableException.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.exception; + +/** + * 可重试异常,仅对于可重试的流程错误会抛出该异常 + * + * @author andrewshan + * @date 2019/8/20 + */ +public class RetriableException extends PolarisException { + + public RetriableException(ErrorCode code, String message) { + super(code, message); + } + + public RetriableException(ErrorCode code, String message, Throwable cause) { + super(code, message, cause); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerCodes.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerCodes.java new file mode 100644 index 000000000..efe4d5516 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerCodes.java @@ -0,0 +1,89 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.exception; + +/** + * Server端返回的错误码信息 + * + * @author andrewshan + * @date 2019/8/28 + */ +public interface ServerCodes { + + /** + * 代表请求执行成功 + */ + int EXECUTE_SUCCESS = 200000; + + /** + * 代表缓存数据未变更 + */ + int DATA_NO_CHANGE = 200001; + + /** + * 资源已经存在,无需重复注册 + */ + int EXISTED_RESOURCE = 400201; + + /** + * 资源不存在 + */ + int NOT_FOUND_RESOURCE = 400202; + + /** + * 服务不存在 + */ + int NOT_FOUND_SERVICE = 400301; + + /** + * 转换为http格式的返回码 + * + * @param code http code + * @return 返回码 + */ + static int toHttpCode(int code) { + return (code / 100000) * 100; + } + + /** + * 将server错误码转换为内部服务错误 + * + * @param code server错误码 + * @return 服务错误信息 + */ + static ErrorCode convertServerErrorToRpcError(int code) { + int typCode = code / 1000; + switch (typCode) { + case 200: + return ErrorCode.Success; + case 400: + return ErrorCode.INVALID_REQUEST; + case 401: + return ErrorCode.UNAUTHORIZED; + case 403: + return ErrorCode.REQUEST_LIMIT; + case 404: + return ErrorCode.CMDB_NOT_FOUND; + case 500: + return ErrorCode.SERVER_ERROR; + default: + return ErrorCode.UNKNOWN_SERVER_ERROR; + } + } + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerErrorResponseException.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerErrorResponseException.java new file mode 100644 index 000000000..52a795e39 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/exception/ServerErrorResponseException.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.exception; + +/** + * 服务不存在异常 + * + * @author andrewshan + * @date 2019/9/17 + */ +public class ServerErrorResponseException extends PolarisException { + + private final int serverCode; + + public static ServerErrorResponseException build(int serverCode, String errorInfo) { + if (serverCode == ServerCodes.NOT_FOUND_RESOURCE || serverCode == ServerCodes.NOT_FOUND_SERVICE) { + return new ServerErrorResponseException(ErrorCode.SERVICE_NOT_FOUND, serverCode, errorInfo); + } + int httpServerCode = ServerCodes.toHttpCode(serverCode); + if (httpServerCode == 500) { + return new ServerErrorResponseException(ErrorCode.SERVER_EXCEPTION, serverCode, errorInfo); + } + return new ServerErrorResponseException(ErrorCode.SERVER_USER_ERROR, serverCode, errorInfo); + } + + private ServerErrorResponseException(ErrorCode code, int serverCode, String errorInfo) { + super(code, errorInfo); + this.serverCode = serverCode; + } + + + public int getServerCode() { + return serverCode; + } +} \ No newline at end of file diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/CircuitBreakerStatus.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/CircuitBreakerStatus.java new file mode 100644 index 000000000..74fccbc11 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/CircuitBreakerStatus.java @@ -0,0 +1,109 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * 实例熔断状态及数据 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class CircuitBreakerStatus { + + /** + * 标识被哪个熔断器熔断 + */ + private final String circuitBreaker; + + /** + * 熔断器状态 + */ + private final Status status; + + /** + * 开始被熔断的时间 + */ + private final long startTimeMs; + + public CircuitBreakerStatus(String circuitBreaker, Status status, long startTimeMs, int maxRequestCount) { + this.circuitBreaker = circuitBreaker; + this.status = status; + this.startTimeMs = startTimeMs; + } + + public String getCircuitBreaker() { + return circuitBreaker; + } + + public Status getStatus() { + return status; + } + + public long getStartTimeMs() { + return startTimeMs; + } + + /** + * 是否可以继续分配请求 + * + * @return boolean + */ + public boolean isAvailable() { + if (status == Status.CLOSE) { + return true; + } else if (status == Status.OPEN) { + return false; + } + //TODO:判断是否已经到达半开最大请求放量阈值 + return true; + } + + @Override + public String toString() { + return "CircuitBreakerStatus{" + + + "circuitBreaker='" + circuitBreaker + '\'' + + + ", status=" + status + + + ", startTimeMs=" + startTimeMs + + + '}'; + } + + /** + * Circuit break status. + * + * @author andrewshan + * @date 2019/8/21 + */ + public enum Status { + /** + * 熔断器关闭,实例可提供服务 + */ + CLOSE, + /** + * 熔断器半开,实例仅提供有限的服务 + */ + HALF_OPEN, + /** + * 熔断器打开状态,实例不提供服务 + */ + OPEN + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java new file mode 100644 index 000000000..cac1a5598 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultInstance.java @@ -0,0 +1,251 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import com.tencent.polaris.api.utils.StringUtils; +import java.util.HashMap; +import java.util.Map; + +public class DefaultInstance implements Instance { + + private String namespace; + + private String service; + + private String revision; + + private final Map circuitBreakerStatuses = new HashMap<>(); + + private boolean healthy; + + private boolean isolated; + + private String protocol; + + private String id; + + private String host; + + private int port; + + private String version; + + private Map metadata; + + private boolean enableHealthCheck; + + private String region; + + private String zone; + + private String campus; + + private int priority; + + private int weight; + + private String logicSet; + + + @Override + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Override + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public String getRevision() { + return revision; + } + + public void setRevision(String revision) { + this.revision = revision; + } + + public Map getCircuitBreakerStatuses() { + return circuitBreakerStatuses; + } + + @Override + public boolean isHealthy() { + return healthy; + } + + public void setHealthy(boolean healthy) { + this.healthy = healthy; + } + + @Override + public boolean isIsolated() { + return isolated; + } + + public void setIsolated(boolean isolated) { + this.isolated = isolated; + } + + @Override + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + @Override + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + @Override + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + @Override + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + @Override + public boolean isEnableHealthCheck() { + return enableHealthCheck; + } + + public void setEnableHealthCheck(boolean enableHealthCheck) { + this.enableHealthCheck = enableHealthCheck; + } + + @Override + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + @Override + public String getZone() { + return zone; + } + + public void setZone(String zone) { + this.zone = zone; + } + + @Override + public String getCampus() { + return campus; + } + + public void setCampus(String campus) { + this.campus = campus; + } + + @Override + public int getPriority() { + return priority; + } + + public void setPriority(int priority) { + this.priority = priority; + } + + @Override + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + @Override + public String getLogicSet() { + return logicSet; + } + + public void setLogicSet(String logicSet) { + this.logicSet = logicSet; + } + + @Override + public CircuitBreakerStatus getCircuitBreakerStatus() { + return circuitBreakerStatuses.get(StatusDimension.EMPTY_DIMENSION); + } + + @Override + public CircuitBreakerStatus getCircuitBreakerStatus(StatusDimension statusDimension) { + return circuitBreakerStatuses.get(statusDimension); + } + + @Override + public int compareTo(Instance instance) { + String curHost = StringUtils.defaultString(this.getHost()); + String remoteHost = StringUtils.defaultString(instance.getHost()); + int result = curHost.compareTo(remoteHost); + if (result != 0) { + return result; + } + return this.getPort() - instance.getPort(); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceEventKeysProvider.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceEventKeysProvider.java new file mode 100644 index 000000000..832e8ef80 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DefaultServiceEventKeysProvider.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Set; + +public class DefaultServiceEventKeysProvider implements ServiceEventKeysProvider { + + private boolean useCache; + + private Set svcEventKeys; + + private ServiceEventKey svcEventKey; + + public void setSvcEventKeys(Set svcEventKeys) { + this.svcEventKeys = svcEventKeys; + } + + public void setSvcEventKey(ServiceEventKey svcEventKey) { + this.svcEventKey = svcEventKey; + } + + @Override + public Set getSvcEventKeys() { + return svcEventKeys; + } + + @Override + public ServiceEventKey getSvcEventKey() { + return svcEventKey; + } + + @Override + public boolean isUseCache() { + return useCache; + } + + public void setUseCache(boolean useCache) { + this.useCache = useCache; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "DefaultServiceEventKeysProvider{" + + "useCache=" + useCache + + ", svcEventKeys=" + svcEventKeys + + ", svcEventKey=" + svcEventKey + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DetectResult.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DetectResult.java new file mode 100644 index 000000000..dc6a39692 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/DetectResult.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Date; + +/** + * 健康探测结果 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class DetectResult { + + /** + * 探测类型,与插件名相同 + */ + private String detectType; + + /** + * 探测持续时间 + */ + private final long elapseTime; + + /** + * 上一次的探测时间 + */ + private final Date lastDetectTime; + + + /** + * 探测返回结果 + */ + private final RetStatus retStatus; + + /** + * 下一次探测时间 + */ + private final Date nextDetectTime; + + public DetectResult(RetStatus retStatus, long elapseTime, Date lastDetectTime, Date nextDetectTime) { + this.retStatus = retStatus; + this.elapseTime = elapseTime; + this.lastDetectTime = lastDetectTime; + this.nextDetectTime = nextDetectTime; + } + + public DetectResult(RetStatus retStatus) { + this.retStatus = retStatus; + this.elapseTime = 0; + this.lastDetectTime = new Date(); + this.nextDetectTime = new Date(); + } + + public String getDetectType() { + return detectType; + } + + public void setDetectType(String detectType) { + this.detectType = detectType; + } + + public long getElapseTime() { + return elapseTime; + } + + public RetStatus getRetStatus() { + return retStatus; + } + + public Date getLastDetectTime() { + return lastDetectTime; + } + + public Date getNextDetectTime() { + return nextDetectTime; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java new file mode 100644 index 000000000..bba459788 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Instance.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Map; + +/** + * 服务实例通用接口 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface Instance extends Comparable { + + /** + * 默认权重为100 + */ + int DEFAULT_WEIGHT = 100; + + String getNamespace(); + + String getService(); + + String getRevision(); + + /** + * 获取整体熔断状态 + * @return 熔断状态 + */ + CircuitBreakerStatus getCircuitBreakerStatus(); + + /** + * 支持按接口等维度获取熔断状态 + * @param statusDimension 维度 + * @return 熔断状态 + */ + CircuitBreakerStatus getCircuitBreakerStatus(StatusDimension statusDimension); + + boolean isHealthy(); + + boolean isIsolated(); + + String getProtocol(); + + String getId(); + + String getHost(); + + int getPort(); + + String getVersion(); + + Map getMetadata(); + + boolean isEnableHealthCheck(); + + String getRegion(); + + String getZone(); + + String getCampus(); + + int getPriority(); + + int getWeight(); + + String getLogicSet(); + + static Instance createDefaultInstance(String instId, String namespace, String service, String host, int port) { + DefaultInstance defaultInstance = new DefaultInstance(); + defaultInstance.setHealthy(true); + defaultInstance.setIsolated(false); + defaultInstance.setId(instId); + defaultInstance.setNamespace(namespace); + defaultInstance.setService(service); + defaultInstance.setHost(host); + defaultInstance.setPort(port); + return defaultInstance; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceGauge.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceGauge.java new file mode 100644 index 000000000..bfdf5eb26 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceGauge.java @@ -0,0 +1,111 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * 服务上报数据信息 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface InstanceGauge extends Service { + + /** + * 获取节点信息 + * + * @return host + */ + String getHost(); + + /** + * 获取端口信息 + * + * @return port + */ + int getPort(); + + /** + * 获取实例 + * + * @return 实例对象 + */ + Instance getInstance(); + + /** + * 设置实例信息 + * + * @param instance 实例数据 + */ + void setInstance(Instance instance); + + /** + * 获取服务实例ID + * + * @return String + */ + String getInstanceId(); + + /** + * 获取服务调用时延 + * + * @return delay + */ + Long getDelay(); + + /** + * 获取服务调用状态 + * + * @return retStatus + */ + RetStatus getRetStatus(); + + /** + * 服务调用返回码 + * + * @return int + */ + Integer getRetCode(); + + /** + * 获取实例分组 + * + * @return subset + */ + String getSubset(); + + /** + * 获取方法名 + * + * @return method + */ + String getMethod(); + + /** + * 获取主调服务信息 + * + * @return Service + */ + Service getCallerService(); + + /** + * 获取请求标签 + * + * @return labels + */ + String getLabels(); +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceLocalValue.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceLocalValue.java new file mode 100644 index 000000000..ba23c99d1 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceLocalValue.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.function.Function; + +/** + * 实例本地属性值 + * + * @author andrewshan + * @date 2019/9/2 + */ +public interface InstanceLocalValue { + + /** + * 获取熔断状态 + * + * @param statusDimension 维度 + * @return CircuitBreakerStatus + */ + CircuitBreakerStatus getCircuitBreakerStatus(StatusDimension statusDimension); + + /** + * 设置熔断状态 + * + * @param statusDimension 维度 + * @param circuitBreakerStatus 熔断状态 + */ + void setCircuitBreakerStatus(StatusDimension statusDimension, CircuitBreakerStatus circuitBreakerStatus); + + /** + * 获取探测结果 + * + * @return DetectResult + */ + DetectResult getDetectResult(); + + /** + * 设置探测结果 + * + * @param detectResult 探测结果 + */ + void setDetectResult(DetectResult detectResult); + + /** + * 获取插件数据 + * + * @param pluginId 插件ID + * @param create 创建对象的函数 + * @return 插件数据 + */ + Object getPluginValue(int pluginId, Function create); + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceWeight.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceWeight.java new file mode 100644 index 000000000..a73c4c0e7 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/InstanceWeight.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * Dynamic weight for an instance. + */ +public class InstanceWeight { + + private String id; + + private int dynamicWeight; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getDynamicWeight() { + return dynamicWeight; + } + + public void setDynamicWeight(int dynamicWeight) { + this.dynamicWeight = dynamicWeight; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/OutlierDetectionStatus.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/OutlierDetectionStatus.java new file mode 100644 index 000000000..10e263ac9 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/OutlierDetectionStatus.java @@ -0,0 +1,80 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * 实例熔断状态及数据 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class OutlierDetectionStatus { + + /** + * 标识被哪个熔断器熔断 + */ + private final String outlierDetector; + + /** + * 熔断器状态 + */ + private final Status status; + + /** + * 开始被熔断的时间 + */ + private final long startTimeMs; + + + public OutlierDetectionStatus(String outlierDetector, Status status, long startTimeMs) { + this.outlierDetector = outlierDetector; + this.status = status; + this.startTimeMs = startTimeMs; + } + + public String getOutlierDetector() { + return outlierDetector; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "OutlierDetectionStatus{" + + "outlierDetector='" + outlierDetector + '\'' + + ", status=" + status + + ", startTimeMs=" + startTimeMs + + '}'; + } + + /** + * outlier detector status. + * + * @author haitaoyan + * @date 2019/09/22 + */ + public enum Status { + /** + * 健康 + */ + HEALTHY, + /** + * 不健康 + */ + DEAD, + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RegistryCacheValue.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RegistryCacheValue.java new file mode 100644 index 000000000..01b4889cc --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RegistryCacheValue.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; + +/** + * 需要记录到本地内存中的缓存对象 + */ +public interface RegistryCacheValue { + + /** + * 判断是否从文件中加载 + * + * @return boolean + */ + boolean isLoadedFromFile(); + + /** + * 缓存对象类型 + * + * @return 对象类型 + */ + EventType getEventType(); + + /** + * 服务实例列表是否已经加载 + * + * @return 加载标识 + */ + boolean isInitialized(); + + /** + * 获取唯一标识信息 + * + * @return revision + */ + String getRevision(); +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RetStatus.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RetStatus.java new file mode 100644 index 000000000..921936c7d --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/RetStatus.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * 服务调用状态 + * + * @author andrewshan + * @date 2019/8/21 + */ +public enum RetStatus { + /** + * 服务调用成功 + */ + RetSuccess, + + /** + * 服务调用失败 + */ + RetFail, + + /** + * 服务调用超时 + */ + RetTimeout, +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Service.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Service.java new file mode 100644 index 000000000..cf02a5ca8 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Service.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +public interface Service { + + /** + * 获取服务名 + * + * @return String + */ + String getService(); + + /** + * 获取名字空间 + * + * @return String + */ + String getNamespace(); +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java new file mode 100644 index 000000000..9302baed3 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKey.java @@ -0,0 +1,73 @@ +package com.tencent.polaris.api.pojo; + +import java.util.Objects; + +/** + * 服务加规则的唯一标识KEY + * + * @author vickliu + * @date + */ +public class ServiceEventKey implements Service { + + @Override + public String getService() { + return serviceKey.getService(); + } + + @Override + public String getNamespace() { + return serviceKey.getNamespace(); + } + + public enum EventType { + UNKNOWN, + INSTANCE, + ROUTING, + CIRCUIT_BREAKING, + RATE_LIMITING, + } + + private final ServiceKey serviceKey; + + private final EventType eventType; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ServiceEventKey)) { + return false; + } + ServiceEventKey that = (ServiceEventKey) o; + return Objects.equals(serviceKey, that.serviceKey) && eventType == that.eventType; + } + + @Override + public int hashCode() { + return Objects.hash(serviceKey, eventType); + } + + public ServiceEventKey(ServiceKey serviceKey, EventType eventType) { + this.serviceKey = serviceKey; + this.eventType = eventType; + } + + public ServiceKey getServiceKey() { + return serviceKey; + } + + public EventType getEventType() { + return eventType; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceEventKey{" + + "serviceKey=" + serviceKey + + ", eventType=" + eventType + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKeysProvider.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKeysProvider.java new file mode 100644 index 000000000..cb1335785 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceEventKeysProvider.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Set; + +public interface ServiceEventKeysProvider { + + /** + * 是否优先使用本地缓存 + * + * @return boolean + */ + boolean isUseCache(); + + /** + * 获取eventKeys集合 + * + * @return 集合 + */ + Set getSvcEventKeys(); + + /** + * 获取单个eventKey + * + * @return 单个 + */ + ServiceEventKey getSvcEventKey(); +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInfo.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInfo.java new file mode 100644 index 000000000..e9ea11668 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInfo.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Map; + +/** + * 服务信息 + * + * @author andrewshan + * @date 2019/9/23 + */ +public class ServiceInfo implements ServiceMetadata { + + private String namespace; + + private String service; + + private Map metadata; + + @Override + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Override + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceInfo{" + + "namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + ", metadata=" + metadata + + '}'; + } +} \ No newline at end of file diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstances.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstances.java new file mode 100644 index 000000000..511987448 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstances.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.List; + +/** + * 服务实例列表信息 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface ServiceInstances extends ServiceMetadata { + + /** + * 获取服务标识 + * + * @return 服务标识 + */ + ServiceKey getServiceKey(); + + /** + * 获取服务实例总权重值 + * + * @return totalWeight + */ + int getTotalWeight(); + + /** + * 获取服务实例列表 + * + * @return 服务列表 + */ + List getInstances(); + + /** + * 服务实例列表是否已经加载 + * + * @return 加载标识 + */ + boolean isInitialized(); + + /** + * 获取唯一标识信息 + * + * @return revision + */ + String getRevision(); + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstancesWrap.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstancesWrap.java new file mode 100644 index 000000000..ebf962107 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceInstancesWrap.java @@ -0,0 +1,139 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import com.tencent.polaris.api.utils.CollectionUtils; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 服务实例集合的包装类,用于实例动态变化的场景 + * + * @author andrewshan + * @date 2019/8/25 + */ +public class ServiceInstancesWrap implements ServiceInstances { + + private final ServiceInstances serviceInstances; + + private List instances; + + private int totalWeight; + + private int hashCode; + + public ServiceInstancesWrap(ServiceInstances serviceInstances, List instances, int totalWeight) { + this.totalWeight = totalWeight; + this.serviceInstances = serviceInstances; + if (null == instances) { + this.instances = serviceInstances.getInstances(); + } else { + this.instances = instances; + } + hashCode = Objects.hash(serviceInstances.getServiceKey(), instances); + } + + @Override + public ServiceKey getServiceKey() { + return serviceInstances.getServiceKey(); + } + + @Override + public int getTotalWeight() { + return totalWeight; + } + + public void reloadTotalWeight() { + this.totalWeight = 0; + if (CollectionUtils.isEmpty(instances)) { + return; + } + for (Instance instance : instances) { + this.totalWeight += instance.getWeight(); + } + } + + @Override + public List getInstances() { + return instances; + } + + public void setInstances(List instances) { + this.instances = instances; + hashCode = Objects.hash(serviceInstances.getServiceKey(), this.instances); + } + + @Override + public boolean isInitialized() { + return true; + } + + @Override + public String getRevision() { + return serviceInstances.getRevision(); + } + + @Override + public String getService() { + return serviceInstances.getService(); + } + + @Override + public String getNamespace() { + return serviceInstances.getNamespace(); + } + + @Override + public Map getMetadata() { + return serviceInstances.getMetadata(); + } + + public List getAllInstances() { + return serviceInstances.getInstances(); + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceInstances that = (ServiceInstances) o; + return Objects.equals(serviceInstances.getServiceKey(), that.getServiceKey()) && + Objects.equals(instances, that.getInstances()); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceInstancesWrap{" + + "service=" + serviceInstances.getServiceKey() + + ", totalWeight=" + totalWeight + + ", instances=" + instances + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceKey.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceKey.java new file mode 100644 index 000000000..b5db742ea --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceKey.java @@ -0,0 +1,76 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Objects; + +/** + * 服务标识,唯一索引一个服务 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class ServiceKey implements Service { + + private final String namespace; + + private final String service; + + public ServiceKey(String namespace, String service) { + this.namespace = namespace; + this.service = service; + } + + @Override + public String getNamespace() { + return namespace; + } + + @Override + public String getService() { + return service; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceKey that = (ServiceKey) o; + return Objects.equals(namespace, that.namespace) && + Objects.equals(service, that.service); + } + + @Override + public int hashCode() { + return Objects.hash(namespace, service); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceKey{" + + "namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceMetadata.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceMetadata.java new file mode 100644 index 000000000..0ba2e5816 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceMetadata.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import java.util.Map; + +/** + * 服务元数据信息 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface ServiceMetadata extends Service { + + /** + * 获取服务元数据信息 + * + * @return metadata + */ + Map getMetadata(); + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceRule.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceRule.java new file mode 100644 index 000000000..dd378b4f8 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/ServiceRule.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * 服务规则封装类 + */ +public interface ServiceRule { + + /** + * 获取具体的规则值 + * + * @return 规则对象 + */ + Object getRule(); + + /** + * 服务实例列表是否已经加载 + * + * @return 加载标识 + */ + boolean isInitialized(); + + /** + * 获取唯一标识信息 + * + * @return revision + */ + String getRevision(); +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/StatusDimension.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/StatusDimension.java new file mode 100644 index 000000000..b40db3725 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/StatusDimension.java @@ -0,0 +1,105 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +import com.tencent.polaris.api.utils.StringUtils; +import java.util.Objects; + +/** + * 状态查询维度 + */ +public class StatusDimension { + + public enum Level { + /** + * 服务级,不指定主调方和接口 + */ + SERVICE, + /** + * 对所有主调方维度生效 + */ + ALL_CALLER, + /** + * 对所有接口生效 + */ + ALL_METHOD, + /** + * 指定主调方和接口生效 + */ + CALLER_METHOD, + } + + public static final StatusDimension EMPTY_DIMENSION = new StatusDimension("", null); + + private final String method; + + private final ServiceKey callerService; + + public StatusDimension(String method, Service callerService) { + if (StringUtils.isBlank(method)) { + this.method = ""; + } else { + this.method = method; + } + if (null != callerService) { + if (StringUtils.isBlank(callerService.getNamespace()) && StringUtils.isBlank(callerService.getService())) { + this.callerService = null; + } else { + this.callerService = new ServiceKey(callerService.getNamespace(), callerService.getService()); + } + } else { + this.callerService = null; + } + } + + public String getMethod() { + return method; + } + + public Service getCallerService() { + return callerService; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + StatusDimension that = (StatusDimension) o; + return Objects.equals(method, that.method) && + Objects.equals(callerService, that.callerService); + } + + @Override + public int hashCode() { + return Objects.hash(method, callerService); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "StatusDimension{" + + "method='" + method + '\'' + + ", callerService=" + callerService + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Subset.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Subset.java new file mode 100644 index 000000000..27589bf74 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/pojo/Subset.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.pojo; + +/** + * 实例分组 + */ +public interface Subset { + + /** + * 实例分组的ID + * + * @return ID + */ + String getId(); + + /** + * 命名空间 + * + * @return namespace + */ + String getNamespace(); + + /** + * 服务名 + * + * @return service + */ + String getService(); + + /** + * 平铺的标签 + * + * @return labels + */ + String getFlatLabels(); + + /** + * 熔断状态 + * + * @return circuitBreakerStatus + */ + CircuitBreakerStatus getCircuitBreakerStatus(); +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/BaseEntity.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/BaseEntity.java new file mode 100644 index 000000000..03e81b251 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/BaseEntity.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +/** + * Contain basic properties for an request/response. + * + * @author andrewshan + * @date 2019/8/21 + */ +public class BaseEntity { + + private String service; + + private String namespace; + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "BaseEntity{" + + ", service='" + service + '\'' + + ", namespace='" + namespace + '\'' + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/Criteria.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/Criteria.java new file mode 100644 index 000000000..51dcee123 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/Criteria.java @@ -0,0 +1,61 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +/** + * 用户态的服务路由因子 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class Criteria { + + /** + * 指定负载均衡策略 + */ + private String lbPolicy; + /** + * 一致性hash的key + */ + private String hashKey; + + public String getHashKey() { + return hashKey; + } + + public void setHashKey(String hashKey) { + this.hashKey = hashKey; + } + + public String getLbPolicy() { + return lbPolicy; + } + + public void setLbPolicy(String lbPolicy) { + this.lbPolicy = lbPolicy; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "Criteria{" + + "lbPolicy='" + lbPolicy + '\'' + + ", hashKey='" + hashKey + '\'' + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/MetadataFailoverType.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/MetadataFailoverType.java new file mode 100644 index 000000000..3ebf24612 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/MetadataFailoverType.java @@ -0,0 +1,62 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +/** + * 元数据路由的降级类型 + * + * @author starkwen + * @date 2021/2/25 下午3:32 + */ +public enum MetadataFailoverType { + + /** + * 默认不降级 + */ + METADATAFAILOVERNONE("metadataFailoverNone"), + + /** + * 降级返回所有节点 + */ + METADATAFAILOVERALL("metadataFailoverAll"), + + /** + * 返回不包含元数据路由key的节点 + */ + METADATAFAILOVERNOTKEY("metadataFailoverNoKey"); + + String name; + + MetadataFailoverType(String name) { + this.name = name; + } + + + public String getName() { + return name; + } + + public static MetadataFailoverType getByName(String name) { + for (MetadataFailoverType value : values()) { + if (value.getName().equals(name)) { + return value; + } + } + return METADATAFAILOVERNONE; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/RequestBaseEntity.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/RequestBaseEntity.java new file mode 100644 index 000000000..a78da3d94 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/RequestBaseEntity.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +/** + * 作为RPC请求的基类型 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class RequestBaseEntity extends BaseEntity { + + /** + * 请求超时时间 + */ + private long timeoutMs; + + public long getTimeoutMs() { + return timeoutMs; + } + + public void setTimeoutMs(long timeoutMs) { + this.timeoutMs = timeoutMs; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "RequestBaseEntity{" + + "timeoutMs=" + timeoutMs + + "} " + super.toString(); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/ServiceCallResult.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/ServiceCallResult.java new file mode 100644 index 000000000..c411ff4c6 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/rpc/ServiceCallResult.java @@ -0,0 +1,240 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.Service; + +/** + * Instance invocation metrics. + * + * @author andrewshan + * @date 2019/8/21 + */ +public class ServiceCallResult implements InstanceGauge { + + /** + * 服务名 + */ + private String service; + + /** + * 命名空间 + */ + private String namespace; + + /** + * 被调节点IP + */ + private String host; + + /** + * 被调端口 + */ + private int port; + + /** + * 服务实例信息 + */ + private Instance instance; + + /** + * 主调方标签,代表服务调用的接口等信息 + */ + private String labels; + + /** + * 请求是否成功以retStatus为准,retCode不生效 + */ + private RetStatus retStatus; + + /** + * 返回码 + */ + private Integer retCode; + + /** + * 时延 单位:ms + */ + private Long delay; + + /** + * 实例分组 + */ + private String subset; + + /** + * 方法 + */ + private String method; + + /** + * 主调服务 + */ + private Service callerService; + + @Override + public String getHost() { + if (null != instance) { + return instance.getHost(); + } + return host; + } + + @Override + public int getPort() { + if (null != instance) { + return instance.getPort(); + } + return port; + } + + @Override + public RetStatus getRetStatus() { + return retStatus; + } + + public void setRetStatus(RetStatus retStatus) { + this.retStatus = retStatus; + } + + @Override + public String getNamespace() { + if (null != instance) { + return instance.getNamespace(); + } + return namespace; + } + + @Override + public String getService() { + if (null != instance) { + return instance.getService(); + } + return service; + } + + @Override + public String getInstanceId() { + if (null == instance) { + return ""; + } + return instance.getId(); + } + + public void setService(String service) { + this.service = service; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + @Override + public Integer getRetCode() { + return retCode; + } + + public void setRetCode(int retCode) { + this.retCode = retCode; + } + + @Override + public Long getDelay() { + return delay; + } + + public void setDelay(long delay) { + this.delay = delay; + } + + @Override + public void setInstance(Instance instance) { + this.instance = instance; + } + + @Override + public Instance getInstance() { + return instance; + } + + @Override + public String getLabels() { + return labels; + } + + public void setLabels(String labels) { + this.labels = labels; + } + + @Override + public String getSubset() { + return subset; + } + + public void setSubset(String subset) { + this.subset = subset; + } + + @Override + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + @Override + public Service getCallerService() { + return callerService; + } + + public void setCallerService(Service callerService) { + this.callerService = callerService; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceCallResult{" + + "service='" + service + '\'' + + ", namespace='" + namespace + '\'' + + ", host='" + host + '\'' + + ", port=" + port + + ", instance=" + instance + + ", labels='" + labels + '\'' + + ", retStatus=" + retStatus + + ", retCode=" + retCode + + ", delay=" + delay + + ", subset='" + subset + '\'' + + ", method='" + method + '\'' + + ", callerService=" + callerService + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ClosableReadWriteLock.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ClosableReadWriteLock.java new file mode 100644 index 000000000..d6688f2cd --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ClosableReadWriteLock.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.utils; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * 可关闭的读写锁 + */ +public class ClosableReadWriteLock { + + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + public LockWrapper readLock() { + return new LockWrapper(readWriteLock.readLock()); + } + + public LockWrapper writeLock() { + return new LockWrapper(readWriteLock.writeLock()); + } + + /** + * 锁封装,保证自动关闭 + */ + public static class LockWrapper implements AutoCloseable { + + private final Lock internalLock; + + public LockWrapper(Lock l) { + this.internalLock = l; + } + + public void lock() { + this.internalLock.lock(); + } + + public void close() { + this.internalLock.unlock(); + } + } + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CollectionUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CollectionUtils.java new file mode 100644 index 000000000..de8d9296e --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/CollectionUtils.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.utils; + +import java.util.Collection; + +public class CollectionUtils { + + public static boolean isEmpty(Collection coll) { + return coll == null || coll.isEmpty(); + } + + public static boolean isNotEmpty(Collection coll) { + return !isEmpty(coll); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/MapUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/MapUtils.java new file mode 100644 index 000000000..614e5467c --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/MapUtils.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.utils; + +import java.util.Map; + +public class MapUtils { + + public static boolean isEmpty(Map map) { + return map == null || map.isEmpty(); + } + + public static boolean isNotEmpty(Map map) { + return !isEmpty(map); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java new file mode 100644 index 000000000..a76acf360 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/RuleUtils.java @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.utils; + +import com.tencent.polaris.client.pb.ModelProto.MatchString; +import com.tencent.polaris.client.pb.ModelProto.MatchString.MatchStringType; + +public class RuleUtils { + + public static final String MATCH_ALL = "*"; + + /** + * 是否全匹配的规则 + * @param ruleMetaValue 规则匹配条件 + * @return 是否全匹配,全匹配则忽略该规则 + */ + public static boolean isMatchAllValue(MatchString ruleMetaValue) { + return ruleMetaValue.getType() == MatchStringType.REGEX + && StringUtils.equals(ruleMetaValue.getValue().getValue(), MATCH_ALL); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/StringUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/StringUtils.java new file mode 100644 index 000000000..1ec1ea217 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/StringUtils.java @@ -0,0 +1,101 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.utils; + +import java.util.Objects; + +public class StringUtils { + + public static boolean isBlank(String str) { + int strLen; + if (str != null && (strLen = str.length()) != 0) { + for (int i = 0; i < strLen; ++i) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + } + return true; + } + + public static String defaultString(String value) { + if (null == value) { + return ""; + } + return value; + } + + public static boolean isNotBlank(String str) { + return !isBlank(str); + } + + public static boolean equals(String str1, String str2) { + return Objects.equals(str1, str2); + } + + public static boolean isEmpty(String str) { + return str == null || str.length() == 0; + } + + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + public static String replace(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, -1); + } + + /** + * 替换字符串 + * + * @param text text + * @param searchString searchString + * @param replacement replacement + * @param max max + * @return 结果 + */ + public static String replace(String text, String searchString, String replacement, int max) { + if (!isEmpty(text) && !isEmpty(searchString) && replacement != null && max != 0) { + int start = 0; + int end = text.indexOf(searchString, start); + if (end == -1) { + return text; + } else { + int replLength = searchString.length(); + int increase = replacement.length() - replLength; + increase = increase < 0 ? 0 : increase; + increase *= max < 0 ? 16 : (max > 64 ? 64 : max); + + StringBuilder buf = new StringBuilder(text.length() + increase); + for (; end != -1; end = text.indexOf(searchString, start)) { + buf.append(text.substring(start, end)).append(replacement); + start = end + replLength; + --max; + if (max == 0) { + break; + } + } + + buf.append(text.substring(start)); + return buf.toString(); + } + } else { + return text; + } + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ThreadPoolUtils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ThreadPoolUtils.java new file mode 100644 index 000000000..6dd7be459 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/api/utils/ThreadPoolUtils.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ThreadPoolUtils { + + private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolUtils.class); + + /** + * 等待所有线程池结束 + * + * @param services 线程池 + */ + public static void waitAndStopThreadPools(ExecutorService[] services) { + for (ExecutorService service : services) { + if (null == service) { + continue; + } + service.shutdown(); + } + for (ExecutorService service : services) { + if (null == service) { + continue; + } + try { + service.awaitTermination(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + LOG.warn("interrupted while waiting thread pool terminated", e); + } + } + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/DefaultInstanceLocalValue.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/DefaultInstanceLocalValue.java new file mode 100644 index 000000000..e7ddf62b5 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/DefaultInstanceLocalValue.java @@ -0,0 +1,85 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.pojo; + +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.StatusDimension; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +/** + * 客户端服务数据统计对象,包含熔断数据、动态权重等信息 + * + * @author andrewshan + * @date 2019/8/27 + */ +public class DefaultInstanceLocalValue implements InstanceLocalValue { + + private final Map> + circuitBreakerStatus = new ConcurrentHashMap<>(); + + private final AtomicReference detectResult = new AtomicReference<>(); + + private final Map pluginValues = new ConcurrentHashMap<>(); + + @Override + public CircuitBreakerStatus getCircuitBreakerStatus(StatusDimension statusDimension) { + AtomicReference value = circuitBreakerStatus + .get(statusDimension); + if (null == value) { + return null; + } + return value.get(); + } + + @Override + public DetectResult getDetectResult() { + return detectResult.get(); + } + + @Override + public void setCircuitBreakerStatus(StatusDimension statusDimension, CircuitBreakerStatus status) { + AtomicReference value = circuitBreakerStatus + .computeIfAbsent(statusDimension, + new Function>() { + @Override + public AtomicReference apply(StatusDimension statusDimension) { + return new AtomicReference<>(); + } + }); + value.set(status); + } + + public void setDetectResult(DetectResult detectResult) { + this.detectResult.set(detectResult); + } + + @Override + public Object getPluginValue(int pluginId, Function create) { + if (null == create) { + return pluginValues.get(pluginId); + } + return pluginValues.computeIfAbsent(pluginId, create); + } + + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/InstanceByProto.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/InstanceByProto.java new file mode 100644 index 000000000..0c2b33695 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/InstanceByProto.java @@ -0,0 +1,214 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.pojo; + +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.client.pb.ServiceProto; +import java.util.Map; +import java.util.Objects; + +/** + * 通过PB对象封装的实例信息 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class InstanceByProto implements Instance { + + private final ServiceKey serviceKey; + + private final ServiceProto.Instance instance; + + private final InstanceLocalValue instanceLocalValue; + + private final int hashCode; + + public InstanceByProto(ServiceKey serviceKey, ServiceProto.Instance instance, + InstanceLocalValue localValue) { + this.serviceKey = serviceKey; + this.instance = instance; + this.instanceLocalValue = localValue; + hashCode = Objects.hash(instance.getHost(), instance.getPort()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InstanceByProto that = (InstanceByProto) o; + return Objects.equals(instance.getHost(), that.instance.getHost()) && Objects + .equals(instance.getPort(), that.instance.getPort()); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String getNamespace() { + return serviceKey.getNamespace(); + } + + @Override + public String getService() { + return serviceKey.getService(); + } + + @Override + public String getRevision() { + return instance.getRevision().getValue(); + } + + @Override + public CircuitBreakerStatus getCircuitBreakerStatus() { + if (null == instanceLocalValue) { + return null; + } + return instanceLocalValue.getCircuitBreakerStatus(StatusDimension.EMPTY_DIMENSION); + } + + @Override + public CircuitBreakerStatus getCircuitBreakerStatus(StatusDimension statusDimension) { + if (null == instanceLocalValue) { + return null; + } + return instanceLocalValue.getCircuitBreakerStatus(statusDimension); + } + + public DetectResult getDetectResult() { + if (null == instanceLocalValue) { + return null; + } + return instanceLocalValue.getDetectResult(); + } + + @Override + public boolean isHealthy() { + return instance.getHealthy().getValue(); + } + + @Override + public boolean isIsolated() { + return instance.getIsolate().getValue(); + } + + @Override + public String getProtocol() { + return instance.getProtocol().getValue(); + } + + @Override + public String getId() { + return instance.getId().getValue(); + } + + @Override + public String getHost() { + return instance.getHost().getValue(); + } + + @Override + public int getPort() { + return instance.getPort().getValue(); + } + + @Override + public String getVersion() { + return instance.getVersion().getValue(); + } + + @Override + public Map getMetadata() { + return instance.getMetadataMap(); + } + + @Override + public boolean isEnableHealthCheck() { + return instance.hasHealthCheck(); + } + + @Override + public String getRegion() { + return instance.getLocation().getRegion().getValue(); + } + + @Override + public String getZone() { + return instance.getLocation().getZone().getValue(); + } + + @Override + public String getCampus() { + return instance.getLocation().getCampus().getValue(); + } + + @Override + public int getPriority() { + return instance.getPriority().getValue(); + } + + @Override + public int getWeight() { + int weight = DEFAULT_WEIGHT; + if (instance.hasWeight()) { + UInt32Value weightValue = instance.getWeight(); + weight = weightValue.getValue(); + } + return weight; + } + + @Override + public String getLogicSet() { + return instance.getLogicSet().getValue(); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "InstanceByProto{" + + "serviceKey=" + serviceKey + + ", instance=" + instance + + '}'; + } + + public InstanceLocalValue getInstanceLocalValue() { + return instanceLocalValue; + } + + @Override + public int compareTo(Instance instance) { + String curHost = this.getHost(); + String remoteHost = instance.getHost(); + int result = curHost.compareTo(remoteHost); + if (result != 0) { + return result; + } + return this.getPort() - instance.getPort(); + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/Node.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/Node.java new file mode 100644 index 000000000..95f982472 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/Node.java @@ -0,0 +1,68 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.pojo; + +import java.util.Objects; + +public class Node { + + private final String host; + + private final int port; + + public Node(String host, int port) { + this.host = host; + this.port = port; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Node node = (Node) o; + return port == node.port && + Objects.equals(host, node.host); + } + + @Override + public int hashCode() { + return Objects.hash(host, port); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "Node{" + + "host='" + host + '\'' + + ", port=" + port + + '}'; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceInstancesByProto.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceInstancesByProto.java new file mode 100644 index 000000000..f8f22f229 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceInstancesByProto.java @@ -0,0 +1,232 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.pojo; + +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.client.pb.ResponseProto; +import com.tencent.polaris.client.pb.ServiceProto; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 通过PB对象封装的服务信息 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class ServiceInstancesByProto implements ServiceInstances, RegistryCacheValue { + + private final ServiceProto.Service service; + + private final ServiceKey svcKey; + + private final List instances; + + private final Map instanceIdMap; + + private final Map nodeMap; + + private final Map metadata; + + private final boolean initialized; + + private final int totalWeight; + + private final boolean loadedFromFile; + + private final int hashCode; + + @Override + public ServiceKey getServiceKey() { + return svcKey; + } + + @Override + public int getTotalWeight() { + return totalWeight; + } + + @Override + public List getInstances() { + return instances; + } + + @Override + public boolean isLoadedFromFile() { + return loadedFromFile; + } + + @Override + public EventType getEventType() { + return EventType.INSTANCE; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public String getRevision() { + if (null != service) { + return service.getRevision().getValue(); + } + return ""; + } + + @Override + public String getService() { + if (null != service) { + return service.getName().getValue(); + } + return ""; + } + + @Override + public String getNamespace() { + if (null != service) { + return service.getNamespace().getValue(); + } + return ""; + } + + @Override + public Map getMetadata() { + return metadata; + } + + /** + * 获取实例本地数据 + * + * @param instId 实例ID + * @return InstanceLocalValue + */ + public InstanceByProto getInstance(String instId) { + return instanceIdMap.get(instId); + } + + public InstanceByProto getInstanceByNode(Node node) { + return nodeMap.get(node); + } + + /** + * 构造函数 + * + * @param response 应答Proto + * @param oldSvcInstances 旧的实例列表 + * @param loadFromFile 是否从缓存文件加载 + */ + public ServiceInstancesByProto(ResponseProto.DiscoverResponse response, ServiceInstancesByProto oldSvcInstances, + boolean loadFromFile) { + this.service = response.getService(); + List tmpInstances = new ArrayList<>(); + Map tmpInstanceMap = new HashMap<>(); + Map tmpNodeMap = new HashMap<>(); + int totalWeight = 0; + ServiceKey svcKey = new ServiceKey(this.service.getNamespace().getValue(), this.service.getName().getValue()); + if (CollectionUtils.isNotEmpty(response.getInstancesList())) { + for (ServiceProto.Instance instance : response.getInstancesList()) { + String instID = instance.getId().getValue(); + InstanceLocalValue instanceLocalValue = null; + if (null != oldSvcInstances) { + InstanceByProto oldInstance = oldSvcInstances.getInstance(instID); + if (null != oldInstance) { + instanceLocalValue = oldInstance.getInstanceLocalValue(); + } + } + if (null == instanceLocalValue) { + //创建一个新的本地缓存实例 + instanceLocalValue = new DefaultInstanceLocalValue(); + } + InstanceByProto targetInstance = new InstanceByProto(svcKey, instance, instanceLocalValue); + totalWeight += targetInstance.getWeight(); + tmpInstances.add(targetInstance); + tmpInstanceMap.put(instID, targetInstance); + tmpNodeMap.put(new Node(targetInstance.getHost(), targetInstance.getPort()), targetInstance); + } + } + Collections.sort(tmpInstances); + hashCode = Objects.hash(svcKey, tmpInstances); + this.svcKey = svcKey; + this.instanceIdMap = Collections.unmodifiableMap(tmpInstanceMap); + this.nodeMap = Collections.unmodifiableMap(tmpNodeMap); + this.instances = Collections.unmodifiableList(tmpInstances); + this.totalWeight = totalWeight; + this.initialized = true; + this.metadata = Collections.unmodifiableMap(this.service.getMetadataMap()); + this.loadedFromFile = loadFromFile; + } + + /** + * 创建空的服务对象 + */ + public ServiceInstancesByProto() { + this.service = null; + this.svcKey = null; + this.initialized = false; + this.instances = Collections.emptyList(); + this.instanceIdMap = Collections.emptyMap(); + this.nodeMap = Collections.emptyMap(); + this.metadata = Collections.emptyMap(); + this.loadedFromFile = false; + this.totalWeight = 0; + hashCode = Objects.hash(instances); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceInstancesByProto{" + + "service=" + service + + ", instances=" + instances + + ", metadata=" + metadata + + ", revision='" + getRevision() + '\'' + + ", initialized=" + initialized + + ", totalWeight=" + totalWeight + + '}'; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceInstances that = (ServiceInstances) o; + return Objects.equals(svcKey, that.getServiceKey()) && + Objects.equals(instances, that.getInstances()); + } + + @Override + public int hashCode() { + return hashCode; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceRuleByProto.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceRuleByProto.java new file mode 100644 index 000000000..0766a2095 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/pojo/ServiceRuleByProto.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.pojo; + +import com.google.protobuf.Message; +import com.google.protobuf.TextFormat; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceRule; + +/** + * 通过PB对象封装的服务信息 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class ServiceRuleByProto implements ServiceRule, RegistryCacheValue { + + private final Message ruleValue; + + private final String revision; + + private final boolean initialized; + + private final boolean loadFromFile; + + private final EventType eventType; + + public ServiceRuleByProto(Message ruleValue, String revision, boolean loadFromFile, EventType eventType) { + this.ruleValue = ruleValue; + this.revision = revision; + this.loadFromFile = loadFromFile; + this.initialized = true; + this.eventType = eventType; + } + + public ServiceRuleByProto() { + this.ruleValue = null; + this.revision = ""; + this.loadFromFile = false; + this.initialized = false; + this.eventType = EventType.UNKNOWN; + } + + @Override + public Object getRule() { + return ruleValue; + } + + @Override + public String getRevision() { + return revision; + } + + + @Override + public boolean isLoadedFromFile() { + return loadFromFile; + } + + @Override + public EventType getEventType() { + return eventType; + } + + @Override + public boolean isInitialized() { + return initialized; + } + + @Override + public String toString() { + return "ServiceRuleByProto [rule=" + (ruleValue == null ? null : TextFormat.shortDebugString(ruleValue)) + + ", revision=" + revision + ", initialized=" + + initialized + ", eventType=" + eventType + "]"; + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/CommonValidator.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/CommonValidator.java new file mode 100644 index 000000000..cc5979272 --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/CommonValidator.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.util; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.utils.StringUtils; + +public class CommonValidator { + + /** + * 校验命名空间和服务名 + * + * @param namespace 命名空间 + * @param service 服务名 + * @throws PolarisException 异常 + */ + public static void validateNamespaceService(String namespace, String service) throws PolarisException { + if (StringUtils.isBlank(namespace)) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "namespace can not be blank"); + } + if (StringUtils.isBlank(service)) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "service can not be blank"); + } + } +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/NamedThreadFactory.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/NamedThreadFactory.java new file mode 100644 index 000000000..6974a416b --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/NamedThreadFactory.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.util; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +public class NamedThreadFactory implements ThreadFactory { + + private final String component; + + private final AtomicInteger idx = new AtomicInteger(0); + + public NamedThreadFactory(String component) { + this.component = component; + } + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + int nextIdx = idx.addAndGet(1); + thread.setName(String.format("polaris-%s-%d", component, nextIdx)); + return thread; + } + +} diff --git a/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/Utils.java b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/Utils.java new file mode 100644 index 000000000..0a6bbd23c --- /dev/null +++ b/polaris-common/polaris-model/src/main/java/com/tencent/polaris/client/util/Utils.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.client.util; + +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.StatusDimension.Level; +import com.tencent.polaris.api.utils.StringUtils; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Common util class. + * + * @author andrewshan + * @date 2019/8/24 + */ +public class Utils { + + private static final Logger LOG = LoggerFactory.getLogger(Utils.class); + + public static long sleepUninterrupted(long millis) { + long currentTime = System.currentTimeMillis(); + long deadline = currentTime + millis; + while (currentTime < deadline) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + LOG.debug(String.format("interrupted while sleeping %d", millis), e); + } + currentTime = System.currentTimeMillis(); + } + return currentTime; + } + + public static String translatePath(String path) { + if (path.startsWith("$HOME")) { + String userHome = System.getProperty("user.home"); + return StringUtils.replace(path, "$HOME", userHome); + } + return path; + } + + + /** + * 用正则表达式来判断 + * 1.compile(String regex) 将给定的正则表达式编译到模式中。 + * 2.matcher(CharSequence input) 创建匹配给定输入与此模式的匹配器。 + * 3.matches() 尝试将整个区域与模式匹配。 + * + * @param regex 正则表达式 + * @param input 输入文本 + * @return 是否匹配 + */ + public static boolean regMatch(String regex, String input) { + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(input); + return m.matches(); + } + + + public static boolean isHealthyInstance(Instance instance, Map dimensions) { + if (!instance.isHealthy()) { + return false; + } + for (StatusDimension statusDimension : dimensions.values()) { + CircuitBreakerStatus circuitBreakerStatus = instance.getCircuitBreakerStatus(statusDimension); + if (null != circuitBreakerStatus && circuitBreakerStatus.getStatus() == CircuitBreakerStatus.Status.OPEN) { + return false; + } + } + return true; + } +} diff --git a/polaris-common/polaris-model/src/main/templates/Version.java b/polaris-common/polaris-model/src/main/templates/Version.java new file mode 100644 index 000000000..d22ee225d --- /dev/null +++ b/polaris-common/polaris-model/src/main/templates/Version.java @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.version; + +public class Version { + + /** + * project version + */ + public static final String VERSION = "${project.version}"; + /** + * build timestamp + */ + public static final String TIMESTAMP = "${build.time}"; +} diff --git a/polaris-common/polaris-protobuf/pom.xml b/polaris-common/polaris-protobuf/pom.xml new file mode 100644 index 000000000..8de23ff58 --- /dev/null +++ b/polaris-common/polaris-protobuf/pom.xml @@ -0,0 +1,67 @@ + + + + polaris-common + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-protobuf + + + + io.grpc + grpc-all + ${grpc.version} + + + javax.annotation + javax.annotation-api + ${javax.annotation.version} + provided + + + + + + + + kr.motd.maven + os-maven-plugin + 1.6.2 + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.6.1 + + + + com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier} + + grpc-java + + io.grpc:protoc-gen-grpc-java:1.38.0:exe:${os.detected.classifier} + + + grpc-java + + + + + + compile + compile-custom + + + + + + + \ No newline at end of file diff --git a/polaris-common/polaris-protobuf/src/main/proto/circuitbreaker.proto b/polaris-common/polaris-protobuf/src/main/proto/circuitbreaker.proto new file mode 100644 index 000000000..8b3b34644 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/circuitbreaker.proto @@ -0,0 +1,185 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; +import "google/protobuf/duration.proto"; +import "model.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "CircuitBreakerProto"; + +//单个熔断规则定义 +message CircuitBreaker { + google.protobuf.StringValue id = 1; + // 规则版本 + google.protobuf.StringValue version = 2; + // 规则名 + google.protobuf.StringValue name = 3; + // 规则命名空间 + google.protobuf.StringValue namespace = 4; + + // 规则所属服务 + google.protobuf.StringValue service = 5; + google.protobuf.StringValue service_namespace = 6; + + //熔断规则可以分为被调规则和主调规则 + //被调规则针对所有的指定主调生效,假如不指定则对所有的主调生效 + //主调规则为当前主调方的规则,假如不指定则针对所有被调生效 + repeated CbRule inbounds = 7; + repeated CbRule outbounds = 8; + + google.protobuf.StringValue token = 9; + google.protobuf.StringValue owners = 10; + // 业务 + google.protobuf.StringValue business = 11; + // 部门 + google.protobuf.StringValue department = 12; + + // 规则描述 + google.protobuf.StringValue comment = 13; + google.protobuf.StringValue ctime = 14; + google.protobuf.StringValue mtime = 15; + google.protobuf.StringValue revision = 16; +} + +// 主调匹配规则 +message SourceMatcher { + // 主调命名空间以及服务名,可以为*,代表全匹配 + google.protobuf.StringValue service = 1; + google.protobuf.StringValue namespace = 2; + // 可选,主调业务标签,用于匹配是否使用该熔断规则,可放置用户的接口信息等 + map labels = 3; +} + +// 熔断恢复配置 +message RecoverConfig { + // 触发熔断后到半开状态之间的等待间隔 + google.protobuf.Duration sleepWindow = 1; + // 半开后,最多重试多少次恢复 + google.protobuf.UInt32Value maxRetryAfterHalfOpen = 2; + // 半开后放量的最大百分比 + repeated google.protobuf.UInt32Value requestRateAfterHalfOpen = 3; + // 熔断器半开到关闭所必须的最少成功率,默认100% + google.protobuf.UInt32Value successRateToClose = 4; + // 半开后最大放量数(用户不配置最大百分比时默认使用该配置) + google.protobuf.UInt32Value requestCountAfterHalfOpen = 5; + //主动探测配置 + enum OutlierDetectWhen { + //不开启监控探测 + NEVER = 0; + //只有在熔断恢复时才开启健康探测 + ON_RECOVER = 1; + //一直开启健康探测 + ALWAYS = 2; + } + OutlierDetectWhen outlierDetectWhen = 6; +} + +// 熔断策略 +message CbPolicy { + // 错误率熔断配置 + message ErrRateConfig { + //是否启用错误率配置 + google.protobuf.BoolValue enable = 1; + // 触发错误率熔断的最低请求阈值 + google.protobuf.UInt32Value requestVolumeThreshold = 2; + // 可选。触发保持状态的错误率阈值,假如不配置,则默认不会进入Preserved状态 + google.protobuf.UInt32Value errorRateToPreserved = 3; + // 触发熔断的错误率阈值 + google.protobuf.UInt32Value errorRateToOpen = 4; + } + ErrRateConfig errorRate = 1; + // 慢调用率熔断策略配置 + message SlowRateConfig { + // 是否启用慢调用率配置 + google.protobuf.BoolValue enable = 1; + // 最大响应时间,超过该时间属于慢调用请求 + google.protobuf.Duration maxRt = 2; + // 可选。触发保持状态的超时率阈值,假如不配置,则默认不会进入Preserved状态 + google.protobuf.UInt32Value slowRateToPreserved = 3; + // 触发熔断的超时率阈值 + google.protobuf.UInt32Value slowRateToOpen = 4; + } + SlowRateConfig slowRate = 2; + // 熔断的决策周期,多久触发一次熔断决策 + google.protobuf.Duration judgeDuration = 3; + //最大熔断比例,超过多少比例后不会继续熔断 + google.protobuf.UInt32Value maxEjectionPercent = 4; + //连续错误数熔断配置 + message ConsecutiveErrConfig { + // 是否启用连续错误数配置 + google.protobuf.BoolValue enable = 1; + // 连续错误数阈值,进入Preserved状态 + google.protobuf.UInt32Value consecutiveErrorToPreserved = 2; + // 连续错误数阈值,进入Open状态 + google.protobuf.UInt32Value consecutiveErrorToOpen = 3; + } + ConsecutiveErrConfig consecutive = 5; +} + +// 目标set的规则 +message DestinationSet { + // 被调命名空间以及服务名,可以为*,代表全匹配 + google.protobuf.StringValue service = 1; + google.protobuf.StringValue namespace = 2; + // 可选,SUBSET标识 + map metadata = 3; + // 需要进行熔断的资源 + // 支持SUBSET(子集群),以及INSTANCE(单个实例),默认为SUBSET + enum Resource { + // 针对实例分组进行熔断 + SUBSET = 0; + // 针对实例进行熔断 + INSTANCE = 1; + } + Resource resource = 4; + // 熔断决策类型,支持GLOBAL(分布式决策)以及LOCAL(本地决策),默认GLOBAL + // 当指定为GLOBAL时,则会定期上报统计数据并根据汇总数据进行熔断决策 + enum Type { + GLOBAL = 0; + LOCAL = 1; + } + Type type = 5; + + //熔断范围,是否扩散针对相同服务下所有接口进行熔断 + enum Scope { + //触发熔断条件,扩散熔断所有接口 + ALL = 0; + //触发熔断条件,只熔断当前接口 + CURRENT = 1; + } + Scope scope = 6; + + // 熔断数据度量周期 + // 所有的阈值指标按此周期进行统计 + google.protobuf.Duration metricWindow = 7; + + // 熔断数据统计精度,决定数据度量的最小周期 + // 度量滑窗的步长=window/precision + google.protobuf.UInt32Value metricPrecision = 8; + + // 熔断数据上报周期,对分布式熔断有效 + google.protobuf.Duration updateInterval = 9; + + // 触发熔断后恢复配置 + RecoverConfig recover = 10; + + // 熔断策略 + CbPolicy policy = 11; + + // 被调的接口信息,指定哪些接口会使用该规则 + MatchString method = 12; + + // 返回码,指定哪些返回码会使用该规则 + repeated google.protobuf.Int64Value errorCodes = 13; +} + +// 具体熔断规则 +message CbRule { + // 如果匹配Source规则,按照Destination进行熔断 + // 多个Source之间的关系为或 + repeated SourceMatcher sources = 1; + repeated DestinationSet destinations = 2; +} + diff --git a/polaris-common/polaris-protobuf/src/main/proto/client.proto b/polaris-common/polaris-protobuf/src/main/proto/client.proto new file mode 100644 index 000000000..f616439e1 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/client.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; +import "model.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "ClientProto"; + +message Client { + google.protobuf.StringValue host = 1; + + enum ClientType { + UNKNOWN = 0; + SDK = 1; + AGENT = 2; + } + + ClientType type = 2; + + google.protobuf.StringValue version = 3; + + Location location = 4; +} diff --git a/polaris-common/polaris-protobuf/src/main/proto/grpcapi_discovery.proto b/polaris-common/polaris-protobuf/src/main/proto/grpcapi_discovery.proto new file mode 100644 index 000000000..7e22732df --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/grpcapi_discovery.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package v1; + +import "client.proto"; +import "service.proto"; +import "request.proto"; +import "response.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "PolarisGRPCService"; + +service PolarisGRPC { + // 客户端上报 + rpc ReportClient(Client) returns(Response) {} + + // 被调方注册服务实例 + rpc RegisterInstance(Instance) returns(Response) {} + // 被调方反注册服务实例 + rpc DeregisterInstance(Instance) returns(Response) {} + + // 统一发现接口 + rpc Discover(stream DiscoverRequest) returns(stream DiscoverResponse) {} + + // 被调方上报心跳 + rpc Heartbeat(Instance) returns(Response) {} +} diff --git a/polaris-common/polaris-protobuf/src/main/proto/grpcapi_v2_metric.proto b/polaris-common/polaris-protobuf/src/main/proto/grpcapi_v2_metric.proto new file mode 100644 index 000000000..9407ec38d --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/grpcapi_v2_metric.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package polaris.metric.v2; + +option go_package = "v2"; + +import "ratelimit_v2.proto"; + +option java_package = "com.tencent.polaris.ratelimit.client.pb"; + +service RateLimitGRPCV2 { + // 限流接口 + rpc Service(stream RateLimitRequest) returns(stream RateLimitResponse) {} + + //时间对齐接口 + rpc TimeAdjust(TimeAdjustRequest) returns(TimeAdjustResponse) {} +} \ No newline at end of file diff --git a/polaris-common/polaris-protobuf/src/main/proto/model.proto b/polaris-common/polaris-protobuf/src/main/proto/model.proto new file mode 100644 index 000000000..40a2e32bc --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/model.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; + +option java_outer_classname="ModelProto"; +option java_package="com.tencent.polaris.client.pb"; + +message Location { + google.protobuf.StringValue region = 1; + google.protobuf.StringValue zone = 2; + google.protobuf.StringValue campus = 3; +} + +message MatchString { + enum MatchStringType { + EXACT = 0; + REGEX = 1; + } + + enum ValueType { + TEXT = 0; + PARAMETER = 1; + VARIABLE = 2; + } + + MatchStringType type = 1; + google.protobuf.StringValue value = 2; + ValueType value_type = 3; +} \ No newline at end of file diff --git a/polaris-common/polaris-protobuf/src/main/proto/ratelimit.proto b/polaris-common/polaris-protobuf/src/main/proto/ratelimit.proto new file mode 100644 index 000000000..616a2aca6 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/ratelimit.proto @@ -0,0 +1,180 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; +import "google/protobuf/duration.proto"; +import "model.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "RateLimitProto"; + +// 同一服务下限流规则集合 +message RateLimit { + // 限流规则集合 + repeated Rule rules = 1; + // 限流规则汇总的revision信息 + google.protobuf.StringValue revision = 2; +} + +// 单个限流规则信息 +message Rule { + // 限流规则唯一标识 + google.protobuf.StringValue id = 1; + // 限流规则所属服务名 + google.protobuf.StringValue service = 2; + // 限流规则所属命名空间 + google.protobuf.StringValue namespace = 3; + // 可选,SUBSET标识 + map subset = 4; + // 限流规则优先级,0值最高 + google.protobuf.UInt32Value priority = 5; + // 限流资源 + enum Resource { + // 针对QPS进行限流 + QPS = 0; + // 针对并发数进行限流 + CONCURRENCY = 1; + } + Resource resource = 6; + // 限流类型 + // global全局限流(默认)或者local单机限流 + enum Type { + GLOBAL = 0; + LOCAL = 1; + } + Type type = 7; + // 业务标签集合,通过KV进行匹配,全部匹配才使用该规则 + map labels = 8; + // 限流阈值 + // 可以有多个粒度的配置(比如同时针对秒级,分钟级,天级),匹配一个则进行限流 + // 全局限流模式下,该值为服务配额总量;单机限流模式下,该值为单个节点能处理的配额量 + repeated Amount amounts = 9; + // 限流动作,对应着客户端的插件名字 + google.protobuf.StringValue action = 10; + // 是否停用该限流规则,默认启用 + google.protobuf.BoolValue disable = 11; + // 限流上报方式,同时支持按固定周期上报,以及达到配额百分比后上报 + Report report = 12; + // 限流规则创建时间 + google.protobuf.StringValue ctime = 13; + // 限流规则修改时间 + google.protobuf.StringValue mtime = 14; + // 限流规则revision信息 + google.protobuf.StringValue revision = 15; + // 服务的TOKEN信息,仅用于控制台,discover接口不下发 + google.protobuf.StringValue service_token = 16 [json_name = "service_token"]; + // 配额调整算法 + AmountAdjuster adjuster = 17; + // 通配符是否合并计算,默认分开计数 + google.protobuf.BoolValue regex_combine = 18; + + // 限流阈值模式 + enum AmountMode { + GLOBAL_TOTAL = 0; // 总体阈值 + SHARE_EQUALLY = 1; // 单机均摊阈值 + } + AmountMode amount_mode = 19; + // 与限流集群连接失败时降级模式 + enum FailoverType { + FAILOVER_LOCAL = 0; // 降级成本地阈值 + FAILOVER_PASS = 1; // 降级成直接通过 + } + FailoverType failover = 20; + // 分布式限流服务集群 + RateLimitCluster cluster = 21; +} + +// 分布式限流服务集群 +message RateLimitCluster { + google.protobuf.StringValue service = 1; + // 限流规则所属命名空间 + google.protobuf.StringValue namespace = 2; +} + +// 限流配额 +message Amount { + // 时间周期内的最大配额数 + google.protobuf.UInt32Value maxAmount = 1; + // 配额生效的时间周期,必须大于等于1s + google.protobuf.Duration validDuration = 2; + // 请求统计精度 + google.protobuf.UInt32Value precision = 3; + // 可选,起始限流阈值,爬坡起始值 + google.protobuf.UInt32Value startAmount = 4; + // 可选,最小限流阈值,降低时最小值 + google.protobuf.UInt32Value minAmount = 5; +} + +// 限流上报方式 +message Report { + // 配额固定上报周期,单位毫秒 + google.protobuf.Duration interval = 1; + // 使用了百分之多少配额后启动一次实时上报,值范围(0,100] + google.protobuf.UInt32Value amountPercent = 2; +} + +// 配额调整算法 +message AmountAdjuster { + ClimbConfig climb = 1; +} + +// 限流调整算法Climb相关配置 +message ClimbConfig { + google.protobuf.BoolValue enable = 1; // 是否开启 + + // 限流数据统计配置 + message MetricConfig { + // 限流数据度量周期,默认60s + google.protobuf.Duration window = 1; + // 数据统计精度,决定数据度量的最小周期,度量滑窗的步长=window/precision + google.protobuf.UInt32Value precision = 2; + // 上报周期,默认20s + google.protobuf.Duration reportInterval = 3; + } + MetricConfig metric = 2; // 限流数据统计配置 + + // 触发调整的策略 + message TriggerPolicy { + // 错误率触发调整配置 + message ErrorRate { + google.protobuf.BoolValue enable = 1; // 是否开启 + google.protobuf.UInt32Value requestVolumeThreshold = 2; // 触发限流调整的最小的请求数 + google.protobuf.Int32Value errorRate = 3; // 触发限流的错误率配置 + + // 特殊错误码触发调整配置 + message SpecialConfig { + google.protobuf.StringValue type = 1; // 自定义错误类型 + repeated google.protobuf.Int64Value errorCodes = 2; // 特定规则针对的错误码 + google.protobuf.Int32Value errorRate = 3; //特定规则错误率 + } + repeated SpecialConfig specials = 4; // 针对部分错误码,使用额外的错误率统计,可设置多组特殊规则 + } + + // 慢调用触发调整配置 + message SlowRate { + google.protobuf.BoolValue enable = 1; // 是否开启 + google.protobuf.Duration maxRt = 2; // 最大响应时间,超过该响应时间属于慢调用 + google.protobuf.Int32Value slowRate = 3; // 慢请求率阈值,达到该阈值进行限流 + } + + ErrorRate errorRate = 1; // 按错误率阈值调整 + SlowRate slowRate = 2; // 慢调用进行触发调整 + } + + TriggerPolicy policy = 3; // 触发调整策略 + + // 爬坡调整相关参数 + message ClimbThrottling { + google.protobuf.Int32Value coldBelowTuneDownRate = 1; // 冷水位以下区间的下调百分比 + google.protobuf.Int32Value coldBelowTuneUpRate = 2; // 冷水位以下区间的上调百分比 + google.protobuf.Int32Value coldAboveTuneDownRate = 3; // 冷水位以上区间的下调百分比 + google.protobuf.Int32Value coldAboveTuneUpRate = 4; // 冷水位以上区间的上调百分比 + google.protobuf.Int32Value limitThresholdToTuneUp = 5; // 冷水位以上,超过该百分的请求被限流后进行阈值上调 + google.protobuf.Duration judgeDuration = 6; // 阈值调整规则的决策间隔 + google.protobuf.Int32Value tuneUpPeriod = 7; // 阈值上调周期数,连续N个决策间隔都为上调,才执行上调 + google.protobuf.Int32Value tuneDownPeriod = 8; // 阈值下调周期数,连续N个决策间隔都为下调,才执行下调 + } + + ClimbThrottling throttling = 4; // 限流调整相关参数 +} diff --git a/polaris-common/polaris-protobuf/src/main/proto/ratelimit_v2.proto b/polaris-common/polaris-protobuf/src/main/proto/ratelimit_v2.proto new file mode 100644 index 000000000..a5da135c5 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/ratelimit_v2.proto @@ -0,0 +1,168 @@ +syntax = "proto3"; + +package polaris.metric.v2; + +option go_package = "v2"; + +option java_package = "com.tencent.polaris.ratelimit.client.pb"; + +//命令字 +enum RateLimitCmd { + INIT = 0; + ACQUIRE = 1; +} + +//限流请求 +message RateLimitRequest { + //命令字 + RateLimitCmd cmd = 1; + //初始化请求 + RateLimitInitRequest rateLimitInitRequest = 2; + //上报请求 + RateLimitReportRequest rateLimitReportRequest = 3; +} + +//限流应答 +message RateLimitResponse { + //命令字 + RateLimitCmd cmd = 1; + //初始化请求 + RateLimitInitResponse rateLimitInitResponse = 2; + //上报请求 + RateLimitReportResponse rateLimitReportResponse = 3; +} + +//限频模式 +enum Mode { + //自适应模式,根据历史流量自动调整 + ADAPTIVE = 0; + //批量抢占模式,客户端进行拉取,Server返回全量剩余配额 + BATCH_OCCUPY = 1; + //批量分摊模式,客户端进行拉取,Server按比例进行分摊 + BATCH_SHARE = 2; +} + +//初始化请求 +message RateLimitInitRequest { + //限流目标对象数据 + LimitTarget target = 1; + //客户端唯一标识 + string clientId = 2; + //限流规则信息 + repeated QuotaTotal totals = 3; + //客户端可指定滑窗数,不指定用默认值 + uint32 slideCount = 4; + //限流模式 + Mode mode = 5; +} + +//初始化应答 +message RateLimitInitResponse { + //应答错误码 + uint32 code = 1; + //限流目标对象,回传给客户端 + LimitTarget target = 2; + //客户端的标识,与clientId对应,一个server全局唯一,上报时候带入 + uint32 clientKey = 3; + //计数器的标识 + repeated QuotaCounter counters = 5; + //实际滑窗个数 + uint32 slideCount = 6; + //限流server绝对时间,单位ms + int64 timestamp = 7; +} + +//限流上报请求 +message RateLimitReportRequest { + //客户端标识 + uint32 clientKey = 1; + //已使用的配额数 + repeated QuotaSum quotaUses = 2; + //配额发生的时间,单位ms + int64 timestamp = 3; +} + +//限流上报应答 +message RateLimitReportResponse { + uint32 code = 1; + //剩余配额数 + repeated QuotaLeft quotaLefts = 2; + //限流server绝对时间,单位ms + int64 timestamp = 3; +} + +//限流目标,针对哪部分数据进行限流 +message LimitTarget { + //命名空间 + string namespace = 1; + //服务名 + string service = 2; + //自定义标签 + string labels = 3; +} + +//阈值模式 +enum QuotaMode { + //整体阈值 + WHOLE = 0; + //单机均分阈值 + DIVIDE = 1; +} + +//阈值配置的值 +message QuotaTotal { + //阈值模式 + QuotaMode mode = 1; + //单位秒 + uint32 duration = 2; + //限流阈值 + uint32 maxAmount = 3; +} + +//限流计数器 +message QuotaCounter { + //单位秒 + uint32 duration = 1; + //bucket的标识,上报时候带入:namespace+service+labelStr+duration + uint32 counterKey = 2; + //剩余配额数,应答返回,允许为负数 + int64 left = 3; + //实际限流模式 + Mode mode = 4; + //接入的客户端数量 + uint32 clientCount = 5; +} + +//客户端阈值使用统计 +message QuotaSum { + //计数器的标识,一个server全局唯一,上报时候带入 + uint32 counterKey = 1; + //已使用的配额数,上报时候带入 + uint32 used = 2; + //被限流数,上报时候带入 + uint32 limited = 3; +} + +//客户端阈值使用统计,由服务端返回 +message QuotaLeft { + //计数器的标识,一个server全局唯一,上报时候带入 + uint32 counterKey = 1; + //剩余配额数,应答返回,允许为负数 + int64 left = 2; + //当前限流模式 + Mode mode = 3; + //接入的客户端数量 + uint32 clientCount = 4; +} + + +//时间点对齐的请求 +message TimeAdjustRequest { + +} + +//时间点对齐的应答 +message TimeAdjustResponse { + //服务器时间点,毫秒 + int64 serverTimestamp = 1; +} diff --git a/polaris-common/polaris-protobuf/src/main/proto/request.proto b/polaris-common/polaris-protobuf/src/main/proto/request.proto new file mode 100644 index 000000000..c6be194a3 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/request.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package v1; + +import "service.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "RequestProto"; + +message DiscoverRequest { + enum DiscoverRequestType { + UNKNOWN = 0; + INSTANCE = 1; + CLUSTER = 2; + ROUTING = 3; + RATE_LIMIT = 4; + CIRCUIT_BREAKER = 5; + SERVICES = 6; + } + + DiscoverRequestType type = 1; + Service service = 2; +} diff --git a/polaris-common/polaris-protobuf/src/main/proto/response.proto b/polaris-common/polaris-protobuf/src/main/proto/response.proto new file mode 100644 index 000000000..8f8f604e1 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/response.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; +import "service.proto"; +import "routing.proto"; +import "client.proto"; +import "ratelimit.proto"; +import "circuitbreaker.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "ResponseProto"; + +message SimpleResponse { + google.protobuf.UInt32Value code = 1; + google.protobuf.StringValue info = 2; +} + +message Response { + google.protobuf.UInt32Value code = 1; + google.protobuf.StringValue info = 2; + Client client = 3; + Namespace namespace = 4; + Service service = 5; + Instance instance = 6; + Routing routing = 7; + ServiceAlias alias = 8; + Rule rateLimit = 9; + CircuitBreaker circuitBreaker = 10; +} + +message BatchWriteResponse { + google.protobuf.UInt32Value code = 1; + google.protobuf.StringValue info = 2; + google.protobuf.UInt32Value size = 3; + repeated Response responses = 4; +} + +message BatchQueryResponse { + google.protobuf.UInt32Value code = 1; + google.protobuf.StringValue info = 2; + google.protobuf.UInt32Value amount = 3; + google.protobuf.UInt32Value size = 4; + repeated Namespace namespaces = 5; + repeated Service services = 6; + repeated Instance instances = 7; + repeated Routing routings = 8; + repeated ServiceAlias aliases = 9; + repeated Rule rateLimits = 10; +} + +message DiscoverResponse { + google.protobuf.UInt32Value code = 1; + google.protobuf.StringValue info = 2; + + enum DiscoverResponseType { + UNKNOWN = 0; + INSTANCE = 1; + CLUSTER = 2; + ROUTING = 3; + RATE_LIMIT = 4; + CIRCUIT_BREAKER = 5; + SERVICES = 6; + } + + DiscoverResponseType type = 3; + Service service = 4; + repeated Instance instances = 5; + Routing routing = 6; + RateLimit rateLimit = 7; + CircuitBreaker circuitBreaker = 8; + repeated Service services = 9; +} diff --git a/polaris-common/polaris-protobuf/src/main/proto/routing.proto b/polaris-common/polaris-protobuf/src/main/proto/routing.proto new file mode 100644 index 000000000..dd0dd923c --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/routing.proto @@ -0,0 +1,72 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; +import "model.proto"; + +option java_outer_classname="RoutingProto"; +option java_package="com.tencent.polaris.client.pb"; + +message Routing { + // 规则所属服务以及命名空间 + google.protobuf.StringValue service = 1; + google.protobuf.StringValue namespace = 2; + + // 每个服务可以配置多条入站或者出站规则 + // 对于每个请求,从上到下依次匹配,若命中则终止 + repeated Route inbounds = 3; + repeated Route outbounds = 4; + + google.protobuf.StringValue ctime = 5; + google.protobuf.StringValue mtime = 6; + google.protobuf.StringValue revision = 7; + + google.protobuf.StringValue service_token = 8 [json_name="service_token"]; +} + +message Route { + // 如果匹配Source规则,按照Destination路由 + // 多个Source之间的关系为或 + repeated Source sources = 1; + repeated Destination destinations = 2; +} + +message Source { + // 主调方服务以及命名空间 + google.protobuf.StringValue service = 1; + google.protobuf.StringValue namespace = 2; + + // 主调方服务实例标签或者请求标签 + // value支持正则匹配 + map metadata = 3; +} + +message Destination { + // 被调方服务以及命名空间 + google.protobuf.StringValue service = 1; + google.protobuf.StringValue namespace = 2; + + // 被调方服务实例标签 + // value支持正则匹配 + map metadata = 3; + + // 根据服务名和服务实例metadata筛选符合条件的服务实例子集 + // 服务实例子集可以设置优先级和权重 + // 优先级:整型,范围[0, 9],最高优先级为0 + // 权重:整型 + // 先按优先级路由,如果存在高优先级,不会使用低优先级 + // 如果存在优先级相同的子集,再按权重分配 + // 优先级和权重可以都不设置/设置一个/设置两个 + // 如果部分设置优先级,部分没有设置,认为没有设置的优先级最低 + // 如果部分设置权重,部分没有设置,认为没有设置的权重为0 + // 如果全部没有设置权重,认为权重相同 + google.protobuf.UInt32Value priority = 4; + google.protobuf.UInt32Value weight = 5; + + // 将请求转发到代理服务 + google.protobuf.StringValue transfer = 6; + + //是否对该set执行隔离,隔离后,不会再分配流量 + google.protobuf.BoolValue isolate = 7; +} \ No newline at end of file diff --git a/polaris-common/polaris-protobuf/src/main/proto/service.proto b/polaris-common/polaris-protobuf/src/main/proto/service.proto new file mode 100644 index 000000000..7cf0b7a22 --- /dev/null +++ b/polaris-common/polaris-protobuf/src/main/proto/service.proto @@ -0,0 +1,100 @@ +syntax = "proto3"; + +package v1; + +import "google/protobuf/wrappers.proto"; +import "model.proto"; + +option java_package = "com.tencent.polaris.client.pb"; +option java_outer_classname = "ServiceProto"; + +message Namespace { + google.protobuf.StringValue name = 1; + google.protobuf.StringValue comment = 2; + google.protobuf.StringValue owners = 3; + google.protobuf.StringValue token = 4; + google.protobuf.StringValue ctime = 5; + google.protobuf.StringValue mtime = 6; +} + +message Service { + google.protobuf.StringValue name = 1; + google.protobuf.StringValue namespace = 2; + + map metadata = 3; + + google.protobuf.StringValue ports = 4; + google.protobuf.StringValue business = 5; + google.protobuf.StringValue department = 6; + google.protobuf.StringValue cmdb_mod1 = 7 [json_name = "cmdb_mod1"]; + google.protobuf.StringValue cmdb_mod2 = 8 [json_name = "cmdb_mod2"]; + google.protobuf.StringValue cmdb_mod3 = 9 [json_name = "cmdb_mod3"]; + google.protobuf.StringValue comment = 10; + google.protobuf.StringValue owners = 11; + google.protobuf.StringValue token = 12; + + google.protobuf.StringValue ctime = 13; + google.protobuf.StringValue mtime = 14; + google.protobuf.StringValue revision = 15; + google.protobuf.StringValue platform_id = 16 [json_name = "platform_id"]; +} + +enum AliasType { + DEFAULT = 0; + CL5SID = 1; +} + +message ServiceAlias { + google.protobuf.StringValue service = 1; + google.protobuf.StringValue namespace = 2; + google.protobuf.StringValue alias = 3; + AliasType type = 4; + google.protobuf.StringValue owners = 8; + google.protobuf.StringValue comment = 9; + google.protobuf.StringValue service_token = 5 [json_name = "service_token"]; + + google.protobuf.StringValue ctime = 6; + google.protobuf.StringValue mtime = 7; +} + +message Instance { + google.protobuf.StringValue id = 1; + google.protobuf.StringValue service = 2; + google.protobuf.StringValue namespace = 3; + google.protobuf.StringValue vpc_id = 21 [json_name = "vpc_id"]; + google.protobuf.StringValue host = 4; + google.protobuf.UInt32Value port = 5; + google.protobuf.StringValue protocol = 6; + google.protobuf.StringValue version = 7; + google.protobuf.UInt32Value priority = 8; + google.protobuf.UInt32Value weight = 9; + google.protobuf.BoolValue enable_health_check = 20; + HealthCheck health_check = 10; + google.protobuf.BoolValue healthy = 11; + google.protobuf.BoolValue isolate = 12; + Location location = 13; + + map metadata = 14; + google.protobuf.StringValue logic_set = 15 [json_name = "logic_set"]; + + google.protobuf.StringValue ctime = 16; + google.protobuf.StringValue mtime = 17; + google.protobuf.StringValue revision = 18; + + google.protobuf.StringValue service_token = 19 [json_name = "service_token"]; +} + +message HealthCheck { + enum HealthCheckType { + UNKNOWN = 0; + HEARTBEAT = 1; + } + + HealthCheckType type = 1; + + HeartbeatHealthCheck heartbeat = 2; +} + +message HeartbeatHealthCheck { + google.protobuf.UInt32Value ttl = 1; +} diff --git a/polaris-common/pom.xml b/polaris-common/pom.xml new file mode 100644 index 000000000..397613fd4 --- /dev/null +++ b/polaris-common/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + polaris-common + pom + + + polaris-config + polaris-model + polaris-protobuf + polaris-config-opensource + polaris-config-tencent + + \ No newline at end of file diff --git a/polaris-dependencies/pom.xml b/polaris-dependencies/pom.xml new file mode 100644 index 000000000..7983184f3 --- /dev/null +++ b/polaris-dependencies/pom.xml @@ -0,0 +1,222 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + pom + polaris-dependencies + + + + + com.tencent.nameservice + polaris-protobuf + ${project.version} + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-config + ${project.version} + + + com.tencent.nameservice + polaris-config-tencent + ${project.version} + + + com.tencent.nameservice + polaris-config-opensource + ${project.version} + + + com.tencent.nameservice + polaris-client + ${project.version} + + + + + com.tencent.nameservice + polaris-factory + ${project.version} + + + + + com.tencent.nameservice + polaris-discovery-api + ${project.version} + + + com.tencent.nameservice + polaris-discovery-client + ${project.version} + + + com.tencent.nameservice + polaris-discovery-factory + ${project.version} + + + + + com.tencent.nameservice + polaris-router-api + ${project.version} + + + com.tencent.nameservice + polaris-router-client + ${project.version} + + + com.tencent.nameservice + polaris-router-factory + ${project.version} + + + + + com.tencent.nameservice + polaris-circuitbreaker-api + ${project.version} + + + com.tencent.nameservice + polaris-circuitbreaker-client + ${project.version} + + + com.tencent.nameservice + polaris-circuitbreaker-factory + ${project.version} + + + + + com.tencent.nameservice + polaris-ratelimit-api + ${project.version} + + + com.tencent.nameservice + polaris-ratelimit-client + ${project.version} + + + com.tencent.nameservice + polaris-ratelimit-factory + ${project.version} + + + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + connector-polaris-grpc + ${project.version} + + + com.tencent.nameservice + resource-cache-memory + ${project.version} + + + com.tencent.nameservice + flow-cache-expired + ${project.version} + + + com.tencent.nameservice + router-isolated + ${project.version} + + + com.tencent.nameservice + router-healthy + ${project.version} + + + com.tencent.nameservice + router-rule + ${project.version} + + + com.tencent.nameservice + router-metadata + ${project.version} + + + com.tencent.nameservice + router-nearby + ${project.version} + + + com.tencent.nameservice + router-set + ${project.version} + + + com.tencent.nameservice + router-canary + ${project.version} + + + com.tencent.nameservice + loadbalancer-random + ${project.version} + + + com.tencent.nameservice + loadbalancer-ringhash + ${project.version} + + + com.tencent.nameservice + circuitbreaker-errcount + ${project.version} + + + com.tencent.nameservice + circuitbreaker-errrate + ${project.version} + + + com.tencent.nameservice + ratelimiter-reject + ${project.version} + + + + com.tencent.nameservice + polaris-test-common + ${project.version} + + + com.tencent.nameservice + polaris-test-mock-discovery + ${project.version} + + + com.tencent.nameservice + stat-prometheus + ${project.version} + + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-api/pom.xml b/polaris-discovery/polaris-discovery-api/pom.xml new file mode 100644 index 000000000..8f382d2a5 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-discovery + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-discovery-api + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ConsumerAPI.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ConsumerAPI.java new file mode 100644 index 000000000..e5b939b72 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ConsumerAPI.java @@ -0,0 +1,118 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.api.core; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.GetServiceRuleRequest; +import com.tencent.polaris.api.rpc.InstancesFuture; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.api.rpc.ServiceRuleResponse; +import java.io.Closeable; + +/** + * 主调端相关的接口API + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface ConsumerAPI extends AutoCloseable, Closeable { + + /** + * 同步获取服务下全量服务列表 + * + * @param req 请求 + * @return 服务实例列表 + * @throws PolarisException 错误码及错误信息 + */ + InstancesResponse getAllInstance(GetAllInstancesRequest req) throws PolarisException; + + /** + * 同步获取服务下单个服务实例 + * + * @param req 请求 + * @return 单个服务实例 + * @throws PolarisException 错误码及错误信息 + */ + InstancesResponse getOneInstance(GetOneInstanceRequest req) throws PolarisException; + + /** + * 同步获取服务下进过路由过滤后的服务列表 + * + * @param req 请求 + * @return 过滤后的服务列表 + * @throws PolarisException 错误码及错误信息 + */ + InstancesResponse getInstances(GetInstancesRequest req) throws PolarisException; + + /** + * 异步获取服务下全量服务列表 + * + * @param req 请求 + * @return 服务实例列表 + * @throws PolarisException 错误码及错误信息 + */ + InstancesFuture asyncGetAllInstances(GetAllInstancesRequest req) throws PolarisException; + + /** + * 异步获取服务下单个服务实例 + * + * @param req 请求 + * @return 单个服务实例 + * @throws PolarisException 错误码及错误信息 + */ + InstancesFuture asyncGetOneInstance(GetOneInstanceRequest req) throws PolarisException; + + /** + * 异步获取服务下进过路由过滤后的服务列表 + * + * @param req 请求 + * @return 过滤后的服务列表 + * @throws PolarisException 错误码及错误信息 + */ + InstancesFuture asyncGetInstances(GetInstancesRequest req) throws PolarisException; + + /** + * 上报调用结果信息 + * + * @param req 调用结果(包括成功失败,返回码,以及时延) + * @throws PolarisException 错误码及错误信息 + */ + void updateServiceCallResult(ServiceCallResult req) throws PolarisException; + + /** + * 获取服务规则 + * + * @param req 请求参数 + * @return 服务规则信息 + * @throws PolarisException 错误码及错误信息 + */ + ServiceRuleResponse getServiceRule(GetServiceRuleRequest req) throws PolarisException; + + /** + * 清理并释放资源 + */ + void destroy(); + + @Override + default void close() { + destroy(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ProviderAPI.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ProviderAPI.java new file mode 100644 index 000000000..b617b8723 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/core/ProviderAPI.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.core; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; +import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterResponse; +import java.io.Closeable; + +/** + * 被调端相关的接口API + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface ProviderAPI extends AutoCloseable, Closeable { + + /** + * 同步注册服务实例 + * + * @param req 注册请求 + * @return 服务实例ID + * @throws PolarisException 错误码及异常信息 + */ + InstanceRegisterResponse register(InstanceRegisterRequest req) throws PolarisException; + + /** + * 同步反注册服务实例 + * + * @param req 服务实例ID + * @throws PolarisException 错误码及异常信息 + */ + void deRegister(InstanceDeregisterRequest req) throws PolarisException; + + /** + * 同步进行心跳上报 + * + * @param req 服务实例ID + * @throws PolarisException 错误码及异常信息 + */ + void heartbeat(InstanceHeartbeatRequest req) throws PolarisException; + + /** + * 清理并释放资源 + */ + void destroy(); + + @Override + default void close() { + destroy(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/CommonProviderBaseEntity.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/CommonProviderBaseEntity.java new file mode 100644 index 000000000..ab7c1a436 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/CommonProviderBaseEntity.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.plugin.server.CommonProviderRequest; + +public class CommonProviderBaseEntity extends RequestBaseEntity { + + protected final CommonProviderRequest request = new CommonProviderRequest(); + + @Override + public String getService() { + return request.getService(); + } + + @Override + public void setService(String service) { + request.setService(service); + } + + @Override + public String getNamespace() { + return request.getNamespace(); + } + + @Override + public void setNamespace(String namespace) { + request.setNamespace(namespace); + } + + public String getToken() { + return request.getToken(); + } + + public void setToken(String token) { + request.setToken(token); + } + + public String getHost() { + return request.getHost(); + } + + public void setHost(String host) { + request.setHost(host); + } + + public Integer getPort() { + return request.getPort(); + } + + public void setPort(Integer port) { + request.setPort(port); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetAllInstancesRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetAllInstancesRequest.java new file mode 100644 index 000000000..e75a28462 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetAllInstancesRequest.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import java.util.Map; + +/** + * 获取全量服务实例请求 + */ +public class GetAllInstancesRequest extends RequestBaseEntity { + + /** + * 服务元数据信息,用于服务路由过滤 + */ + private Map metadata; + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "GetAllInstancesRequest{" + + "metadata=" + metadata + + '}'; + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetInstancesRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetInstancesRequest.java new file mode 100644 index 000000000..68a6ba6ad --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetInstancesRequest.java @@ -0,0 +1,135 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceMetadata; +import java.util.Map; + +/** + * 批量服务实例查询请求 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class GetInstancesRequest extends RequestBaseEntity { + + /** + * 服务元数据信息,用于服务路由过滤 + */ + private Map metadata; + + /** + * 主调方服务信息 + */ + private ServiceMetadata serviceInfo; + + /** + * 是否返回熔断实例,默认否 + */ + private boolean includeCircuitBreak; + /** + * 是否返回不健康的服务实例,默认否 + */ + private boolean includeUnhealthy; + + /** + * 金丝雀集群 + */ + private String canary; + + /** + * 接口参数 + */ + private String method; + + /** + * 可选, metadata失败降级策略 + */ + private MetadataFailoverType metadataFailoverType; + + public boolean isIncludeCircuitBreak() { + return includeCircuitBreak; + } + + public void setIncludeCircuitBreak(boolean includeCircuitBreak) { + this.includeCircuitBreak = includeCircuitBreak; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public boolean isIncludeUnhealthy() { + return includeUnhealthy; + } + + public void setIncludeUnhealthy(boolean includeUnhealthy) { + this.includeUnhealthy = includeUnhealthy; + } + + + public ServiceMetadata getServiceInfo() { + return serviceInfo; + } + + public void setServiceInfo(ServiceMetadata serviceInfo) { + this.serviceInfo = serviceInfo; + } + + public String getCanary() { + return canary; + } + + public void setCanary(String canary) { + this.canary = canary; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public MetadataFailoverType getMetadataFailoverType() { + return metadataFailoverType; + } + + public void setMetadataFailoverType(MetadataFailoverType metadataFailoverType) { + this.metadataFailoverType = metadataFailoverType; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "GetInstancesRequest{" + + "metadata=" + metadata + + ", serviceInfo=" + serviceInfo + + ", includeCircuitBreak=" + includeCircuitBreak + + ", includeUnhealthy=" + includeUnhealthy + + ", canary='" + canary + '\'' + + ", method='" + method + '\'' + + ", metadataFailoverType=" + metadataFailoverType + + "} " + super.toString(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetOneInstanceRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetOneInstanceRequest.java new file mode 100644 index 000000000..afc3886d9 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetOneInstanceRequest.java @@ -0,0 +1,121 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceMetadata; +import java.util.Map; + +/** + * 单个服务实例查询请求 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class GetOneInstanceRequest extends RequestBaseEntity { + + /** + * 可选,服务元数据信息,用于服务路由过滤 + */ + private Map metadata; + + /** + * 所属的金丝雀集群 + */ + private String canary; + + /** + * 可选,负载均衡辅助参数 + */ + private Criteria criteria; + + /** + * 接口参数 + */ + private String method; + + /** + * 可选, metadata失败降级策略 + */ + private MetadataFailoverType metadataFailoverType; + + /** + * 主调方服务信息 + */ + private ServiceMetadata serviceInfo; + + public Criteria getCriteria() { + return criteria; + } + + public void setCriteria(Criteria criteria) { + this.criteria = criteria; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public String getCanary() { + return canary; + } + + public void setCanary(String canary) { + this.canary = canary; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public ServiceMetadata getServiceInfo() { + return serviceInfo; + } + + public void setServiceInfo(ServiceMetadata serviceInfo) { + this.serviceInfo = serviceInfo; + } + + public MetadataFailoverType getMetadataFailoverType() { + return metadataFailoverType; + } + + public void setMetadataFailoverType(MetadataFailoverType metadataFailoverType) { + this.metadataFailoverType = metadataFailoverType; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "GetOneInstanceRequest{" + + "metadata=" + metadata + + ", canary='" + canary + '\'' + + ", criteria=" + criteria + + ", method='" + method + '\'' + + ", metadataFailoverType=" + metadataFailoverType + + ", serviceInfo=" + serviceInfo + + "} " + super.toString(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesRequest.java new file mode 100644 index 000000000..6a960c48f --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesRequest.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; +import java.util.Set; + +public class GetResourcesRequest extends RequestBaseEntity implements ServiceEventKeysProvider { + + private Set svcEventKeys; + + public boolean isUseCache() { + return false; + } + + @Override + public Set getSvcEventKeys() { + return svcEventKeys; + } + + @Override + public ServiceEventKey getSvcEventKey() { + return null; + } + + public void setSvcEventKeys(Set svcEventKeys) { + this.svcEventKeys = svcEventKeys; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "GetResourcesRequest{" + + "svcEventKeys=" + svcEventKeys + + "} " + super.toString(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesResponse.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesResponse.java new file mode 100644 index 000000000..1c5f49388 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetResourcesResponse.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceRule; +import java.util.Map; + +/** + * 获取资源的应答 + */ +public class GetResourcesResponse { + + private final Map services; + + private final Map rules; + + public GetResourcesResponse( + Map services, Map rules) { + this.services = services; + this.rules = rules; + } + + public Map getServices() { + return services; + } + + public Map getRules() { + return rules; + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetServiceRuleRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetServiceRuleRequest.java new file mode 100644 index 000000000..e4caf0a24 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/GetServiceRuleRequest.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 服务实例拉取请求 + */ +public class GetServiceRuleRequest extends RequestBaseEntity { + + private ServiceEventKey.EventType ruleType; + + public ServiceEventKey.EventType getRuleType() { + return ruleType; + } + + public void setRuleType(ServiceEventKey.EventType ruleType) { + this.ruleType = ruleType; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "GetServiceRuleRequest{" + + "ruleType=" + ruleType + + '}'; + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceDeregisterRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceDeregisterRequest.java new file mode 100644 index 000000000..492292a3c --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceDeregisterRequest.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.plugin.server.CommonProviderRequest; + +/** + * 实例反注册请求 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class InstanceDeregisterRequest extends CommonProviderBaseEntity { + + public String getInstanceID() { + return request.getInstanceID(); + } + + public void setInstanceID(String instanceID) { + request.setInstanceID(instanceID); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "InstanceDeregisterRequest{" + + "request=" + request + + '}'; + } + + public CommonProviderRequest getRequest() { + return request; + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceHeartbeatRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceHeartbeatRequest.java new file mode 100644 index 000000000..1cccda715 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceHeartbeatRequest.java @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +/** + * 服务实例心跳上报请求 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class InstanceHeartbeatRequest extends InstanceDeregisterRequest { + + @Override + public String toString() { + return "InstanceHeartbeatRequest{} " + super.toString(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterRequest.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterRequest.java new file mode 100644 index 000000000..3aaf97712 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterRequest.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.plugin.server.CommonProviderRequest; +import java.util.Map; + +/** + * 服务实例注册请求 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class InstanceRegisterRequest extends CommonProviderBaseEntity { + + public String getVersion() { + return request.getVersion(); + } + + public void setVersion(String version) { + request.setVersion(version); + } + + public String getProtocol() { + return request.getProtocol(); + } + + public void setProtocol(String protocol) { + request.setProtocol(protocol); + } + + public Integer getWeight() { + return request.getWeight(); + } + + public void setWeight(Integer weight) { + request.setWeight(weight); + } + + public Integer getPriority() { + return request.getPriority(); + } + + public void setPriority(Integer priority) { + request.setPriority(priority); + } + + public Map getMetadata() { + return request.getMetadata(); + } + + public void setMetadata(Map metadata) { + request.setMetadata(metadata); + } + + /** + * 心跳上报的TTL,单位秒 + * + * @return ttl + */ + public Integer getTtl() { + return request.getTtl(); + } + + public void setTtl(Integer ttl) { + request.setTtl(ttl); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "InstanceRegisterRequest{" + + "request=" + request + + '}'; + } + + public CommonProviderRequest getRequest() { + return request; + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterResponse.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterResponse.java new file mode 100644 index 000000000..404ebf890 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstanceRegisterResponse.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +/** + * 服务实例注册应答 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class InstanceRegisterResponse { + + private final String instanceId; + + private final boolean exists; + + public InstanceRegisterResponse(String instanceId, boolean exists) { + this.instanceId = instanceId; + this.exists = exists; + } + + public String getInstanceId() { + return instanceId; + } + + public boolean isExists() { + return exists; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "InstanceRegisterResponse{" + + "instanceId='" + instanceId + '\'' + + ", exists=" + exists + + '}'; + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesFuture.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesFuture.java new file mode 100644 index 000000000..43fc82b3c --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesFuture.java @@ -0,0 +1,27 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import java.util.concurrent.CompletableFuture; + +/** + * 异步获取实例的应答 + */ +public class InstancesFuture extends CompletableFuture { + +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesResponse.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesResponse.java new file mode 100644 index 000000000..a7f432e65 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/InstancesResponse.java @@ -0,0 +1,87 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceInstancesWrap; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +/** + * Response for instances query request. + * + * @author andrewshan + * @date 2019/8/21 + */ +public class InstancesResponse extends BaseEntity { + + private final ServiceInstances serviceInstances; + + private final Map metadata; + + private final int totalWeight; + + private final Instance[] instances; + + public InstancesResponse(ServiceInstances serviceInstances) { + this.serviceInstances = serviceInstances; + this.metadata = serviceInstances.getMetadata(); + this.setService(serviceInstances.getService()); + this.setNamespace(serviceInstances.getNamespace()); + Collection svcInstances = serviceInstances.getInstances(); + this.instances = svcInstances.toArray(new Instance[svcInstances.size()]); + this.totalWeight = serviceInstances.getTotalWeight(); + } + + public InstancesResponse(ServiceInstances serviceInstances, Instance singleInstance) { + this.serviceInstances = serviceInstances; + this.metadata = serviceInstances.getMetadata(); + this.setService(serviceInstances.getService()); + this.setNamespace(serviceInstances.getNamespace()); + this.instances = new Instance[]{singleInstance}; + this.totalWeight = serviceInstances.getTotalWeight(); + } + + public Map getMetadata() { + return metadata; + } + + public Instance[] getInstances() { + return instances; + } + + public int getTotalWeight() { + return totalWeight; + } + + public ServiceInstances toServiceInstances() { + return new ServiceInstancesWrap(serviceInstances, Arrays.asList(getInstances()), totalWeight); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "InstancesResponse{" + + "metadata=" + metadata + + ", totalWeight=" + totalWeight + + ", instances=" + Arrays.toString(instances) + + "} " + super.toString(); + } +} diff --git a/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/ServiceRuleResponse.java b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/ServiceRuleResponse.java new file mode 100644 index 000000000..c0c24c654 --- /dev/null +++ b/polaris-discovery/polaris-discovery-api/src/main/java/com/tencent/polaris/api/rpc/ServiceRuleResponse.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 - 2020. THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceRule; + +/** + * Response for instances query request. + * + * @author andrewshan + * @date 2019/8/21 + */ +public class ServiceRuleResponse extends BaseEntity { + + private final ServiceRule serviceRule; + + public ServiceRuleResponse(ServiceRule serviceRule) { + this.serviceRule = serviceRule; + } + + @Override + public String toString() { + return "ServiceRuleResponse{ service_rule=" + serviceRule + " }" + super.toString(); + } + + public ServiceRule getServiceRule() { + return serviceRule; + } +} diff --git a/polaris-discovery/polaris-discovery-client/pom.xml b/polaris-discovery/polaris-discovery-client/pom.xml new file mode 100644 index 000000000..c53cf253f --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/pom.xml @@ -0,0 +1,32 @@ + + + + polaris-discovery + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-discovery-client + + + + com.tencent.nameservice + polaris-discovery-api + ${project.version} + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultConsumerAPI.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultConsumerAPI.java new file mode 100644 index 000000000..29fcc836b --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultConsumerAPI.java @@ -0,0 +1,148 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.api; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.GetServiceRuleRequest; +import com.tencent.polaris.api.rpc.InstancesFuture; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.api.rpc.ServiceRuleResponse; +import com.tencent.polaris.client.api.BaseEngine; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.api.ServiceCallResultListener; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.discovery.client.flow.AsyncFlow; +import com.tencent.polaris.discovery.client.flow.CommonInstancesRequest; +import com.tencent.polaris.discovery.client.flow.CommonRuleRequest; +import com.tencent.polaris.discovery.client.flow.SyncFlow; +import com.tencent.polaris.discovery.client.util.Validator; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ConsumerAPI的标准实现 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class DefaultConsumerAPI extends BaseEngine implements ConsumerAPI { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultConsumerAPI.class); + + private final Configuration config; + + private final SyncFlow syncFlow = new SyncFlow(); + + private final AsyncFlow asyncFlow = new AsyncFlow(); + + private List serviceCallResultListeners; + + public DefaultConsumerAPI(SDKContext context) { + super(context); + config = context.getConfig(); + syncFlow.init(context.getExtensions()); + asyncFlow.init(syncFlow); + } + + @Override + protected void subInit() { + serviceCallResultListeners = ServiceCallResultListener.getServiceCallResultListeners(sdkContext); + sdkContext.registerDestroyHook(new Destroyable() { + @Override + protected void doDestroy() { + if (null != serviceCallResultListeners) { + for (ServiceCallResultListener listener : serviceCallResultListeners) { + listener.destroy(); + } + } + } + }); + } + + @Override + public InstancesResponse getAllInstance(GetAllInstancesRequest req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetAllInstancesRequest(req); + CommonInstancesRequest allRequest = new CommonInstancesRequest(req, config); + return syncFlow.commonSyncGetAllInstances(allRequest); + } + + @Override + public InstancesResponse getOneInstance(GetOneInstanceRequest req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetOneInstanceRequest(req); + CommonInstancesRequest allRequest = new CommonInstancesRequest(req, config); + return syncFlow.commonSyncGetOneInstance(allRequest); + } + + @Override + public InstancesResponse getInstances(GetInstancesRequest req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetInstancesRequest(req); + CommonInstancesRequest allRequest = new CommonInstancesRequest(req, config); + return syncFlow.commonSyncGetInstances(allRequest); + } + + @Override + public InstancesFuture asyncGetOneInstance(GetOneInstanceRequest req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetOneInstanceRequest(req); + CommonInstancesRequest allRequest = new CommonInstancesRequest(req, config); + return asyncFlow.commonAsyncGetOneInstance(allRequest); + } + + @Override + public InstancesFuture asyncGetInstances(GetInstancesRequest req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetInstancesRequest(req); + CommonInstancesRequest allRequest = new CommonInstancesRequest(req, config); + return asyncFlow.commonAsyncGetInstances(allRequest); + } + + @Override + public InstancesFuture asyncGetAllInstances(GetAllInstancesRequest req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetAllInstancesRequest(req); + CommonInstancesRequest allRequest = new CommonInstancesRequest(req, config); + return asyncFlow.commonAsyncGetAllInstances(allRequest); + } + + @Override + public void updateServiceCallResult(ServiceCallResult req) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateServiceCallResult(req); + for (ServiceCallResultListener listener : serviceCallResultListeners) { + listener.onServiceCallResult(req); + } + } + + @Override + public ServiceRuleResponse getServiceRule(GetServiceRuleRequest request) throws PolarisException { + checkAvailable("ConsumerAPI"); + Validator.validateGetServiceRuleRequest(request); + CommonRuleRequest commonRuleRequest = new CommonRuleRequest(request, config); + return syncFlow.commonSyncGetServiceRule(commonRuleRequest); + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultProviderAPI.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultProviderAPI.java new file mode 100644 index 000000000..db9fe7b46 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/api/DefaultProviderAPI.java @@ -0,0 +1,118 @@ +package com.tencent.polaris.discovery.client.api; + +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.RetriableException; +import com.tencent.polaris.api.plugin.server.CommonProviderResponse; +import com.tencent.polaris.api.plugin.server.ServerConnector; +import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; +import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterResponse; +import com.tencent.polaris.client.api.BaseEngine; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.discovery.client.util.Validator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ProviderAPI的标准实现 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class DefaultProviderAPI extends BaseEngine implements ProviderAPI { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultProviderAPI.class); + + private ServerConnector serverConnector; + + public DefaultProviderAPI(SDKContext sdkContext) { + super(sdkContext); + } + + @Override + protected void subInit() { + serverConnector = sdkContext.getExtensions().getServerConnector(); + } + + @Override + public InstanceRegisterResponse register(InstanceRegisterRequest req) throws PolarisException { + checkAvailable("ProviderAPI"); + Validator.validateInstanceRegisterRequest(req); + long retryInterval = sdkContext.getConfig().getGlobal().getAPI().getRetryInterval(); + long timeout = getTimeout(req); + while (timeout > 0) { + long start = System.currentTimeMillis(); + try { + CommonProviderResponse response = serverConnector.registerInstance(req.getRequest()); + LOG.info("register {}/{} instance {} succ", req.getNamespace(), req.getService(), + response.getInstanceID()); + return new InstanceRegisterResponse(response.getInstanceID(), response.isExists()); + } catch (PolarisException e) { + if (e instanceof RetriableException) { + LOG.warn("instance register request error, retrying.", e); + Utils.sleepUninterrupted(retryInterval); + continue; + } + throw e; + } finally { + timeout -= System.currentTimeMillis() - start; + } + } + throw new PolarisException(ErrorCode.API_TIMEOUT, "instance register request timeout."); + } + + @Override + public void deRegister(InstanceDeregisterRequest req) throws PolarisException { + checkAvailable("ProviderAPI"); + Validator.validateInstanceDeregisterRequest(req); + long retryInterval = sdkContext.getConfig().getGlobal().getAPI().getRetryInterval(); + long timeout = getTimeout(req); + while (timeout > 0) { + long start = System.currentTimeMillis(); + try { + serverConnector.deregisterInstance(req.getRequest()); + LOG.info("deregister instance {} succ", req); + return; + } catch (PolarisException e) { + if (e instanceof RetriableException) { + LOG.warn("instance deregister request error, retrying.", e); + Utils.sleepUninterrupted(retryInterval); + continue; + } + throw e; + } finally { + timeout -= System.currentTimeMillis() - start; + } + } + throw new PolarisException(ErrorCode.API_TIMEOUT, "instance deregister request timeout."); + } + + @Override + public void heartbeat(InstanceHeartbeatRequest req) throws PolarisException { + checkAvailable("ProviderAPI"); + Validator.validateHeartbeatRequest(req); + long timeout = getTimeout(req); + long retryInterval = sdkContext.getConfig().getGlobal().getAPI().getRetryInterval(); + while (timeout > 0) { + long start = System.currentTimeMillis(); + try { + serverConnector.heartbeat(req.getRequest()); + return; + } catch (PolarisException e) { + if (e instanceof RetriableException) { + LOG.warn("heartbeat request error, retrying.", e); + Utils.sleepUninterrupted(retryInterval); + continue; + } + throw e; + } finally { + timeout -= System.currentTimeMillis() - start; + } + } + throw new PolarisException(ErrorCode.API_TIMEOUT, "heartbeat request timeout."); + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/AsyncFlow.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/AsyncFlow.java new file mode 100644 index 000000000..96823c4f1 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/AsyncFlow.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.rpc.InstancesFuture; + +/** + * AsyncFlow.java + * + * @author andrewshan + * @date 2019/8/24 + */ +public class AsyncFlow { + + private SyncFlow syncFlow; + + public void init(SyncFlow syncFlow) { + this.syncFlow = syncFlow; + } + + /** + * 异步获取单个服务实例 + * + * @param request 请求参数 + * @return future + */ + public InstancesFuture commonAsyncGetOneInstance(CommonInstancesRequest request) { + GetOneInstanceSupplier supplier = new GetOneInstanceSupplier(request, syncFlow); + return (InstancesFuture) InstancesFuture.supplyAsync(supplier); + } + + /** + * 异步获取路由后的服务实例 + * + * @param request 参数对象 + * @return 路由后的实例列表 + * @throws PolarisException 获取失败异常 + */ + public InstancesFuture commonAsyncGetInstances(CommonInstancesRequest request) throws PolarisException { + GetInstancesSupplier supplier = new GetInstancesSupplier(request, syncFlow); + return (InstancesFuture) InstancesFuture.supplyAsync(supplier); + } + + /** + * 异步获取全量服务实例 + * + * @param request 请求对象 + * @return 全量服务实例 + * @throws PolarisException 获取失败 + */ + public InstancesFuture commonAsyncGetAllInstances(CommonInstancesRequest request) throws PolarisException { + GetAllInstancesSupplier supplier = new GetAllInstancesSupplier(request, syncFlow); + return (InstancesFuture) InstancesFuture.supplyAsync(supplier); + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonInstancesRequest.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonInstancesRequest.java new file mode 100644 index 000000000..639ccdbc5 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonInstancesRequest.java @@ -0,0 +1,218 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.rpc.Criteria; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.FlowControlParam; +import java.util.HashSet; +import java.util.Set; + +/** + * 基础的服务信息请求 + */ +public class CommonInstancesRequest implements ServiceEventKeysProvider, FlowControlParam { + + private final ServiceEventKey srcRuleEventKey; + + private final ServiceEventKey dstInstanceEventKey; + + private final ServiceEventKey dstRuleEventKey; + + private final RouteInfo routeInfo; + + private ServiceInstances dstInstances; + + private final Criteria criteria; + + private final Set svcEventKeys = new HashSet<>(); + + private long timeoutMs; + + private int maxRetry; + + private long retryIntervalMs; + + /** + * 构造函数 + * + * @param request 请求 + * @param configuration 配置 + */ + public CommonInstancesRequest(GetAllInstancesRequest request, Configuration configuration) { + ServiceKey dstSvcKey = new ServiceKey(request.getNamespace(), request.getService()); + dstInstanceEventKey = new ServiceEventKey(dstSvcKey, EventType.INSTANCE); + svcEventKeys.add(dstInstanceEventKey); + dstRuleEventKey = null; + srcRuleEventKey = null; + routeInfo = null; + criteria = null; + BaseFlow.buildFlowControlParam(request, configuration, this); + } + + /** + * 构造函数 + * + * @param request 请求 + * @param configuration 配置 + */ + public CommonInstancesRequest(GetOneInstanceRequest request, Configuration configuration) { + ServiceKey dstSvcKey = new ServiceKey(request.getNamespace(), request.getService()); + dstInstanceEventKey = new ServiceEventKey(dstSvcKey, EventType.INSTANCE); + svcEventKeys.add(dstInstanceEventKey); + dstRuleEventKey = new ServiceEventKey(dstSvcKey, EventType.ROUTING); + svcEventKeys.add(dstRuleEventKey); + ServiceMetadata srcServiceInfo = request.getServiceInfo(); + if (null != srcServiceInfo && !StringUtils.isBlank(srcServiceInfo.getNamespace()) && !StringUtils + .isBlank(srcServiceInfo.getService())) { + ServiceKey srcService = new ServiceKey(srcServiceInfo.getNamespace(), srcServiceInfo.getService()); + srcRuleEventKey = new ServiceEventKey(srcService, EventType.ROUTING); + svcEventKeys.add(srcRuleEventKey); + } else { + srcRuleEventKey = null; + } + ServiceInfo dstServiceInfo = new ServiceInfo(); + dstServiceInfo.setNamespace(request.getNamespace()); + dstServiceInfo.setService(request.getService()); + dstServiceInfo.setMetadata(request.getMetadata()); + routeInfo = new RouteInfo(srcServiceInfo, dstServiceInfo, request.getMethod()); + routeInfo.setCanary(request.getCanary()); + routeInfo.setMetadataFailoverType(request.getMetadataFailoverType()); + criteria = request.getCriteria(); + BaseFlow.buildFlowControlParam(request, configuration, this); + } + + /** + * 构造函数 + * + * @param request 请求 + * @param configuration 配置 + */ + public CommonInstancesRequest(GetInstancesRequest request, Configuration configuration) { + ServiceKey dstSvcKey = new ServiceKey(request.getNamespace(), request.getService()); + dstInstanceEventKey = new ServiceEventKey(dstSvcKey, EventType.INSTANCE); + svcEventKeys.add(dstInstanceEventKey); + dstRuleEventKey = new ServiceEventKey(dstSvcKey, EventType.ROUTING); + svcEventKeys.add(dstRuleEventKey); + ServiceMetadata srcServiceInfo = request.getServiceInfo(); + if (null != srcServiceInfo) { + ServiceKey srcService = new ServiceKey(srcServiceInfo.getNamespace(), srcServiceInfo.getService()); + srcRuleEventKey = new ServiceEventKey(srcService, EventType.ROUTING); + svcEventKeys.add(srcRuleEventKey); + } else { + srcRuleEventKey = null; + } + ServiceInfo dstServiceInfo = new ServiceInfo(); + dstServiceInfo.setNamespace(request.getNamespace()); + dstServiceInfo.setService(request.getService()); + dstServiceInfo.setMetadata(request.getMetadata()); + routeInfo = new RouteInfo(srcServiceInfo, dstServiceInfo, request.getMethod()); + routeInfo.setIncludeCircuitBreakInstances(request.isIncludeCircuitBreak()); + routeInfo.setIncludeUnhealthyInstances(request.isIncludeUnhealthy()); + routeInfo.setCanary(request.getCanary()); + routeInfo.setMetadataFailoverType(request.getMetadataFailoverType()); + criteria = null; + BaseFlow.buildFlowControlParam(request, configuration, this); + } + + public ServiceEventKey getSrcRuleEventKey() { + return srcRuleEventKey; + } + + public ServiceEventKey getDstInstanceEventKey() { + return dstInstanceEventKey; + } + + public ServiceEventKey getDstRuleEventKey() { + return dstRuleEventKey; + } + + public RouteInfo getRouteInfo() { + return routeInfo; + } + + public ServiceInstances getDstInstances() { + return dstInstances; + } + + public void setDstInstances(ServiceInstances dstInstances) { + this.dstInstances = dstInstances; + } + + public Criteria getCriteria() { + return criteria; + } + + @Override + public boolean isUseCache() { + return false; + } + + @Override + public Set getSvcEventKeys() { + return svcEventKeys; + } + + @Override + public ServiceEventKey getSvcEventKey() { + return null; + } + + @Override + public long getTimeoutMs() { + return timeoutMs; + } + + @Override + public void setTimeoutMs(long timeoutMs) { + this.timeoutMs = timeoutMs; + } + + @Override + public long getRetryIntervalMs() { + return retryIntervalMs; + } + + @Override + public void setRetryIntervalMs(long retryIntervalMs) { + this.retryIntervalMs = retryIntervalMs; + } + + @Override + public int getMaxRetry() { + return maxRetry; + } + + @Override + public void setMaxRetry(int maxRetry) { + this.maxRetry = maxRetry; + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonRuleRequest.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonRuleRequest.java new file mode 100644 index 000000000..4dd19bff9 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/CommonRuleRequest.java @@ -0,0 +1,89 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetServiceRuleRequest; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.FlowControlParam; +import java.util.Set; + +public class CommonRuleRequest implements ServiceEventKeysProvider, FlowControlParam { + + private final ServiceEventKey svcEventKey; + + private long timeoutMs; + + private int maxRetry; + + private long retryIntervalMs; + + public CommonRuleRequest(GetServiceRuleRequest request, Configuration config) { + svcEventKey = new ServiceEventKey( + new ServiceKey(request.getNamespace(), request.getService()), request.getRuleType()); + BaseFlow.buildFlowControlParam(request, config, this); + } + + @Override + public boolean isUseCache() { + return false; + } + + @Override + public Set getSvcEventKeys() { + return null; + } + + @Override + public ServiceEventKey getSvcEventKey() { + return svcEventKey; + } + + @Override + public long getTimeoutMs() { + return timeoutMs; + } + + @Override + public void setTimeoutMs(long timeoutMs) { + this.timeoutMs = timeoutMs; + } + + @Override + public long getRetryIntervalMs() { + return retryIntervalMs; + } + + @Override + public void setRetryIntervalMs(long retryIntervalMs) { + this.retryIntervalMs = retryIntervalMs; + } + + @Override + public int getMaxRetry() { + return maxRetry; + } + + @Override + public void setMaxRetry(int maxRetry) { + this.maxRetry = maxRetry; + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetAllInstancesSupplier.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetAllInstancesSupplier.java new file mode 100644 index 000000000..ae3ba62c6 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetAllInstancesSupplier.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.rpc.InstancesResponse; +import java.util.function.Supplier; + +/** + * GetAllInstancesSupplier + */ +public class GetAllInstancesSupplier implements Supplier { + + private final CommonInstancesRequest request; + + private final SyncFlow syncFlow; + + public GetAllInstancesSupplier(CommonInstancesRequest request, SyncFlow syncFlow) { + this.request = request; + this.syncFlow = syncFlow; + } + + @Override + public InstancesResponse get() { + return syncFlow.commonSyncGetAllInstances(request); + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetInstancesSupplier.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetInstancesSupplier.java new file mode 100644 index 000000000..6cb32fc74 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetInstancesSupplier.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.rpc.InstancesResponse; +import java.util.function.Supplier; + +/** + * GetInstancesSupplier + */ +public class GetInstancesSupplier implements Supplier { + + private final CommonInstancesRequest request; + + private final SyncFlow syncFlow; + + public GetInstancesSupplier(CommonInstancesRequest request, SyncFlow syncFlow) { + this.request = request; + this.syncFlow = syncFlow; + } + + @Override + public InstancesResponse get() { + return syncFlow.commonSyncGetInstances(request); + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetOneInstanceSupplier.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetOneInstanceSupplier.java new file mode 100644 index 000000000..bd34e9262 --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/GetOneInstanceSupplier.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.rpc.InstancesResponse; +import java.util.function.Supplier; + +/** + * GetOneInstanceSupplier + */ +public class GetOneInstanceSupplier implements Supplier { + + private final CommonInstancesRequest request; + + private final SyncFlow syncFlow; + + public GetOneInstanceSupplier(CommonInstancesRequest request, SyncFlow syncFlow) { + this.request = request; + this.syncFlow = syncFlow; + } + + @Override + public InstancesResponse get() { + return syncFlow.commonSyncGetOneInstance(request); + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/SyncFlow.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/SyncFlow.java new file mode 100644 index 000000000..122c87d9e --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/flow/SyncFlow.java @@ -0,0 +1,124 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.flow; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.api.rpc.ServiceRuleResponse; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.ResourcesResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SyncFlow { + + private static final Logger LOG = LoggerFactory.getLogger(SyncFlow.class); + + private Extensions extensions; + + public void init(Extensions extensions) { + this.extensions = extensions; + } + + /** + * 获取全量服务实例 + * + * @param request 请求对象 + * @return 全量服务实例 + * @throws PolarisException 异常 + */ + public InstancesResponse commonSyncGetAllInstances(CommonInstancesRequest request) throws PolarisException { + syncGetServiceInstances(request); + ServiceInstances dstInstances = request.getDstInstances(); + return new InstancesResponse(dstInstances); + } + + /** + * 获取多个实例列表 + * + * @param request 请求对象 + * @return 实例应答 + * @throws PolarisException 异常 + */ + public InstancesResponse commonSyncGetInstances(CommonInstancesRequest request) throws PolarisException { + syncGetServiceInstances(request); + ServiceInstances dstInstances = request.getDstInstances(); + if (CollectionUtils.isEmpty(dstInstances.getInstances())) { + return new InstancesResponse(dstInstances); + } + ServiceInstances routerInstances = + BaseFlow.processServiceRouters(request.getRouteInfo(), request.getDstInstances(), + extensions.getConfigRouterChainGroup()); + return new InstancesResponse(routerInstances); + } + + /** + * 获取单个实例 + * + * @param request 请求对象 + * @return 实例应答 + * @throws PolarisException 异常 + */ + public InstancesResponse commonSyncGetOneInstance(CommonInstancesRequest request) throws PolarisException { + syncGetServiceInstances(request); + ServiceInstances dstInstances = request.getDstInstances(); + if (CollectionUtils.isEmpty(dstInstances.getInstances())) { + return new InstancesResponse(dstInstances); + } + ServiceInstances routerInstances = + BaseFlow.processServiceRouters(request.getRouteInfo(), request.getDstInstances(), + extensions.getConfigRouterChainGroup()); + LoadBalancer loadBalancer = extensions.getLoadBalancer(); + Instance instance = BaseFlow.processLoadBalance(loadBalancer, request.getCriteria(), routerInstances); + return new InstancesResponse(dstInstances, instance); + } + + /** + * 获取服务规则信息 + * + * @param request 请求对象 + * @return 规则数据 + * @throws PolarisException 异常 + */ + public ServiceRuleResponse commonSyncGetServiceRule(CommonRuleRequest request) throws PolarisException { + ResourcesResponse resourcesResponse = BaseFlow.syncGetResources(extensions, false, request, request); + return new ServiceRuleResponse(resourcesResponse.getServiceRule(request.getSvcEventKey())); + } + + /** + * 获取服务实例以及路由数据 + * + * @param request 请求对象 + * @throws PolarisException 异常 + */ + private void syncGetServiceInstances(CommonInstancesRequest request) throws PolarisException { + ResourcesResponse resourcesResponse = BaseFlow.syncGetResources(extensions, false, request, request); + request.setDstInstances(resourcesResponse.getServiceInstances(request.getDstInstanceEventKey())); + if (null != request.getDstRuleEventKey()) { + request.getRouteInfo().setDestRouteRule(resourcesResponse.getServiceRule(request.getDstRuleEventKey())); + } + if (null != request.getSrcRuleEventKey()) { + request.getRouteInfo().setSourceRouteRule(resourcesResponse.getServiceRule(request.getSrcRuleEventKey())); + } + } +} diff --git a/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/util/Validator.java b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/util/Validator.java new file mode 100644 index 000000000..ee2e4e71f --- /dev/null +++ b/polaris-discovery/polaris-discovery-client/src/main/java/com/tencent/polaris/discovery/client/util/Validator.java @@ -0,0 +1,192 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.client.util; + + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.BaseEntity; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.GetResourcesRequest; +import com.tencent.polaris.api.rpc.GetServiceRuleRequest; +import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; +import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterRequest; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.util.CommonValidator; +import java.util.Set; + +/** + * 校验API入参是否正确. + * + * @author andrewshan + * @date 2019/8/21 + */ +public class Validator { + + private static final int MAX_PORT = 65536; + + /** + * 校验获取资源请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败 + */ + public static void validateGetResourcesRequest(GetResourcesRequest request) throws PolarisException { + Set svcEventKeys = request.getSvcEventKeys(); + if (CollectionUtils.isEmpty(svcEventKeys)) { + return; + } + for (ServiceEventKey svcEventKey : svcEventKeys) { + ServiceKey serviceKey = svcEventKey.getServiceKey(); + ServiceEventKey.EventType eventType = svcEventKey.getEventType(); + if (null == serviceKey || null == eventType) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "missing service key or event type"); + } + CommonValidator.validateNamespaceService(serviceKey.getNamespace(), serviceKey.getService()); + } + } + + /** + * 校验获取单个服务实例的请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateGetOneInstanceRequest(GetOneInstanceRequest request) throws PolarisException { + checkCommon(request); + } + + /** + * 校验获取批量服务实例的请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateGetInstancesRequest(GetInstancesRequest request) throws PolarisException { + checkCommon(request); + } + + /** + * 校验获取批量服务实例的请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateGetAllInstancesRequest(GetAllInstancesRequest request) throws PolarisException { + checkCommon(request); + } + + /** + * 校验获取服务规则的请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败 + */ + public static void validateGetServiceRuleRequest(GetServiceRuleRequest request) throws PolarisException { + checkCommon(request); + if (request.getRuleType() == ServiceEventKey.EventType.INSTANCE) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "event type can not be instance"); + } + } + + /** + * 校验用户上报的调用结果 + * + * @param serviceCallResult 调用结果 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateServiceCallResult(ServiceCallResult serviceCallResult) throws PolarisException { + CommonValidator.validateNamespaceService(serviceCallResult.getNamespace(), serviceCallResult.getService()); + validateHostPort(serviceCallResult.getHost(), serviceCallResult.getPort()); + if (null == serviceCallResult.getRetStatus()) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "retStatus can not be blank"); + } + if (null != serviceCallResult.getDelay() && serviceCallResult.getDelay() < 0) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "delay can not be less than 0"); + } + } + + /** + * 校验端口信息 + * + * @param port 端口类型 + * @throws PolarisException 校验失败异常 + */ + private static void validateHostPort(String host, Integer port) throws PolarisException { + if (StringUtils.isBlank(host)) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "host can not be blank"); + } + if (port == null) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "port can not be null"); + } + if (port <= 0 || port >= MAX_PORT) { + throw new PolarisException( + ErrorCode.API_INVALID_ARGUMENT, "port value should be in range (0, 65536)."); + } + } + + /** + * 校验实例注册请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateInstanceRegisterRequest(InstanceRegisterRequest request) throws PolarisException { + checkCommon(request); + validateHostPort(request.getHost(), request.getPort()); + } + + /** + * 校验实例反注册请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateInstanceDeregisterRequest(InstanceDeregisterRequest request) throws PolarisException { + if (StringUtils.isNotBlank(request.getInstanceID())) { + return; + } + CommonValidator.validateNamespaceService(request.getNamespace(), request.getService()); + validateHostPort(request.getHost(), request.getPort()); + } + + /** + * 校验实例心跳请求 + * + * @param request 请求对象 + * @throws PolarisException 校验失败会抛出异常 + */ + public static void validateHeartbeatRequest(InstanceHeartbeatRequest request) throws PolarisException { + if (StringUtils.isNotBlank(request.getInstanceID())) { + return; + } + validateHostPort(request.getHost(), request.getPort()); + CommonValidator.validateNamespaceService(request.getNamespace(), request.getService()); + } + + private static void checkCommon(BaseEntity entity) throws PolarisException { + CommonValidator.validateNamespaceService(entity.getNamespace(), entity.getService()); + } +} diff --git a/polaris-discovery/polaris-discovery-examples/pom.xml b/polaris-discovery/polaris-discovery-examples/pom.xml new file mode 100644 index 000000000..a797a0cb7 --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/pom.xml @@ -0,0 +1,47 @@ + + + + polaris-discovery + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-discovery-examples + + + + com.tencent.nameservice + polaris-discovery-factory + ${project.version} + + + commons-cli + commons-cli + 1.4 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetAllInstancesExample.java b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetAllInstancesExample.java new file mode 100644 index 000000000..df5ad3dbd --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetAllInstancesExample.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples.consumer; + +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.router.examples.utils.ExampleUtils; +import com.tencent.polaris.router.examples.utils.ExampleUtils.InitResult; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +public class GetAllInstancesExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = ExampleUtils.initConsumerConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPI()) { + System.out.println("namespace " + namespace + ", service " + service); + GetAllInstancesRequest allInstancesRequest = new GetAllInstancesRequest(); + allInstancesRequest.setNamespace(namespace); + allInstancesRequest.setService(service); + InstancesResponse instancesResponse = consumerAPI.getAllInstance(allInstancesRequest); + System.out.println("instances count is " + instancesResponse.getInstances().length); + } + } +} diff --git a/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetOneInstanceExample.java b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetOneInstanceExample.java new file mode 100644 index 000000000..90fdb355c --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/consumer/GetOneInstanceExample.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples.consumer; + +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.router.examples.utils.ExampleUtils; +import com.tencent.polaris.router.examples.utils.ExampleUtils.InitResult; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +public class GetOneInstanceExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = ExampleUtils.initConsumerConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPI()) { + System.out.println("namespace " + namespace + ", service " + service); + GetOneInstanceRequest getOneInstanceRequest = new GetOneInstanceRequest(); + getOneInstanceRequest.setNamespace(namespace); + getOneInstanceRequest.setService(service); + InstancesResponse oneInstance = consumerAPI.getOneInstance(getOneInstanceRequest); + Instance[] instances = oneInstance.getInstances(); + System.out.println("instances count is " + instances.length); + Instance targetInstance = instances[0]; + System.out.printf("target instance is %s:%d%n", targetInstance.getHost(), targetInstance.getPort()); + } + } +} diff --git a/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/DeregisterExample.java b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/DeregisterExample.java new file mode 100644 index 000000000..1ccfadb5d --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/DeregisterExample.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples.provider; + +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; +import com.tencent.polaris.router.examples.utils.ExampleUtils; +import com.tencent.polaris.router.examples.utils.ExampleUtils.InitResult; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +public class DeregisterExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = ExampleUtils.initProviderConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + String host = initResult.getHost(); + int port = initResult.getPort(); + String token = initResult.getToken(); + try (ProviderAPI providerAPI = DiscoveryAPIFactory.createProviderAPI()) { + InstanceDeregisterRequest instanceDeregisterRequest = new InstanceDeregisterRequest(); + instanceDeregisterRequest.setNamespace(namespace); + instanceDeregisterRequest.setService(service); + instanceDeregisterRequest.setHost(host); + instanceDeregisterRequest.setPort(port); + instanceDeregisterRequest.setToken(token); + providerAPI.deRegister(instanceDeregisterRequest); + System.out.println("deregister for service successfully: " + service); + + } + } +} diff --git a/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/HeartbeatExample.java b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/HeartbeatExample.java new file mode 100644 index 000000000..fd0a43eae --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/HeartbeatExample.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples.provider; + +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; +import com.tencent.polaris.router.examples.utils.ExampleUtils; +import com.tencent.polaris.router.examples.utils.ExampleUtils.InitResult; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +public class HeartbeatExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = ExampleUtils.initProviderConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + String host = initResult.getHost(); + int port = initResult.getPort(); + String token = initResult.getToken(); + try (ProviderAPI providerAPI = DiscoveryAPIFactory.createProviderAPI()) { + InstanceHeartbeatRequest instanceHeartbeatRequest = new InstanceHeartbeatRequest(); + instanceHeartbeatRequest.setNamespace(namespace); + instanceHeartbeatRequest.setService(service); + instanceHeartbeatRequest.setHost(host); + instanceHeartbeatRequest.setPort(port); + instanceHeartbeatRequest.setToken(token); + providerAPI.heartbeat(instanceHeartbeatRequest); + System.out.println("heartbeat for service successfully: " + service); + } + } +} diff --git a/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/RegisterExample.java b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/RegisterExample.java new file mode 100644 index 000000000..6a3837519 --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/provider/RegisterExample.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples.provider; + +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.rpc.InstanceRegisterRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterResponse; +import com.tencent.polaris.router.examples.utils.ExampleUtils; +import com.tencent.polaris.router.examples.utils.ExampleUtils.InitResult; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +public class RegisterExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = ExampleUtils.initProviderConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + String host = initResult.getHost(); + int port = initResult.getPort(); + String token = initResult.getToken(); + try (ProviderAPI providerAPI = DiscoveryAPIFactory.createProviderAPI()) { + InstanceRegisterRequest instanceRegisterRequest = new InstanceRegisterRequest(); + instanceRegisterRequest.setNamespace(namespace); + instanceRegisterRequest.setService(service); + instanceRegisterRequest.setHost(host); + instanceRegisterRequest.setPort(port); + instanceRegisterRequest.setToken(token); + if (initResult.getTtl() > 0) { + instanceRegisterRequest.setTtl(initResult.getTtl()); + } + InstanceRegisterResponse instanceRegisterResponse = providerAPI.register(instanceRegisterRequest); + System.out.println("response after register is " + instanceRegisterResponse); + } + } +} diff --git a/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/utils/ExampleUtils.java b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/utils/ExampleUtils.java new file mode 100644 index 000000000..3dca445ca --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/java/com/tencent/polaris/router/examples/utils/ExampleUtils.java @@ -0,0 +1,136 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples.utils; + +import com.tencent.polaris.api.utils.StringUtils; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +public class ExampleUtils { + + public static class InitResult { + + private final String namespace; + private final String service; + private final String host; + private final int port; + private final int ttl; + private final String token; + + public InitResult(String namespace, String service) { + this(namespace, service, "", 0, "", 0); + } + + public InitResult(String namespace, String service, String host, int port, String token, int ttl) { + this.namespace = namespace; + this.service = service; + this.host = host; + this.port = port; + this.token = token; + this.ttl = ttl; + } + + public String getNamespace() { + return namespace; + } + + public String getService() { + return service; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public String getToken() { + return token; + } + + public int getTtl() { + return ttl; + } + } + + /** + * 初始化配置对象 + * + * @param args 命名行参数 + * @return 配置对象 + * @throws ParseException 解析异常 + */ + public static InitResult initConsumerConfiguration(String[] args) throws ParseException { + CommandLineParser parser = new DefaultParser(); + Options options = new Options(); + options.addOption("namespace", "service namespace", true, "namespace for service"); + options.addOption("service", "service name", true, "service name"); + + CommandLine commandLine = parser.parse(options, args); + String namespace = commandLine.getOptionValue("namespace"); + String service = commandLine.getOptionValue("service"); + if (StringUtils.isBlank(namespace) || StringUtils.isBlank(service)) { + System.out.println("namespace or service is required"); + System.exit(1); + } + return new InitResult(namespace, service); + } + + /** + * 初始化被调方配置对象 + * + * @param args 命名行参数 + * @return 配置对象 + * @throws ParseException 解析异常 + */ + public static InitResult initProviderConfiguration(String[] args) throws ParseException { + CommandLineParser parser = new DefaultParser(); + Options options = new Options(); + options.addOption("namespace", "service namespace", true, "namespace for service"); + options.addOption("service", "service name", true, "service name"); + options.addOption("host", "host", true, "host"); + options.addOption("port", "port", true, "port"); + options.addOption("token", "token", true, "token"); + options.addOption("ttl", "ttl", true, "ttl"); + + CommandLine commandLine = parser.parse(options, args); + String namespace = commandLine.getOptionValue("namespace"); + String service = commandLine.getOptionValue("service"); + String host = commandLine.getOptionValue("host"); + String token = commandLine.getOptionValue("token"); + String portStr = commandLine.getOptionValue("port"); + if (StringUtils.isBlank(namespace) || StringUtils.isBlank(service) || StringUtils.isBlank(host) || StringUtils + .isBlank(portStr)) { + System.out.println("namespace / service / host/ token / port is required"); + System.exit(1); + } + int port = Integer.parseInt(portStr); + int ttl = 0; + String ttlStr = commandLine.getOptionValue("ttl"); + if (StringUtils.isNotBlank(ttlStr)) { + ttl = Integer.parseInt(ttlStr); + } + return new InitResult(namespace, service, host, port, token, ttl); + } + +} diff --git a/polaris-discovery/polaris-discovery-examples/src/main/resources/log4j2.xml b/polaris-discovery/polaris-discovery-examples/src/main/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-examples/src/main/resources/polaris.yml b/polaris-discovery/polaris-discovery-examples/src/main/resources/polaris.yml new file mode 100644 index 000000000..6ccebae10 --- /dev/null +++ b/polaris-discovery/polaris-discovery-examples/src/main/resources/polaris.yml @@ -0,0 +1,12 @@ +global: + # configuration for connecting the polaris server + serverConnector: + # target server address + addresses: + - 175.27.154.41:8091 +#描述:主调端配置 +consumer: + #描述:节点熔断相关配置 + circuitBreaker: + #描述:是否启用节点熔断功能 + enable: false \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-factory/pom.xml b/polaris-discovery/polaris-discovery-factory/pom.xml new file mode 100644 index 000000000..3ffa748ea --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/pom.xml @@ -0,0 +1,118 @@ + + + + polaris-discovery + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-discovery-factory + + + + com.tencent.nameservice + polaris-discovery-client + ${project.version} + + + + com.tencent.nameservice + connector-polaris-grpc + ${project.version} + + + + com.tencent.nameservice + resource-cache-memory + ${project.version} + + + + com.tencent.nameservice + flow-cache-expired + ${project.version} + + + + com.tencent.nameservice + router-isolated + ${project.version} + + + com.tencent.nameservice + router-healthy + ${project.version} + + + com.tencent.nameservice + router-rule + ${project.version} + + + com.tencent.nameservice + router-nearby + ${project.version} + + + com.tencent.nameservice + router-metadata + ${project.version} + + + com.tencent.nameservice + router-canary + ${project.version} + + + com.tencent.nameservice + router-set + ${project.version} + + + + com.tencent.nameservice + loadbalancer-random + ${project.version} + + + com.tencent.nameservice + loadbalancer-ringhash + ${project.version} + + + + + com.tencent.nameservice + polaris-circuitbreaker-client + ${project.version} + + + com.tencent.nameservice + circuitbreaker-errrate + ${project.version} + + + com.tencent.nameservice + circuitbreaker-errcount + ${project.version} + + + + + com.tencent.nameservice + polaris-test-common + ${project.version} + test + + + com.tencent.nameservice + polaris-test-mock-discovery + ${project.version} + test + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-factory/src/main/java/com/tencent/polaris/factory/api/DiscoveryAPIFactory.java b/polaris-discovery/polaris-discovery-factory/src/main/java/com/tencent/polaris/factory/api/DiscoveryAPIFactory.java new file mode 100644 index 000000000..b47627d18 --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/main/java/com/tencent/polaris/factory/api/DiscoveryAPIFactory.java @@ -0,0 +1,134 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.api; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.discovery.client.api.DefaultConsumerAPI; +import com.tencent.polaris.discovery.client.api.DefaultProviderAPI; +import com.tencent.polaris.factory.ConfigAPIFactory; +import java.io.InputStream; + +/** + * 用于创建API的工厂类型 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class DiscoveryAPIFactory { + + /** + * 使用默认配置创建ConsumerAPI + * + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPI() throws PolarisException { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + return createConsumerAPIByConfig(configuration); + } + + /** + * 通过SDK上下文创建ConsumerAPI + * + * @param context SDK上下文,包含插件列表,配置对象等信息 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPIByContext(SDKContext context) throws PolarisException { + DefaultConsumerAPI defaultValue = new DefaultConsumerAPI(context); + defaultValue.init(); + return defaultValue; + } + + /** + * 通过配置文件创建ConsumerAPI + * + * @param configStream 配置文件流 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPIByFile(InputStream configStream) throws PolarisException { + Configuration configuration = ConfigAPIFactory.loadConfig(configStream); + return DiscoveryAPIFactory.createConsumerAPIByConfig(configuration); + } + + /** + * 通过配置对象创建ConsumerAPI + * + * @param config 配置对象 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPIByConfig(Configuration config) throws PolarisException { + SDKContext context = SDKContext.initContextByConfig(config); + return DiscoveryAPIFactory.createConsumerAPIByContext(context); + } + + /** + * 通过默认配置创建ProviderAPI + * + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程异常 + */ + public static ProviderAPI createProviderAPI() throws PolarisException { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + return createProviderAPIByConfig(configuration); + } + + /** + * 通过SDK上下文创建ProviderAPI + * + * @param context SDK上下文,包含插件列表,配置对象等信息 + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPIByContext(SDKContext context) throws PolarisException { + DefaultProviderAPI defaultValue = new DefaultProviderAPI(context); + defaultValue.init(); + return defaultValue; + } + + /** + * 通过配置文件创建ProviderAPI + * + * @param configStream 配置文件流 + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPIByFile(InputStream configStream) throws PolarisException { + Configuration configuration = ConfigAPIFactory.loadConfig(configStream); + return DiscoveryAPIFactory.createProviderAPIByConfig(configuration); + } + + /** + * 通过配置对象创建ProviderAPI + * + * @param config 配置对象 + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPIByConfig(Configuration config) throws PolarisException { + SDKContext context = SDKContext.initContextByConfig(config); + return createProviderAPIByContext(context); + } + +} diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ConsumerTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ConsumerTest.java new file mode 100644 index 000000000..40377f0e5 --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ConsumerTest.java @@ -0,0 +1,338 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.test.core; + +import static com.tencent.polaris.test.common.Consts.ITERATE_COUNT; +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.ClusterConfig; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.client.pojo.Node; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.factory.config.global.ClusterConfigImpl; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ConsumerTest { + + private static final Logger LOG = LoggerFactory.getLogger(ConsumerTest.class); + + private NamingServer namingServer; + + private enum Operation { + ALL_HEALTHY, HAS_UNHEALTHY + } + + private static final Map validParams = new HashMap<>(); + + private static final String SERVICE_TEST_NORMAL = "java_test_normal"; + + private static final String SERVICE_TEST_ABNORMAL = "java_test_abnormal"; + + private static final String NOT_EXISTS_SERVICE = "java_test_not_exists"; + + static { + validParams.put(Operation.ALL_HEALTHY, + new ValidParam(SERVICE_TEST_NORMAL, 6, 6, 6)); + validParams.put(Operation.HAS_UNHEALTHY, + new ValidParam(SERVICE_TEST_ABNORMAL, 10, 4, 8)); + } + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + for (ValidParam validParam : validParams.values()) { + InstanceParameter instanceParameter = new InstanceParameter(); + instanceParameter.setHealthy(true); + instanceParameter.setIsolated(false); + instanceParameter.setWeight(100); + ServiceKey serviceKey = new ServiceKey(NAMESPACE_TEST, validParam.getServiceName()); + List nodes = namingServer.getNamingService().batchAddInstances(serviceKey, 10000, + validParam.getCountAll(), instanceParameter); + if (validParam.getCountAll() > validParam.getCountHealth()) { + int abnormalCount = validParam.getCountAll() - validParam.getCountHealth(); + int unhealthyCount = abnormalCount / 2; + int isolatedCount = abnormalCount - unhealthyCount; + for (int i = 0; i < unhealthyCount; i++) { + namingServer.getNamingService().setInstanceHealthyStatus( + serviceKey, nodes.get(i), false, null, null); + } + for (int i = 0; i < isolatedCount; i++) { + namingServer.getNamingService().setInstanceHealthyStatus( + serviceKey, nodes.get(nodes.size() - 1 - i), null, true, null); + } + } + if (validParam.getCountAll() > validParam.getCountHasWeight()) { + int weightZeroCount = validParam.getCountAll() - validParam.getCountHasWeight(); + for (int i = 0; i < weightZeroCount; i++) { + namingServer.getNamingService().setInstanceHealthyStatus( + serviceKey, nodes.get(i), null, null, 0); + } + } + } + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @Test + public void testSyncGetAllInstancesNormal() { + commonTestSyncGetAllInstances(Operation.ALL_HEALTHY); + } + + @Test + public void testSyncGetInstancesNormal() { + commonTestSyncGetInstances(Operation.ALL_HEALTHY); + } + + @Test + public void testSyncGetOneInstanceNormal() { + commonTestSyncGetOneInstance(Operation.ALL_HEALTHY); + } + + @Test + public void testConcurrentSyncGetOneInstanceNormal() { + concurrentTestSyncGetOneInstance(Operation.ALL_HEALTHY); + } + + + @Test + public void testSyncGetAllInstancesAbnormal() { + commonTestSyncGetAllInstances(Operation.HAS_UNHEALTHY); + } + + @Test + public void testSyncGetInstancesAbnormal() { + commonTestSyncGetInstances(Operation.HAS_UNHEALTHY); + } + + @Test + public void testSyncGetOneInstanceAbnormal() { + commonTestSyncGetOneInstance(Operation.HAS_UNHEALTHY); + } + + @Test + public void testUseBuiltinAsDiscover() { + ValidParam validParam = validParams.get(Operation.ALL_HEALTHY); + Configuration configuration = TestUtils.configWithEnvAddress(); + ConfigurationImpl configImpl = (ConfigurationImpl) configuration; + configImpl.setDefault(); + ClusterConfig discoverCluster = configImpl.getGlobal().getSystem() + .getDiscoverCluster(); + ((ClusterConfigImpl) discoverCluster).setSameAsBuiltin(true); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + for (int i = 0; i < ITERATE_COUNT; i++) { + GetOneInstanceRequest request = new GetOneInstanceRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(validParam.getServiceName()); + + InstancesResponse instancesResponse = consumerAPI.getOneInstance(request); + Assert.assertEquals(1, instancesResponse.getInstances().length); + + Instance instance = instancesResponse.getInstances()[0]; + Assert.assertTrue(instance.isHealthy()); + Assert.assertFalse(instance.isIsolated()); + Assert.assertEquals(100, instance.getWeight()); + } + } + } + + @Test + public void testGetNotExistsService() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + for (int i = 0; i < 10; i++) { + GetOneInstanceRequest request = new GetOneInstanceRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(NOT_EXISTS_SERVICE); + try { + consumerAPI.getOneInstance(request); + Assert.fail("not exists service should throw exception"); + } catch (PolarisException e) { + ErrorCode code = e.getCode(); + if (code != ErrorCode.SERVER_USER_ERROR) { + Assert.fail(e.getMessage()); + } + LOG.info("not exist service message is {}", e.getMessage()); + } + } + //把实例加上去,可以重新获取 + InstanceParameter parameter = new InstanceParameter(); + parameter.setHealthy(true); + parameter.setIsolated(false); + parameter.setWeight(100); + namingServer.getNamingService().batchAddInstances( + new ServiceKey(NAMESPACE_TEST, NOT_EXISTS_SERVICE), 10100, 10, parameter); + GetOneInstanceRequest request = new GetOneInstanceRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(NOT_EXISTS_SERVICE); + InstancesResponse oneInstance = consumerAPI.getOneInstance(request); + Assert.assertEquals(1, oneInstance.getInstances().length); + + } + } + + public void commonTestSyncGetAllInstances(Operation operation) { + ValidParam validParam = validParams.get(operation); + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + for (int i = 0; i < ITERATE_COUNT; i++) { + GetAllInstancesRequest request = new GetAllInstancesRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(validParam.getServiceName()); + + InstancesResponse instancesResponse = consumerAPI.getAllInstance(request); + Assert.assertEquals(validParam.getCountAll(), instancesResponse.getInstances().length); + Assert.assertEquals(validParam.getCountHasWeight() * 100, instancesResponse.getTotalWeight()); + } + } + } + + public void commonTestSyncGetInstances(Operation operation) { + ValidParam validParam = validParams.get(operation); + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + for (int i = 0; i < ITERATE_COUNT; i++) { + GetInstancesRequest request = new GetInstancesRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(validParam.getServiceName()); + + InstancesResponse instancesResponse = consumerAPI.getInstances(request); + Assert.assertEquals(validParam.getCountHealth(), instancesResponse.getInstances().length); + Assert.assertEquals(validParam.getCountHealth() * 100, instancesResponse.getTotalWeight()); + } + } + } + + public void concurrentTestSyncGetOneInstance(Operation operation) { + ValidParam validParam = validParams.get(operation); + Configuration configuration = TestUtils.configWithEnvAddress(); + ExecutorService executorService = Executors.newCachedThreadPool(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + for (int i = 0; i < ITERATE_COUNT; i++) { + executorService.submit(new Runnable() { + @Override + public void run() { + GetOneInstanceRequest request = new GetOneInstanceRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(validParam.getServiceName()); + + InstancesResponse instancesResponse = consumerAPI.getOneInstance(request); + Assert.assertEquals(1, instancesResponse.getInstances().length); + + Instance instance = instancesResponse.getInstances()[0]; + Assert.assertTrue(instance.isHealthy()); + Assert.assertFalse(instance.isIsolated()); + Assert.assertEquals(100, instance.getWeight()); + } + }); + } + } + } + + public void commonTestSyncGetOneInstance(Operation operation) { + ValidParam validParam = validParams.get(operation); + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + for (int i = 0; i < ITERATE_COUNT; i++) { + GetOneInstanceRequest request = new GetOneInstanceRequest(); + request.setNamespace(NAMESPACE_TEST); + request.setService(validParam.getServiceName()); + + InstancesResponse instancesResponse = consumerAPI.getOneInstance(request); + Assert.assertEquals(1, instancesResponse.getInstances().length); + + Instance instance = instancesResponse.getInstances()[0]; + Assert.assertTrue(instance.isHealthy()); + Assert.assertFalse(instance.isIsolated()); + Assert.assertEquals(100, instance.getWeight()); + } + } + } + + private static class ValidParam { + + final String serviceName; + + final int countAll; + + final int countHealth; + + final int countHasWeight; + + public ValidParam(String serviceName, int countAll, int countHealth, int countHasWeight) { + this.serviceName = serviceName; + this.countAll = countAll; + this.countHealth = countHealth; + this.countHasWeight = countHasWeight; + } + + public String getServiceName() { + return serviceName; + } + + public int getCountAll() { + return countAll; + } + + public int getCountHealth() { + return countHealth; + } + + public int getCountHasWeight() { + return countHasWeight; + } + } + +} diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/MetadataRouterTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/MetadataRouterTest.java new file mode 100644 index 000000000..1d92a6b7d --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/MetadataRouterTest.java @@ -0,0 +1,207 @@ +package com.tencent.polaris.discovery.test.core; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_PRODUCTION; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.api.rpc.MetadataFailoverType; +import com.tencent.polaris.client.pojo.Node; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * @author starkwen + * @date 2021/2/25 下午4:56 + */ +public class MetadataRouterTest { + + public static final String METADATA_SERVICE = "192000705:65767"; + public static final String METADATA_SERVICE_1 = "192000705:65584"; + private NamingServer namingServer; + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + /** + * 该服务下有四个实例 + * 1. 127.0.0.1:80 不健康 Env-set:1-0 + * 2. 127.0.0.1:70 健康 Env-set:1-0 + * 3. 127.0.0.1:100 健康 Env-set:1-0 + * 4. 127.0.0.1:90 健康 + */ + ServiceKey serviceKey = new ServiceKey(NAMESPACE_PRODUCTION, METADATA_SERVICE); + InstanceParameter parameter = new InstanceParameter(); + parameter.setWeight(100); + parameter.setHealthy(false); + parameter.setIsolated(false); + Map metadata = new HashMap<>(); + metadata.put("Env-set", "1-0"); + parameter.setMetadata(metadata); + namingServer.getNamingService().addInstance(serviceKey, new Node("127.0.0.1", 80), parameter); + parameter.setHealthy(true); + namingServer.getNamingService().addInstance(serviceKey, new Node("127.0.0.1", 70), parameter); + namingServer.getNamingService().addInstance(serviceKey, new Node("127.0.0.1", 100), parameter); + parameter.setMetadata(null); + namingServer.getNamingService().addInstance(serviceKey, new Node("127.0.0.1", 90), parameter); + /** + * 该服务下有两个实例 + * 1. 127.0.0.1:80 不健康 Env-set:1-0 + * 2. 127.0.0.1:81 不健康 + */ + ServiceKey serviceKey1 = new ServiceKey(NAMESPACE_PRODUCTION, METADATA_SERVICE_1); + parameter.setMetadata(metadata); + parameter.setHealthy(false); + namingServer.getNamingService().addInstance(serviceKey1, new Node("127.0.0.1", 80), parameter); + parameter.setMetadata(null); + namingServer.getNamingService().addInstance(serviceKey1, new Node("127.0.0.1", 81), parameter); + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @Test + public void testNormalScene() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumer = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + // 该服务下有四个实例,其中有两个满足metadata路由(分别为port70,100),80 90端口不满足 + for (int i = 0; i < 10; i++) { + GetOneInstanceRequest getInstances2 = new GetOneInstanceRequest(); + getInstances2.setNamespace(NAMESPACE_PRODUCTION); + getInstances2.setService(METADATA_SERVICE); + Map map = new HashMap<>(); + map.put("Env-set", "1-0"); + getInstances2.setMetadata(map); + InstancesResponse ins = null; + try { + ins = consumer.getOneInstance(getInstances2); + } catch (PolarisException e) { + e.printStackTrace(); + } + int port = ins.getInstances()[0].getPort(); + Assert.assertNotEquals(90, port); + Assert.assertNotEquals(80, port); + } + } + } + + @Test + public void testFailoverNoneScene() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumer = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + // 该服务下有四个实例,只有一个80端口实例满足metadata路由,但是不健康,降级策略采用默认不降级 + for (int i = 0; i < 10; i++) { + GetOneInstanceRequest getInstances2 = new GetOneInstanceRequest(); + getInstances2.setNamespace(NAMESPACE_PRODUCTION); + getInstances2.setService(METADATA_SERVICE); + Map map = new HashMap<>(); + map.put("Env-set", "1-1"); + getInstances2.setMetadata(map); + InstancesResponse ins = null; + try { + ins = consumer.getOneInstance(getInstances2); + } catch (PolarisException e) { + Assert.assertEquals(ErrorCode.METADATA_MISMATCH, e.getCode()); + } + } + } + } + + @Test + public void testFailoverAllScene() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumer = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + // 该服务下有四个实例,只有一个80端口实例满足metadata路由, + // 但是不健康,降级策略采用默认返回所有健康实例,其中80端口实例为不健康,故80端口的实例不会返回 + for (int i = 0; i < 10; i++) { + GetOneInstanceRequest getInstances2 = new GetOneInstanceRequest(); + getInstances2.setNamespace("Production"); + getInstances2.setService(METADATA_SERVICE); + Map map = new HashMap<>(); + map.put("Env-set", "1-1"); + getInstances2.setMetadata(map); + getInstances2.setMetadataFailoverType(MetadataFailoverType.METADATAFAILOVERALL); + InstancesResponse ins = null; + try { + ins = consumer.getOneInstance(getInstances2); + } catch (PolarisException e) { + e.printStackTrace(); + } + Assert.assertNotEquals(80, ins.getInstances()[0].getPort()); + } + } + } + + @Test + public void testFailoverNotKeyScene() { + // 传入Env-set:1-1 ,应该返回第4个实例,因为前三个都包含Env-set这个key + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumer = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + for (int i = 0; i < 10; i++) { + GetOneInstanceRequest getInstances2 = new GetOneInstanceRequest(); + getInstances2.setNamespace("Production"); + getInstances2.setService(METADATA_SERVICE); + Map map = new HashMap<>(); + map.put("Env-set", "1-1"); + getInstances2.setMetadata(map); + //TODO: 通过配置文件来设置该配置 + getInstances2.setMetadataFailoverType(MetadataFailoverType.METADATAFAILOVERNOTKEY); + InstancesResponse ins = null; + try { + ins = consumer.getOneInstance(getInstances2); + } catch (PolarisException e) { + Assert.fail(e.getMessage()); + } + Assert.assertEquals(90, ins.getInstances()[0].getPort()); + Assert.assertEquals("127.0.0.1", ins.getInstances()[0].getHost()); + } + } + } + + @Test + public void testFailoverNotKeyScene2() { + // 传入Env-set:1-1 ,应该返回第2个实例,因为第一个都包含Env-set这个key + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumer = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + // 该服务下有两个实例,都不健康,且只有一个实例满足metadata路由, + // 降级策略采用默认返回所有非metadata健康实例,故只返回不满足metadata的那个实例 + for (int i = 0; i < 10; i++) { + GetOneInstanceRequest getInstances2 = new GetOneInstanceRequest(); + getInstances2.setNamespace(NAMESPACE_PRODUCTION); + getInstances2.setService(METADATA_SERVICE_1); + Map map = new HashMap<>(); + map.put("Env-set", "1-1"); + getInstances2.setMetadata(map); + getInstances2.setMetadataFailoverType(MetadataFailoverType.METADATAFAILOVERNOTKEY); + InstancesResponse ins = null; + try { + ins = consumer.getOneInstance(getInstances2); + } catch (PolarisException e) { + e.printStackTrace(); + } + Assert.assertEquals(81, ins.getInstances()[0].getPort()); + } + } + } +} diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ProviderTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ProviderTest.java new file mode 100644 index 000000000..ba9fa22a9 --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ProviderTest.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.test.core; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.InstanceDeregisterRequest; +import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterRequest; +import com.tencent.polaris.api.rpc.InstanceRegisterResponse; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.test.common.Consts; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import java.io.IOException; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class ProviderTest { + + private NamingServer namingServer; + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @Test + public void testRoundTrip() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ProviderAPI providerAPI = DiscoveryAPIFactory.createProviderAPIByConfig(configuration)) { + for (int i = 0; i < 5; i++) { + namingServer.getNamingService().addService(new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER)); + //注册 + InstanceRegisterRequest registerRequest = new InstanceRegisterRequest(); + registerRequest.setNamespace(NAMESPACE_TEST); + registerRequest.setService(SERVICE_PROVIDER); + registerRequest.setHost(Consts.HOST); + registerRequest.setPort(Consts.PORT); + registerRequest.setProtocol("http"); + registerRequest.setToken(Consts.PROVIDER_TOKEN); + registerRequest.setTtl(5); + InstanceRegisterResponse response = providerAPI.register(registerRequest); + Assert.assertTrue(StringUtils.isNotBlank(response.getInstanceId())); + Assert.assertFalse(response.isExists()); + //再注册 + response = providerAPI.register(registerRequest); + Assert.assertTrue(StringUtils.isNotBlank(response.getInstanceId())); + Assert.assertTrue(response.isExists()); + Utils.sleepUninterrupted(5000); + //心跳上报 + InstanceHeartbeatRequest heartbeatRequest = new InstanceHeartbeatRequest(); + heartbeatRequest.setNamespace(NAMESPACE_TEST); + heartbeatRequest.setService(SERVICE_PROVIDER); + heartbeatRequest.setHost(Consts.HOST); + heartbeatRequest.setPort(Consts.PORT); + heartbeatRequest.setToken(Consts.PROVIDER_TOKEN); + providerAPI.heartbeat(heartbeatRequest); + //反注册 + InstanceDeregisterRequest deregisterRequest = new InstanceDeregisterRequest(); + deregisterRequest.setNamespace(NAMESPACE_TEST); + deregisterRequest.setService(SERVICE_PROVIDER); + deregisterRequest.setHost(Consts.HOST); + deregisterRequest.setPort(Consts.PORT); + deregisterRequest.setToken(Consts.PROVIDER_TOKEN); + providerAPI.deRegister(deregisterRequest); + } + } + } +} diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java new file mode 100644 index 000000000..119f5e196 --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.test.core; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICES; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * RetryConnectTest.java + * + * @author andrewshan + * @date 2019/9/7 + */ +public class RetryConnectTest { + + private static final Logger LOG = LoggerFactory.getLogger(RetryConnectTest.class); + + private NamingServer namingServer; + + @Before + public void before() { + Thread startThread = new Thread(new Runnable() { + @Override + public void run() { + Utils.sleepUninterrupted(1000); + LOG.info("now start the naming server mock"); + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } + LOG.info("finish starting the naming server mock"); + InstanceParameter parameter = new InstanceParameter(); + parameter.setHealthy(true); + parameter.setIsolated(false); + parameter.setWeight(100); + namingServer.getNamingService().batchAddInstances( + new ServiceKey(NAMESPACE_TEST, SERVICES[0]), 10100, 10, parameter); + } + }); + startThread.start(); + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + public static Configuration createMaxRetryConfiguration() { + ConfigurationImpl configuration = (ConfigurationImpl) TestUtils.configWithEnvAddress(); + configuration.setDefault(); + configuration.getGlobal().getAPI().setMaxRetryTimes(50); + configuration.getGlobal().getAPI().setRetryInterval(100); + return configuration; + } + + @Test + public void testRetryConsumer() { + ConsumerAPI consumerAPI = null; + try { + Configuration simpleConfiguration = createMaxRetryConfiguration(); + consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(simpleConfiguration); + Assert.assertNotNull(consumerAPI); + GetInstancesRequest req = new GetInstancesRequest(); + req.setNamespace(NAMESPACE_TEST); + req.setService(SERVICES[0]); + req.setTimeoutMs(5000); + long startTime = System.currentTimeMillis(); + consumerAPI.getInstances(req); + long endTime = System.currentTimeMillis(); + LOG.info(String.format("testGetInstances time consumer is %dms", endTime - startTime)); + } catch (PolarisException e) { + Assert.fail(e.getMessage()); + } finally { + if (null != consumerAPI) { + consumerAPI.destroy(); + } + } + } +} \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceDynamicRuleTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceDynamicRuleTest.java new file mode 100644 index 000000000..f9cc2944c --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceDynamicRuleTest.java @@ -0,0 +1,110 @@ +package com.tencent.polaris.discovery.test.core; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_PRODUCTION; + +import com.google.protobuf.StringValue; +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.client.pb.ModelProto.MatchString; +import com.tencent.polaris.client.pb.ModelProto.MatchString.MatchStringType; +import com.tencent.polaris.client.pb.RoutingProto.Destination; +import com.tencent.polaris.client.pb.RoutingProto.Route; +import com.tencent.polaris.client.pb.RoutingProto.Routing; +import com.tencent.polaris.client.pb.RoutingProto.Source; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * 北极星页面中配置的动态路由规则 + * 在上下游服务埋自定义的Metadata数据,这里不同于yaml中的Metadata + */ +public class ServiceDynamicRuleTest { + + public static final String RULE_ROUTER_SERVICE = "tdocs.manage.pay.trpc"; + public static final int MATCH_META_COUNT = 2; + private NamingServer namingServer; + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + ServiceKey serviceKey = new ServiceKey(NAMESPACE_PRODUCTION, RULE_ROUTER_SERVICE); + InstanceParameter parameter = new InstanceParameter(); + parameter.setWeight(100); + parameter.setHealthy(true); + parameter.setIsolated(false); + Map metadata = new HashMap<>(); + metadata.put("env", "base"); + parameter.setMetadata(metadata); + namingServer.getNamingService().batchAddInstances(serviceKey, 10001, MATCH_META_COUNT, parameter); + parameter.setMetadata(null); + namingServer.getNamingService().batchAddInstances(serviceKey, 10010, 8, parameter); + Map data = new HashMap<>(); + data.put("env", MatchString.newBuilder() + .setType(MatchStringType.EXACT) + .setValue(StringValue.newBuilder() + .setValue("base").build()) + .build()); + Map srcData = new HashMap<>(); + srcData.put("uid", MatchString.newBuilder() + .setType(MatchStringType.EXACT) + .setValue(StringValue.newBuilder() + .setValue("144115217417489762").build()) + .build()); + Routing routing = Routing.newBuilder() + .addInbounds(Route.newBuilder().addDestinations(Destination.newBuilder().putAllMetadata(data).setWeight( + UInt32Value.newBuilder().setValue(100).build()).build()) + .addSources(Source.newBuilder().setNamespace(StringValue.newBuilder().setValue("*").build()) + .setService( + StringValue.newBuilder().setValue("*").build()). + putAllMetadata(srcData).build()).build()) + .build(); + namingServer.getNamingService().setRouting(serviceKey, routing); + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @Test + public void testServiceDynamicRule() { + GetInstancesRequest getInstancesRequest = new GetInstancesRequest(); + getInstancesRequest.setNamespace(NAMESPACE_PRODUCTION); + getInstancesRequest.setService(RULE_ROUTER_SERVICE); + + Map map = new HashMap<>(); + map.put("uid", "144115217417489762"); + + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setNamespace(NAMESPACE_PRODUCTION); + serviceInfo.setService(RULE_ROUTER_SERVICE); + serviceInfo.setMetadata(map); + // 设置主调方服务信息 即 Metadata等规则信息 + getInstancesRequest.setServiceInfo(serviceInfo); + Configuration configuration = TestUtils.configWithEnvAddress(); + try (ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration)) { + InstancesResponse oneInstance = consumerAPI.getInstances(getInstancesRequest); + Assert.assertEquals(MATCH_META_COUNT, oneInstance.getInstances().length); + } + } +} \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceExpireTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceExpireTest.java new file mode 100644 index 000000000..fbea04f33 --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/ServiceExpireTest.java @@ -0,0 +1,171 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.test.core; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.SERVICES; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetOneInstanceRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServiceExpireTest.java + * + * @author andrewshan + * @date 2019/9/7 + */ +public class ServiceExpireTest { + + private static final Logger LOG = LoggerFactory.getLogger(ServiceExpireTest.class); + + private NamingServer namingServer; + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + InstanceParameter parameter = new InstanceParameter(); + parameter.setHealthy(true); + parameter.setIsolated(false); + parameter.setWeight(100); + for (int i = 0; i < 2; i++) { + namingServer.getNamingService().batchAddInstances( + new ServiceKey(NAMESPACE_TEST, SERVICES[i]), 10100, 10, parameter); + } + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + private static class GetServiceTask implements Runnable { + + private final ConsumerAPI consumerAPI; + + private final CountDownLatch countDownLatch; + + private final int index; + + GetServiceTask(ConsumerAPI consumerAPI, CountDownLatch countDownLatch, int index) { + this.consumerAPI = consumerAPI; + this.countDownLatch = countDownLatch; + this.index = index; + } + + @Override + public void run() { + try { + Assert.assertNotNull(consumerAPI); + GetOneInstanceRequest req = new GetOneInstanceRequest(); + req.setNamespace(NAMESPACE_TEST); + req.setService(SERVICES[index]); + req.setTimeoutMs(2 * 1000); + long startTime = System.currentTimeMillis(); + InstancesResponse instances = consumerAPI.getOneInstance(req); + long endTime = System.currentTimeMillis(); + LOG.info(String.format("testGetInstance time consumer is %dms", endTime - startTime)); + Assert.assertEquals(1, instances.getInstances().length); + } catch (PolarisException e) { + Assert.fail(e.getMessage()); + } finally { + if (null != countDownLatch) { + countDownLatch.countDown(); + } + } + } + } + + @Test + public void testMultiServices() { + ConsumerAPI consumerAPI = null; + try { + Configuration configuration = createConfiguration(); + consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration); + CountDownLatch countDownLatch = new CountDownLatch(2); + Thread firstThread = new Thread(new GetServiceTask(consumerAPI, countDownLatch, 0)); + Thread secondThread = new Thread(new GetServiceTask(consumerAPI, countDownLatch, 1)); + firstThread.start(); + secondThread.start(); + countDownLatch.await(2, TimeUnit.SECONDS); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } finally { + if (null != consumerAPI) { + consumerAPI.destroy(); + } + } + } + + @Test + public void testServiceExpiration() { + ConsumerAPI consumerAPI = null; + try { + Configuration configuration = createConfiguration(); + consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(configuration); + Runnable firstThread = new GetServiceTask(consumerAPI, null, 0); + Runnable secondThread = new GetServiceTask(consumerAPI, null, 1); + firstThread.run(); + Utils.sleepUninterrupted(2000); + secondThread.run(); + LOG.info("wait for expire"); + Utils.sleepUninterrupted(6000); + firstThread.run(); + secondThread.run(); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } finally { + if (null != consumerAPI) { + consumerAPI.destroy(); + } + } + + } + + private static Configuration createConfiguration() { + ConfigurationImpl configuration = (ConfigurationImpl) TestUtils.configWithEnvAddress(); + configuration.setDefault(); + configuration.getConsumer().getLocalCache().setServiceExpireTime(5000); + configuration.getGlobal().getServerConnector().setServerSwitchInterval(1500L); + return configuration; + } +} \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/suite/DiscoveryAPITestingSuite.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/suite/DiscoveryAPITestingSuite.java new file mode 100644 index 000000000..303b8b58f --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/suite/DiscoveryAPITestingSuite.java @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.discovery.test.suite; + +import com.tencent.polaris.discovery.test.core.ConsumerTest; +import com.tencent.polaris.discovery.test.core.MetadataRouterTest; +import com.tencent.polaris.discovery.test.core.ProviderTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + ConsumerTest.class, + ProviderTest.class, + MetadataRouterTest.class, +}) +public class DiscoveryAPITestingSuite { + +} diff --git a/polaris-discovery/polaris-discovery-factory/src/test/resources/log4j2.xml b/polaris-discovery/polaris-discovery-factory/src/test/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-discovery/polaris-discovery-factory/src/test/resources/polaris.yml b/polaris-discovery/polaris-discovery-factory/src/test/resources/polaris.yml new file mode 100644 index 000000000..94a2fedf8 --- /dev/null +++ b/polaris-discovery/polaris-discovery-factory/src/test/resources/polaris.yml @@ -0,0 +1,21 @@ +global: + #描述: 系统相关配置 + system: + variables: + x: y + x1: y1 + #描述:对接polaris server的相关配置 + serverConnector: + addresses: + - 127.0.0.1:10081 + api: + #api超时时间 + timeout: 2s +#描述:主调端配置 +consumer: + localCache: + persistEnable: false + #描述:节点熔断相关配置 + circuitBreaker: + #描述:是否启用节点熔断功能 + enable: false \ No newline at end of file diff --git a/polaris-discovery/pom.xml b/polaris-discovery/pom.xml new file mode 100644 index 000000000..cbd2ac457 --- /dev/null +++ b/polaris-discovery/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-discovery + pom + + + polaris-discovery-api + polaris-discovery-client + polaris-discovery-examples + polaris-discovery-factory + + \ No newline at end of file diff --git a/polaris-distribution/polaris-factory-shaded/pom.xml b/polaris-distribution/polaris-factory-shaded/pom.xml new file mode 100644 index 000000000..eed47ab52 --- /dev/null +++ b/polaris-distribution/polaris-factory-shaded/pom.xml @@ -0,0 +1,83 @@ + + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../../pom.xml + + + polaris-factory-shaded + 4.0.0 + + + com.tencent.nameservice + polaris-factory + ${project.version} + + + + + + + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + com.google + shade.polaris.com.google + + + com.fasterxml + shade.polaris.com.fasterxml + + + google + shade.polaris.google + + + io.grpc.protobuf + shade.polaris.io.grpc.protobuf + + + + + + + + + + + + tencent_public_snapshots + tencent_public_snapshot + https://mirrors.tencent.com/repository/maven/tencent_public_snapshots/ + + + + tencent_public + tencent_public_release + https://mirrors.tencent.com/repository/maven/tencent_public/ + + + \ No newline at end of file diff --git a/polaris-distribution/pom.xml b/polaris-distribution/pom.xml new file mode 100644 index 000000000..19dac584d --- /dev/null +++ b/polaris-distribution/pom.xml @@ -0,0 +1,19 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-distribution + pom + + + polaris-factory-shaded + + \ No newline at end of file diff --git a/polaris-factory/pom.xml b/polaris-factory/pom.xml new file mode 100644 index 000000000..3ffb1f9e9 --- /dev/null +++ b/polaris-factory/pom.xml @@ -0,0 +1,46 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-factory + + + + com.tencent.nameservice + polaris-discovery-factory + ${project.version} + + + com.tencent.nameservice + polaris-circuitbreaker-factory + ${project.version} + + + com.tencent.nameservice + polaris-ratelimit-factory + ${project.version} + + + + + com.tencent.nameservice + polaris-test-common + ${project.version} + test + + + com.tencent.nameservice + polaris-test-mock-discovery + ${project.version} + test + + + \ No newline at end of file diff --git a/polaris-factory/src/main/java/com/tencent/polaris/factory/api/APIFactory.java b/polaris-factory/src/main/java/com/tencent/polaris/factory/api/APIFactory.java new file mode 100644 index 000000000..6fbda80fd --- /dev/null +++ b/polaris-factory/src/main/java/com/tencent/polaris/factory/api/APIFactory.java @@ -0,0 +1,209 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.api; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.ConfigAPIFactory; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.factory.LimitAPIFactory; +import java.io.InputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class APIFactory { + + private static final Logger LOG = LoggerFactory.getLogger(APIFactory.class); + + /** + * 创建默认配置的对象,优先获取conf/polaris.yaml配置文件,假如没有,则创建默认的配置对象 + * + * @return 配置对象 + * @throws PolarisException 初始化异常 + */ + public static Configuration defaultConfig() throws PolarisException { + return ConfigAPIFactory.defaultConfig(); + } + + /** + * 通过配置文件加载配置对象 + * + * @param configStream 配置文件流 + * @return 配置对象 + * @throws PolarisException 文件加载异常 + */ + public static Configuration loadConfig(InputStream configStream) throws PolarisException { + return ConfigAPIFactory.loadConfig(configStream); + } + + /** + * 通过默认配置初始化SDK上下文 + * + * @return SDK上下文 + * @throws PolarisException 初始化过程的异常 + */ + public static SDKContext initContext() throws PolarisException { + return SDKContext.initContext(); + } + + /** + * 通过配置对象初始化SDK上下文 + * + * @param config 配置对象 + * @return SDK上下文 + * @throws PolarisException 初始化过程的异常 + */ + public static SDKContext initContextByConfig(Configuration config) throws PolarisException { + return SDKContext.initContextByConfig(config); + } + + /** + * 通过配置文件初始化SDK上下文 + * + * @param inputStream 配置文件 + * @return SDK上下文 + * @throws PolarisException 初始化过程的异常 + */ + public static SDKContext initContextByFile(InputStream inputStream) throws PolarisException { + Configuration config = ConfigAPIFactory.loadConfig(inputStream); + return SDKContext.initContextByConfig(config); + } + + /** + * 通过默认配置创建ConsumerAPI + * + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPI() throws PolarisException { + return DiscoveryAPIFactory.createConsumerAPI(); + } + + /** + * 通过SDK上下文创建ConsumerAPI + * + * @param context SDK上下文,包含插件列表,配置对象等信息 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPIByContext(SDKContext context) throws PolarisException { + return DiscoveryAPIFactory.createConsumerAPIByContext(context); + } + + /** + * 通过配置文件创建ConsumerAPI + * + * @param inputStream 文件 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPIByFile(InputStream inputStream) throws PolarisException { + SDKContext context = initContextByFile(inputStream); + return createConsumerAPIByContext(context); + } + + /** + * 通过配置对象创建ConsumerAPI + * + * @param config 配置对象 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ConsumerAPI createConsumerAPIByConfig(Configuration config) throws PolarisException { + return DiscoveryAPIFactory.createConsumerAPIByConfig(config); + } + + /** + * 通过默认配置创建ProviderAPI + * + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPI() throws PolarisException { + return DiscoveryAPIFactory.createProviderAPI(); + } + + /** + * 通过SDK上下文创建ProviderAPI + * + * @param context SDK上下文,包含插件列表,配置对象等信息 + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPIByContext(SDKContext context) throws PolarisException { + return DiscoveryAPIFactory.createProviderAPIByContext(context); + } + + /** + * 通过配置对象创建ProviderAPI + * + * @param config 配置对象 + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPIByConfig(Configuration config) throws PolarisException { + return DiscoveryAPIFactory.createProviderAPIByConfig(config); + } + + /** + * 通过配置文件创建ProviderAPI + * + * @param inputStream 文件 + * @return ProviderAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static ProviderAPI createProviderAPIByFile(InputStream inputStream) throws PolarisException { + SDKContext context = initContextByFile(inputStream); + return createProviderAPIByContext(context); + } + + /** + * 通过默认配置创建LimitAPI + * + * @return LimitAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static LimitAPI createLimitAPI() throws PolarisException { + return LimitAPIFactory.createLimitAPI(); + } + + /** + * 通过SDK上下文创建LimitAPI + * + * @param context SDK上下文,包含插件列表,配置对象等信息 + * @return LimitAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static LimitAPI createLimitAPIByContext(SDKContext context) throws PolarisException { + return LimitAPIFactory.createLimitAPIByContext(context); + } + + /** + * 通过配置对象创建LimitAPI + * + * @param config 配置对象 + * @return LimitAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static LimitAPI createLimitAPIByConfig(Configuration config) throws PolarisException { + return LimitAPIFactory.createLimitAPIByConfig(config); + } +} diff --git a/polaris-factory/src/test/java/com/tencent/polaris/factory/api/test/APIFactoryTest.java b/polaris-factory/src/test/java/com/tencent/polaris/factory/api/test/APIFactoryTest.java new file mode 100644 index 000000000..38c8663d9 --- /dev/null +++ b/polaris-factory/src/test/java/com/tencent/polaris/factory/api/test/APIFactoryTest.java @@ -0,0 +1,114 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.api.test; + +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.Supplier; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.api.APIFactory; +import com.tencent.polaris.test.common.TestUtils; +import java.io.InputStream; +import org.junit.Assert; +import org.junit.Test; + +/** + * 测试API工厂相关接口 + * + * @author andrewshan + * @date 2019/8/28 + */ +public class APIFactoryTest { + + @Test + public void testInitContextByFile() { + InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("conf/default.yaml"); + SDKContext sdkContext = null; + try { + sdkContext = APIFactory.initContextByFile(resourceAsStream); + Supplier plugins = sdkContext.getPlugins(); + Plugin plugin = plugins + .getPlugin(PluginTypes.CIRCUIT_BREAKER.getBaseType(), DefaultPlugins.CIRCUIT_BREAKER_ERROR_COUNT); + Assert.assertNotNull(plugin); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } finally { + if (null != sdkContext) { + sdkContext.destroy(); + } + } + } + + @Test + public void testInitContextByConfig() { + SDKContext sdkContext = null; + try { + sdkContext = APIFactory.initContextByConfig(TestUtils.createSimpleConfiguration(8888)); + Supplier plugins = sdkContext.getPlugins(); + Plugin plugin = plugins.getPlugin( + PluginTypes.LOAD_BALANCER.getBaseType(), LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM); + Assert.assertNotNull(plugin); + } catch (PolarisException e) { + Assert.fail(e.getMessage()); + } finally { + if (null != sdkContext) { + sdkContext.destroy(); + } + } + } + + @Test + public void testCreateConsumerAPIByFile() { + // FIXME @thrteenwang update route config in default.yaml + InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("conf/default.yaml"); + ConsumerAPI consumerAPI = null; + try { + consumerAPI = APIFactory.createConsumerAPIByFile(resourceAsStream); + Assert.assertNotNull(consumerAPI); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } finally { + if (null != consumerAPI) { + consumerAPI.destroy(); + } + } + } + + @Test + public void testCreateProviderAPIByFile() { + // FIXME @thrteenwang update route config in default.yaml + InputStream resourceAsStream = getClass().getClassLoader().getResourceAsStream("conf/default.yaml"); + ProviderAPI providerAPI = null; + try { + providerAPI = APIFactory.createProviderAPIByFile(resourceAsStream); + Assert.assertNotNull(providerAPI); + } catch (Exception e) { + Assert.fail(e.getMessage()); + } finally { + if (null != providerAPI) { + providerAPI.destroy(); + } + } + } + +} diff --git a/polaris-factory/src/test/resources/conf/default.yaml b/polaris-factory/src/test/resources/conf/default.yaml new file mode 100644 index 000000000..20be9cc93 --- /dev/null +++ b/polaris-factory/src/test/resources/conf/default.yaml @@ -0,0 +1,88 @@ +global: + #描述系统相关配置 + system: + #描述:SDK运行模式 + #类型:enum + #范围:0(直连模式,SDK直接对接server); 1(代理模式,SDK只对接agent, 通过agent进行server的对接) + #默认值:0 + mode: 0 + #服务发现集群 + discoverCluster: + namespace: Polaris + service: polaris.discover + #可选:服务刷新间隔 + refreshInterval: 10m + #健康检查集群 + healthCheckCluster: + namespace: Polaris + service: polaris.healthcheck + #可选:服务刷新间隔 + refreshInterval: 10m + #监控上报集群 + monitorCluster: + namespace: Polaris + service: polaris.monitor + #可选:服务刷新间隔 + refreshInterval: 10m + api: + #客户端绑定的网卡地址,默认eth1 + bindIf: eth1 + #客户端上报周期,默认30s + reportInterval: 30s + #api超时时间 + timeout: 1s + #最大重试次数,默认10次 + maxRetryTimes: 10 + #重试间隔,默认5ms + retryInterval: 5ms + flowCache: + enable: true + name: simpleCache + expireInterval: 60s + serverConnector: + mode: 0 + addresses: + - 9.157.1.185:8090 #上海公共集群埋点 + - 9.87.200.63:8090 #深圳公共集群埋点 + protocol: grpc + connectTimeout: 50ms + connectionIdleTimeout: 100ms + #对接的polaris server节点切换周期 + serverSwitchInterval: 10m + plugin: { } +consumer: + localCache: + serviceExpireTime: 24h + serviceRefreshInterval: 2s + persistDir: $HOME/polaris/backup + type: inmemory + #最多隔离节点数的百分比 + #当隔离节点数超过该阈值,负载均衡取消隔离不健康节点,默认90% + maxEjectPercentThreshold: 0.9 + plugin: { } + serviceRouter: + chain: + - ruleBasedRouter + loadbalancer: + type: weightedRandom + circuitbreaker: + enable: false + checkPeriod: 5s + chain: + - errorCount + - errorRate + plugin: + errCount: + continuousErrorThreshold: 10 + metricStatTimeWindow: 1s + requestCountAfterHalfOpen: 3 + sleepWindow: 5s + successCountAfterHalfOpen: 2 + errRate: + errorRateThreshold: 0.5 + metricNumBuckets: 12 + metricStatTimeWindow: 1m + requestCountAfterHalfOpen: 3 + requestVolumeThreshold: 10 + sleepWindow: 5s + successCountAfterHalfOpen: 2 diff --git a/polaris-factory/src/test/resources/log4j2.xml b/polaris-factory/src/test/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-factory/src/test/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-factory/src/test/resources/svc1_routing.json b/polaris-factory/src/test/resources/svc1_routing.json new file mode 100644 index 000000000..ea257d018 --- /dev/null +++ b/polaris-factory/src/test/resources/svc1_routing.json @@ -0,0 +1,40 @@ +{ + "service": "svc1", + "namespace": "NAMESPACE-test", + "outbounds": [ + { + "sources": [ + { + "metadata": { + "env": { + "type": 0, + "value": "base" + } + } + } + ], + "destinations": [ + { + "service": "*", + "metadata": { + "env": { + "type": 0, + "value": "base" + } + }, + "priority": 0 + }, + { + "service": "*", + "metadata": { + "env": { + "type": 0, + "value": "test" + } + }, + "priority": 1 + } + ] + } + ] +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/pom.xml b/polaris-plugins/polaris-plugin-api/pom.xml new file mode 100644 index 000000000..3351cdbd6 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-plugins + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugin-api + + + + com.tencent.nameservice + polaris-config + ${project.version} + + + com.tencent.nameservice + polaris-model + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/IdAwarePlugin.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/IdAwarePlugin.java new file mode 100644 index 000000000..9bc92081d --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/IdAwarePlugin.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin; + +public interface IdAwarePlugin extends Plugin { + + /** + * 获取插件ID + * + * @return pluginID + */ + int getId(); + + /** + * 设置插件ID + * + * @param id 插件ID + */ + void setId(int id); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Manager.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Manager.java new file mode 100644 index 000000000..6bc9e9bf8 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Manager.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.compose.Extensions; + +/** + * 插件管理器 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface Manager extends Supplier { + + /** + * 初始化插件列表 + * + * @param context 插件初始化上下文 + * @throws PolarisException 插件初始化过程中抛出异常 + */ + void initPlugins(InitContext context) throws PolarisException; + + /** + * 在应用上下文初始化完毕后进行调用 + * + * @param extensions 插件实例 + * @throws PolarisException 执行任务过程抛出异常 + */ + void postContextInitPlugins(Extensions extensions) throws PolarisException; + + /** + * 销毁已初始化的插件列表 + */ + void destroyPlugins(); + + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Plugin.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Plugin.java new file mode 100644 index 000000000..990944e3f --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Plugin.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.compose.Extensions; + +/** + * 插件的通用接口,包含插件的标识 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface Plugin { + + /** + * 获取插件名 + * + * @return String + */ + String getName(); + + /** + * 获取插件类型 + * + * @return type + */ + PluginType getType(); + + /** + * 初始化插件,在AppContext初始化之前调用 + * + * @param ctx 初始化上下文 + */ + void init(InitContext ctx) throws PolarisException; + + /** + * 在整个AppContext初始化完毕后调用 + * + * @param ctx 插件实例信息 + * @throws PolarisException 执行任务过程抛出异常 + */ + void postContextInit(Extensions ctx) throws PolarisException; + + /** + * 销毁插件 + */ + void destroy(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/PluginType.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/PluginType.java new file mode 100644 index 000000000..1298d462d --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/PluginType.java @@ -0,0 +1,82 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin; + +import java.util.Objects; + +/** + * 插件类型定义 + */ +public class PluginType implements Comparable { + + /** + * 插件的类型 + */ + private final Class clazz; + + /** + * 插件等级,决定初始化顺序 + */ + private final int level; + + public PluginType(Class clazz, int level) { + this.clazz = clazz; + this.level = level; + } + + public Class getClazz() { + return clazz; + } + + public int getLevel() { + return level; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PluginType that = (PluginType) o; + return level == that.level && + Objects.equals(clazz, that.clazz); + } + + @Override + public int hashCode() { + return Objects.hash(clazz, level); + } + + @Override + public int compareTo(PluginType o) { + return level - o.getLevel(); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "PluginType{" + + "clazz=" + clazz.getCanonicalName() + + ", level=" + level + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Supplier.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Supplier.java new file mode 100644 index 000000000..848d8cd78 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/Supplier.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin; + +import com.tencent.polaris.api.exception.PolarisException; +import java.util.Collection; + +/** + * 插件提供器 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface Supplier { + + /** + * 获取插件实例 + * + * @param type 插件类型 + * @param name 插件名 + * @return 插件实例 + * @throws PolarisException 获取失败抛出异常 + */ + Plugin getPlugin(PluginType type, String name) throws PolarisException; + + /** + * 获取某类型下的所有插件 + * + * @param type 插件类型 + * @return 插件实例列表 + * @throws PolarisException 获取失败异常 + */ + Collection getPlugins(PluginType type) throws PolarisException; + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/TypeProvider.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/TypeProvider.java new file mode 100644 index 000000000..fb8a07427 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/TypeProvider.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin; + +import java.util.List; + +/** + * 插件类型提供者 + */ +public interface TypeProvider { + + /** + * 获取插件类型列表 + * + * @return list + */ + List getTypes(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/FlowCache.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/FlowCache.java new file mode 100644 index 000000000..9f1ddcf3d --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/FlowCache.java @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.cache; + +import com.tencent.polaris.api.plugin.Plugin; +import java.util.function.Function; +import java.util.regex.Pattern; + +/** + * 流程缓存管理器 + */ +public interface FlowCache extends Plugin { + + /** + * 获取 + * + * @param regexStr 正则表达式原始字符 + * @return 编译正则后的结果 + */ + Pattern loadOrStoreCompiledRegex(String regexStr); + + /** + * 借取缓存对象 + * + * @param clazz 缓存类型 + * @param 类型 + * @return 缓存对象 + */ + T borrowThreadCacheObject(Class clazz); + + /** + * 归还缓存对象 + * + * @param object 缓存对象 + * @param 类型 + */ + void giveBackThreadCacheObject(T object); + + /** + * 加载插件缓存,提供函数,当不存在时候进行构建 + * + * @param pluginIdx 插件ID + * @param key 缓存键 + * @param createFunc 创建缓存值的工厂方法 + * @param 缓存值类型 + * @return 缓存值 + */ + T loadPluginCacheObject(int pluginIdx, Object key, Function createFunc); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/PluginHashKey.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/PluginHashKey.java new file mode 100644 index 000000000..f63cedd74 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/cache/PluginHashKey.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.cache; + +import com.tencent.polaris.api.plugin.Plugin; +import java.util.Objects; + +public class PluginHashKey { + + private final Class type; + + private final int hashCode; + + public PluginHashKey(Class type, int hashCode) { + this.type = type; + this.hashCode = hashCode; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PluginHashKey that = (PluginHashKey) o; + return hashCode == that.hashCode && + Objects.equals(type, that.type); + } + + @Override + public int hashCode() { + return Objects.hash(type, hashCode); + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreakResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreakResult.java new file mode 100644 index 000000000..b90139080 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreakResult.java @@ -0,0 +1,148 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.circuitbreaker; + +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.Subset; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 熔断结果 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class CircuitBreakResult { + + public static class ResultKey { + + private final String instId; + + private final StatusDimension statusDimension; + + public ResultKey(String instId, StatusDimension statusDimension) { + this.instId = instId; + this.statusDimension = statusDimension; + } + + public String getInstId() { + return instId; + } + + public StatusDimension getStatusDimension() { + return statusDimension; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ResultKey resultKey = (ResultKey) o; + return Objects.equals(instId, resultKey.instId) && Objects + .equals(statusDimension, resultKey.statusDimension); + } + + @Override + public int hashCode() { + return Objects.hash(instId, statusDimension); + } + } + + private final long createTimeMs; + + private final int maxRequestCountAfterHalfOpen; + + private final Map> instanceResult = new HashMap<>(); + + private final Map> subsetResult = new HashMap<>(); + + public CircuitBreakResult(long createTimeMs, int maxRequestCountAfterHalfOpen) { + this.createTimeMs = createTimeMs; + this.maxRequestCountAfterHalfOpen = maxRequestCountAfterHalfOpen; + instanceResult.put(CircuitBreakerStatus.Status.CLOSE, new HashMap<>()); + instanceResult.put(CircuitBreakerStatus.Status.HALF_OPEN, new HashMap<>()); + instanceResult.put(CircuitBreakerStatus.Status.OPEN, new HashMap<>()); + subsetResult.put(CircuitBreakerStatus.Status.CLOSE, new HashMap<>()); + subsetResult.put(CircuitBreakerStatus.Status.HALF_OPEN, new HashMap<>()); + subsetResult.put(CircuitBreakerStatus.Status.OPEN, new HashMap<>()); + } + + public boolean isEmptyResult() { + for (Map.Entry> entry : instanceResult.entrySet()) { + if (!entry.getValue().isEmpty()) { + return false; + } + } + for (Map.Entry> entry : subsetResult.entrySet()) { + if (!entry.getValue().isEmpty()) { + return false; + } + } + return true; + } + + public Map getInstancesToOpen() { + return instanceResult.get(CircuitBreakerStatus.Status.OPEN); + } + + public Map getInstancesToHalfOpen() { + return instanceResult.get(CircuitBreakerStatus.Status.HALF_OPEN); + } + + public Map getInstancesToClose() { + return instanceResult.get(CircuitBreakerStatus.Status.CLOSE); + } + + public Map getSubsetsToOpen() { + return subsetResult.get(CircuitBreakerStatus.Status.OPEN); + } + + public Map getSubsetsToHalfOpen() { + return subsetResult.get(CircuitBreakerStatus.Status.HALF_OPEN); + } + + public Map getSubsetsToClose() { + return subsetResult.get(CircuitBreakerStatus.Status.CLOSE); + } + + public long getCreateTimeMs() { + return createTimeMs; + } + + public int getMaxRequestCountAfterHalfOpen() { + return maxRequestCountAfterHalfOpen; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "CircuitBreakResult{" + + + "result=" + instanceResult + + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreaker.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreaker.java new file mode 100644 index 000000000..17a1ab78c --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/circuitbreaker/CircuitBreaker.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.circuitbreaker; + +import com.tencent.polaris.api.plugin.IdAwarePlugin; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.Subset; +import java.util.Collection; + +/** + * 【扩展点接口】节点熔断器 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface CircuitBreaker extends IdAwarePlugin { + + /** + * 定期进行实例熔断计算,返回需要进行状态转换的实例ID. + * + * @param instances 服务实例列表 + * @return 熔断结果 + */ + CircuitBreakResult checkInstance(Collection instances); + + /** + * 定期进行实例分组熔断计算 + * + * @param subsets 实例分组 + * @return 熔断结果 + */ + CircuitBreakResult checkSubset(Collection subsets); + + /** + * 实时上报健康状态并进行连续失败熔断判断,返回当前实例是否需要进行立即熔断. + * + * @param metric 服务调用结果信息 + * @return 是否需要进行实时熔断 + */ + boolean stat(InstanceGauge metric); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/InitContext.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/InitContext.java new file mode 100644 index 000000000..aab409d3a --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/InitContext.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.common; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.plugin.Supplier; +import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; +import java.util.Collection; + +/** + * 插件初始化相关的上下文接口 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface InitContext { + + Configuration getConfig(); + + Supplier getPlugins(); + + ValueContext getValueContext(); + + Collection getServerServices(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java new file mode 100644 index 000000000..501150a2c --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/PluginTypes.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.common; + +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; +import com.tencent.polaris.api.plugin.detect.OutlierDetector; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.plugin.server.ServerConnector; +import com.tencent.polaris.api.plugin.stat.StatReporter; +import com.tencent.polaris.api.plugin.weight.WeightAdjuster; + +/** + * The plugin types that our framework support. + */ +public enum PluginTypes { + /** + * 注册中心连接器扩展点 + */ + SERVER_CONNECTOR(new PluginType(ServerConnector.class, 0)), + + /** + * 流程缓存扩展点 + */ + FLOW_CACHE(new PluginType(FlowCache.class, 0)), + + /** + * 本地缓存扩展点 + */ + LOCAL_REGISTRY(new PluginType(LocalRegistry.class, 1)), + + /** + * 服务路由扩展点 + */ + SERVICE_ROUTER(new PluginType(ServiceRouter.class, 2)), + + /** + * 负载均衡扩展点 + */ + LOAD_BALANCER(new PluginType(LoadBalancer.class, 2)), + + /** + * 健康探测扩展点 + */ + OUTLIER_DETECTOR(new PluginType(OutlierDetector.class, 2)), + + /** + * 节点熔断扩展点 + */ + CIRCUIT_BREAKER(new PluginType(CircuitBreaker.class, 2)), + + /** + * 动态权重调整扩展点 + */ + WEIGHT_ADJUSTER(new PluginType(WeightAdjuster.class, 2)), + + /** + * 统计上报扩展点 + */ + STAT_REPORTER(new PluginType(StatReporter.class, 2)), + + /** + * 限流器扩展点 + */ + SERVICE_LIMITER(new PluginType(ServiceRateLimiter.class, 2)); + + private PluginType baseType; + + PluginTypes(PluginType baseType) { + this.baseType = baseType; + } + + public PluginType getBaseType() { + return baseType; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/RpcTypeProviders.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/RpcTypeProviders.java new file mode 100644 index 000000000..5f4f6b71b --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/RpcTypeProviders.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.common; + +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.TypeProvider; +import java.util.ArrayList; +import java.util.List; + +public class RpcTypeProviders implements TypeProvider { + + @Override + public List getTypes() { + List types = new ArrayList<>(); + for (PluginTypes type : PluginTypes.values()) { + types.add(type.getBaseType()); + } + return types; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/ValueContext.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/ValueContext.java new file mode 100644 index 000000000..48d39d531 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/common/ValueContext.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.api.plugin.common; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 用于主流程传递kv数据的上下文对象,线程安全 + * + * @author andrewshan + * @date 2019/9/18 + */ +public class ValueContext { + + private static final Logger LOG = LoggerFactory.getLogger(ValueContext.class); + + private static final String KEY_HOST = "key_host"; + + private static final String KEY_CLIENT_ID = "key_clientId"; + + private final Object lock = new Object(); + + private volatile boolean locationReady; + + private final Map coreMap; + + public ValueContext() { + coreMap = new ConcurrentHashMap<>(); + } + + public void setValue(String key, T value) { + coreMap.put(key, value); + } + + @SuppressWarnings("unchecked") + public T getValue(String key) { + return (T) coreMap.get(key); + } + + /** + * 等待地域信息就绪 + * + * @param timeoutMs 超时时间 + * @throws InterruptedException 中断异常 + */ + public void waitForLocationReady(long timeoutMs) throws InterruptedException { + if (LOG.isDebugEnabled()) { + LOG.debug("waitForLocationReady: timeoutMs:{}, is ready:{},object:{}", timeoutMs, + locationReady, this); + } + if (locationReady) { + return; + } + + synchronized (lock) { + lock.wait(timeoutMs); + } + } + + public String getHost() { + return getValue(KEY_HOST); + } + + public void setHost(String host) { + setValue(KEY_HOST, host); + } + + public String getClientId() { + return getValue(KEY_CLIENT_ID); + } + + public void setClientId(String clientId) { + setValue(KEY_CLIENT_ID, clientId); + } + + public void notifyAllForLocationReady() { + if (LOG.isDebugEnabled()) { + LOG.debug("notifyAllForLocationReady:object:{}", this); + } + locationReady = true; + + synchronized (lock) { + lock.notifyAll(); + } + } + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/DefaultRouterChainGroup.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/DefaultRouterChainGroup.java new file mode 100644 index 000000000..7fd6aae48 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/DefaultRouterChainGroup.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.compose; + +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import java.util.List; + +/** + * 基本路由链 + */ +public class DefaultRouterChainGroup implements RouterChainGroup { + + private final List beforeRouters; + + private final List coreRouters; + + private final List afterRouters; + + public DefaultRouterChainGroup(List beforeRouters, + List coreRouters, + List afterRouters) { + this.beforeRouters = beforeRouters; + this.coreRouters = coreRouters; + this.afterRouters = afterRouters; + } + + @Override + public List getBeforeRouters() { + return beforeRouters; + } + + @Override + public List getCoreRouters() { + return coreRouters; + } + + @Override + public List getAfterRouters() { + return afterRouters; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java new file mode 100644 index 000000000..62dc7d7e0 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/Extensions.java @@ -0,0 +1,189 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.compose; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig.When; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Supplier; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.common.ValueContext; +import com.tencent.polaris.api.plugin.detect.OutlierDetector; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.plugin.server.ServerConnector; +import com.tencent.polaris.api.utils.CollectionUtils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * 流程编排所需要用到的插件实例列表 + * + * @author andrewshan + */ +public class Extensions { + + private LocalRegistry localRegistry; + + private ServerConnector serverConnector; + + private LoadBalancer loadBalancer; + + private final List circuitBreakers = new ArrayList<>(); + + private final List outlierDetectors = new ArrayList<>(); + + private Configuration configuration; + + private Supplier plugins; + + //系统服务的路由链 + private RouterChainGroup sysRouterChainGroup; + + //配置文件中加载的路由链 + private RouterChainGroup configRouterChainGroup; + + //流程缓存引擎 + private FlowCache flowCache; + + //全局变量 + private ValueContext valueContext; + + /** + * 初始化 + * + * @param config 配置 + * @param plugins 插件工厂 + * @param valueContext 全局变量 + * @throws PolarisException 异常 + */ + public void init(Configuration config, Supplier plugins, ValueContext valueContext) throws PolarisException { + this.configuration = config; + this.plugins = plugins; + this.valueContext = valueContext; + String localCacheType = config.getConsumer().getLocalCache().getType(); + localRegistry = (LocalRegistry) plugins.getPlugin(PluginTypes.LOCAL_REGISTRY.getBaseType(), localCacheType); + String flowCacheName = config.getGlobal().getSystem().getFlowCache().getName(); + flowCache = (FlowCache) plugins.getPlugin(PluginTypes.FLOW_CACHE.getBaseType(), flowCacheName); + String loadBalanceType = config.getConsumer().getLoadbalancer().getType(); + loadBalancer = (LoadBalancer) plugins.getPlugin(PluginTypes.LOAD_BALANCER.getBaseType(), loadBalanceType); + + List beforeRouters = loadServiceRouters(config.getConsumer().getServiceRouter().getBeforeChain(), + plugins); + List coreRouters = loadServiceRouters(config.getConsumer().getServiceRouter().getChain(), + plugins); + List afterRouters = loadServiceRouters(config.getConsumer().getServiceRouter().getAfterChain(), + plugins); + configRouterChainGroup = new DefaultRouterChainGroup(beforeRouters, coreRouters, afterRouters); + //加载系统路由链 + List sysBefore = new ArrayList<>(); + sysBefore.add(ServiceRouterConfig.DEFAULT_ROUTER_ISOLATED); + List sysAfter = new ArrayList<>(); + sysAfter.add(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER); + List sysBeforeRouters = loadServiceRouters(sysBefore, plugins); + List sysAfterRouters = loadServiceRouters(sysAfter, plugins); + sysRouterChainGroup = new DefaultRouterChainGroup(sysBeforeRouters, Collections.emptyList(), sysAfterRouters); + + //加载熔断器 + boolean enable = config.getConsumer().getCircuitBreaker().isEnable(); + List cbChain = config.getConsumer().getCircuitBreaker().getChain(); + if (enable && CollectionUtils.isNotEmpty(cbChain)) { + for (String cbName : cbChain) { + circuitBreakers.add((CircuitBreaker) plugins.getPlugin( + PluginTypes.CIRCUIT_BREAKER.getBaseType(), cbName)); + } + } + + //加载探测器 + loadOutlierDetector(config, plugins); + + String protocol = config.getGlobal().getServerConnector().getProtocol(); + serverConnector = (ServerConnector) plugins.getPlugin(PluginTypes.SERVER_CONNECTOR.getBaseType(), protocol); + + } + + public ValueContext getValueContext() { + return valueContext; + } + + public static List loadServiceRouters(List routerChain, Supplier plugins) { + List routers = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(routerChain)) { + for (String routerName : routerChain) { + routers.add((ServiceRouter) plugins.getPlugin(PluginTypes.SERVICE_ROUTER.getBaseType(), routerName)); + } + } + return Collections.unmodifiableList(routers); + } + + private void loadOutlierDetector(Configuration config, Supplier plugins) throws PolarisException { + boolean enable = config.getConsumer().getOutlierDetection().getWhen() != When.never; + List detectionChain = config.getConsumer().getOutlierDetection().getChain(); + if (enable && CollectionUtils.isNotEmpty(detectionChain)) { + for (String detectorName : detectionChain) { + outlierDetectors.add((OutlierDetector) plugins.getPlugin( + PluginTypes.OUTLIER_DETECTOR.getBaseType(), detectorName)); + } + } + } + + public Supplier getPlugins() { + return plugins; + } + + public LocalRegistry getLocalRegistry() { + return localRegistry; + } + + public LoadBalancer getLoadBalancer() { + return loadBalancer; + } + + public List getCircuitBreakers() { + return circuitBreakers; + } + + public List getOutlierDetectors() { + return outlierDetectors; + } + + public Configuration getConfiguration() { + return configuration; + } + + public ServerConnector getServerConnector() { + return serverConnector; + } + + public RouterChainGroup getSysRouterChainGroup() { + return sysRouterChainGroup; + } + + public RouterChainGroup getConfigRouterChainGroup() { + return configRouterChainGroup; + } + + public FlowCache getFlowCache() { + return flowCache; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/RouterChainGroup.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/RouterChainGroup.java new file mode 100644 index 000000000..4e8d785ac --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/RouterChainGroup.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.compose; + +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import java.util.List; + +/** + * 路由链分组 + */ +public interface RouterChainGroup { + + /** + * 获取前置路由链 + * + * @return 前置路由链 + */ + List getBeforeRouters(); + + /** + * 获取核心路由链 + * + * @return 核心路由链 + */ + List getCoreRouters(); + + /** + * 获取后置路由链 + * + * @return 后置路由链 + */ + List getAfterRouters(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/ServerServiceInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/ServerServiceInfo.java new file mode 100644 index 000000000..906080a9e --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/compose/ServerServiceInfo.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.compose; + +import com.tencent.polaris.api.config.global.ClusterConfig; +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.ServiceKey; +import java.util.Collections; +import java.util.List; + +public class ServerServiceInfo { + + private final ServiceKey serviceKey; + + private final ClusterType clusterType; + + private final long refreshIntervalMs; + + private final List routers; + + private final String lbPolicy; + + public ServerServiceInfo(ClusterType clusterType, ClusterConfig clusterConfig) + throws PolarisException { + this.clusterType = clusterType; + this.serviceKey = new ServiceKey(clusterConfig.getNamespace(), clusterConfig.getService()); + this.refreshIntervalMs = clusterConfig.getRefreshInterval(); + routers = Collections.unmodifiableList(clusterConfig.getRouters()); + lbPolicy = clusterConfig.getLbPolicy(); + } + + public ClusterType getClusterType() { + return clusterType; + } + + public long getRefreshIntervalMs() { + return refreshIntervalMs; + } + + public ServiceKey getServiceKey() { + return serviceKey; + } + + public List getRouters() { + return routers; + } + + public String getLbPolicy() { + return lbPolicy; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServerServiceInfo{" + + "serviceKey=" + serviceKey + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/detect/OutlierDetector.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/detect/OutlierDetector.java new file mode 100644 index 000000000..51549cca9 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/detect/OutlierDetector.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.detect; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; + +/** + * 【扩展点接口】主动健康探测策略 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface OutlierDetector extends Plugin { + + /** + * 对单个实例进行探测,返回探测结果 + * 每个探测方法自己去判断当前周期是否需要探测,如果无需探测,则返回nil + * + * @param instance 单个服务实例 + * @return 实例探测结果 + * @throws PolarisException 异常信息 + */ + DetectResult detectInstance(Instance instance) throws PolarisException; +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/impl/PluginManager.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/impl/PluginManager.java new file mode 100644 index 000000000..9bf0a5fbf --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/impl/PluginManager.java @@ -0,0 +1,144 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.impl; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.IdAwarePlugin; +import com.tencent.polaris.api.plugin.Manager; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.StringUtils; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; + + +/** + * 插件管理器,承载当前API的插件实例列表 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class PluginManager implements Manager { + + private final Map> typedPlugins = new HashMap<>(); + + private final List pluginTypes; + + public PluginManager(List pluginTypes) { + Collections.sort(pluginTypes); + this.pluginTypes = pluginTypes; + } + + @Override + public void initPlugins(InitContext context) throws PolarisException { + //先实例化所有插件 + int baseId = 0; + for (PluginType pluginType : pluginTypes) { + Map plugins = new HashMap<>(); + typedPlugins.put(pluginType, plugins); + ServiceLoader loader = ServiceLoader.load(pluginType.getClazz()); + for (Plugin plugin : loader) { + baseId++; + String name = plugin.getName(); + if (StringUtils.isBlank(name) || plugins.containsKey(name)) { + throw new PolarisException(ErrorCode.PLUGIN_ERROR, + String.format("duplicated name for plugin(name=%s, type=%s)", name, pluginType)); + } + if (plugin instanceof IdAwarePlugin) { + ((IdAwarePlugin) plugin).setId(baseId); + } + plugins.put(name, plugin); + } + } + //再进行初始化 + for (PluginType pluginType : pluginTypes) { + Map plugins = typedPlugins.get(pluginType); + for (Map.Entry pluginEntry : plugins.entrySet()) { + pluginEntry.getValue().init(context); + } + } + } + + /** + * 在应用上下文初始化完毕后进行调用 + * + * @param extensions 插件实例 + * @throws PolarisException 运行异常 + */ + public void postContextInitPlugins(Extensions extensions) throws PolarisException { + for (PluginType pluginType : pluginTypes) { + Map plugins = typedPlugins.get(pluginType); + if (MapUtils.isEmpty(plugins)) { + continue; + } + for (Map.Entry pluginEntry : plugins.entrySet()) { + Plugin plugin = pluginEntry.getValue(); + plugin.postContextInit(extensions); + } + } + } + + /** + * 销毁已初始化的插件列表 + */ + @Override + public void destroyPlugins() { + //倒序遍历 + for (int i = pluginTypes.size() - 1; i >= 0; i--) { + PluginType pluginType = pluginTypes.get(i); + Map plugins = typedPlugins.get(pluginType); + if (MapUtils.isEmpty(plugins)) { + continue; + } + for (Map.Entry plugin : plugins.entrySet()) { + plugin.getValue().destroy(); + } + } + } + + + @Override + public Plugin getPlugin(PluginType type, String name) throws PolarisException { + if (!typedPlugins.containsKey(type)) { + throw new PolarisException(ErrorCode.PLUGIN_ERROR, String.format("plugins type(type=%s) not found", type)); + } + Map plugins = typedPlugins.get(type); + if (!plugins.containsKey(name)) { + throw new PolarisException(ErrorCode.PLUGIN_ERROR, + String.format("plugin(name=%s, type=%s) not found", name, type)); + } + return plugins.get(name); + } + + @Override + public Collection getPlugins(PluginType type) throws PolarisException { + if (!typedPlugins.containsKey(type)) { + throw new PolarisException(ErrorCode.PLUGIN_ERROR, String.format("plugins type(type=%s) not found", type)); + } + Map plugins = typedPlugins.get(type); + return plugins.values(); + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/loadbalance/LoadBalancer.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/loadbalance/LoadBalancer.java new file mode 100644 index 000000000..fe2980f65 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/loadbalance/LoadBalancer.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.loadbalance; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.Criteria; + +/** + * 【扩展点接口】负载均衡 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface LoadBalancer extends Plugin { + + /** + * 进行负载均衡,选择一个实例 + * + * @param criteria 负载均衡的关键因子 + * @param instances 服务实例列表 + * @return 单个服务实例 + * @throws PolarisException SDK被销毁则抛出异常 + */ + Instance chooseInstance(Criteria criteria, ServiceInstances instances) throws PolarisException; +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/AmountInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/AmountInfo.java new file mode 100644 index 000000000..3d839bc87 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/AmountInfo.java @@ -0,0 +1,31 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +public class AmountInfo { + + private long maxAmount; + + public long getMaxAmount() { + return maxAmount; + } + + public void setMaxAmount(long maxAmount) { + this.maxAmount = maxAmount; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/InitCriteria.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/InitCriteria.java new file mode 100644 index 000000000..56dd88bf2 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/InitCriteria.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +import com.tencent.polaris.client.pb.RateLimitProto.Rule; + +public class InitCriteria { + + private String windowKey; + + private Rule rule; + + public void setRule(Rule rule) { + this.rule = rule; + } + + public Rule getRule() { + return rule; + } + + public String getWindowKey() { + return windowKey; + } + + public void setWindowKey(String windowKey) { + this.windowKey = windowKey; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/LocalQuotaInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/LocalQuotaInfo.java new file mode 100644 index 000000000..cc75e84c8 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/LocalQuotaInfo.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +public class LocalQuotaInfo { + + private final long quotaUsed; + + private final long quotaLimited; + + public LocalQuotaInfo(long quotaUsed, long quotaLimited) { + this.quotaUsed = quotaUsed; + this.quotaLimited = quotaLimited; + } + + public long getQuotaUsed() { + return quotaUsed; + } + + public long getQuotaLimited() { + return quotaLimited; + } + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaBucket.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaBucket.java new file mode 100644 index 000000000..6d6a072bd --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaBucket.java @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +import com.tencent.polaris.api.exception.PolarisException; +import java.util.Map; + +/** + * bucket的令牌桶实现 + */ +public interface QuotaBucket { + + /** + * 在令牌桶/漏桶中进行单个配额的划扣,并返回本次分配的结果 + * + * @param curTimeMs 当前时间点 + * @param count 需获取的配额数 + * @return 分配结果 + * @throws PolarisException 异常信息 + */ + QuotaResult allocateQuota(long curTimeMs, int count) throws PolarisException; + + /** + * 释放配额(仅对于并发数限流有用) + */ + void release(); + + /** + * 远程配额更新 + * + * @param remoteQuotaInfo 远程同步结果 + */ + void onRemoteUpdate(RemoteQuotaInfo remoteQuotaInfo); + + /** + * 拉取本地使用配额情况以供上报 + * + * @param curTimeMs 当前时间点 + * @return localQuotaInfo,key为validDuration,单位为秒 + */ + Map fetchLocalUsage(long curTimeMs); + + /** + * 获取规则的限流阈值信息 + * + * @return 限流阈值信息,key为validDuration,单位为秒 + */ + Map getAmountInfo(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaResult.java new file mode 100644 index 000000000..926cd9376 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/QuotaResult.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making CL5 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +public class QuotaResult { + + public enum Code { + /** + * OK,代表请求可以通过 + */ + QuotaResultOk, + /** + * Limited,代表本次请求被限流 + */ + QuotaResultLimited + } + + private final Code code; + + private final long waitMs; + + private final String info; + + public QuotaResult(Code code, long waitMs, String info) { + this.code = code; + this.waitMs = waitMs; + this.info = info; + } + + public Code getCode() { + return code; + } + + public long getWaitMs() { + return waitMs; + } + + public String getInfo() { + return info; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/RemoteQuotaInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/RemoteQuotaInfo.java new file mode 100644 index 000000000..c8e8df927 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/RemoteQuotaInfo.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +public class RemoteQuotaInfo { + + private final long remoteQuotaLeft; + + private final int clientCount; + + private final long curTimeMs; + + private final long durationMs; + + public RemoteQuotaInfo(long remoteQuotaLeft, int clientCount, long curTimeMs, long durationMs) { + this.remoteQuotaLeft = remoteQuotaLeft; + this.clientCount = clientCount; + this.curTimeMs = curTimeMs; + this.durationMs = durationMs; + } + + /** + * 远程剩余配额 + * + * @return 剩余配额 + */ + public long getRemoteQuotaLeft() { + return remoteQuotaLeft; + } + + /** + * 共享相同bucket的实例数 + * + * @return 实例数 + */ + public int getClientCount() { + return clientCount; + } + + /** + * 配额所属的时间点(客户端本地时间) + * + * @return clientTimeStamp + */ + public long getCurTimeMs() { + return curTimeMs; + } + + /** + * 限流时间段 + * + * @return delay + */ + public long getDurationMs() { + return durationMs; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/ServiceRateLimiter.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/ServiceRateLimiter.java new file mode 100644 index 000000000..2938a1e4e --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/ratelimiter/ServiceRateLimiter.java @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.api.plugin.ratelimiter; + +import com.tencent.polaris.api.plugin.Plugin; + +public interface ServiceRateLimiter extends Plugin { + + String LIMITER_REJECT = "reject"; + + /** + * 初始化并创建令牌桶/漏桶, 主流程会在首次调用,以及规则对象变更的时候,调用该方法 + * + * @param criteria 参数对象 + * @return 限流令牌桶 + */ + QuotaBucket initQuota(InitCriteria criteria); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/AbstractResourceEventListener.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/AbstractResourceEventListener.java new file mode 100644 index 000000000..cec69cd21 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/AbstractResourceEventListener.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; + +public abstract class AbstractResourceEventListener implements ResourceEventListener { + + @Override + public void onResourceAdd(ServiceEventKey svcEventKey, RegistryCacheValue newValue) { + + } + + @Override + public void onResourceUpdated(ServiceEventKey svcEventKey, RegistryCacheValue oldValue, + RegistryCacheValue newValue) { + + } + + @Override + public void onResourceDeleted(ServiceEventKey svcEventKey, RegistryCacheValue oldValue) { + + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java new file mode 100644 index 000000000..38e816c25 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/CacheHandler.java @@ -0,0 +1,74 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; + +/** + * 缓存处理操作对象,为不同的类型的缓存提供处理能力 + */ +public interface CacheHandler { + + enum CachedStatus { + /** + * cache不存在 + */ + CacheNotExists, + /** + * cache发生变更 + */ + CacheChanged, + /** + * cache未发生变更 + */ + CacheNotChanged, + /** + * cache是空的,但是server没有返回data + */ + CacheEmptyButNoData + } + + /** + * 获取缓存类型 + * + * @return 缓存类型 + */ + EventType getTargetEventType(); + + /** + * 比较缓存值是否发生变更 + * + * @param oldValue 旧值 + * @param newValue 新值 + * @return 状态 + */ + CachedStatus compareMessage(RegistryCacheValue oldValue, Object newValue); + + /** + * 将服务端原始消息转换为缓存对象 + * + * @param oldValue 旧值 + * @param newValue 新原始消息 + * @param isCacheLoaded 是否从本地缓存加载 + * @return 新缓存值 + */ + RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded); + +} + diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/EventCompleteNotifier.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/EventCompleteNotifier.java new file mode 100644 index 000000000..3680bcd08 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/EventCompleteNotifier.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 事件通知回调 + */ +public interface EventCompleteNotifier { + + /** + * 任务正常完成时的回调函数 + * + * @param svcEventKey 服务信息 + */ + void complete(ServiceEventKey svcEventKey); + + /** + * 任务异常完成时候的回调函数 + * + * @param svcEventKey 服务信息 + * @param throwable 异常数据 + */ + void completeExceptionally(ServiceEventKey svcEventKey, Throwable throwable); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/InstanceProperty.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/InstanceProperty.java new file mode 100644 index 000000000..c6c4b6235 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/InstanceProperty.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + + +import com.tencent.polaris.api.pojo.Instance; +import java.util.Map; + +/** + * 服务实例更新属性列表 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class InstanceProperty { + + /** + * 属性标签,标识熔断状态 + */ + public static final String PROPERTY_CIRCUIT_BREAKER_STATUS = "circuitBreakerStatus"; + public static final String PROPERTY_OUTLIER_DETECTOR_STATUS = "OutlierDetectorStatus"; + /** + * 属性标签,标识故障探测结果 + */ + public static final String PROPERTY_DETECT_RESULT = "detectResult"; + + /** + * 动态权重更新索引 + */ + public static final String DYNAMIC_WEIGHT_KEY = "dynamicWeight"; + + private final Instance instance; + + private final Map properties; + + public InstanceProperty(Instance instance, Map properties) { + this.instance = instance; + this.properties = properties; + } + + public Instance getInstance() { + return instance; + } + + public Map getProperties() { + return properties; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "InstanceProperty{" + + "instance=" + instance + + ", properties=" + properties + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/LocalRegistry.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/LocalRegistry.java new file mode 100644 index 000000000..2e6b2feec --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/LocalRegistry.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.server.ServerConnector; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import java.util.Set; + +/** + * 【扩展点接口】本地缓存扩展点 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface LocalRegistry extends Plugin { + + /** + * 获取服务列表 + * + * @return Set + */ + Set getServices(); + + /** + * 获取服务规则 + * + * @param filter 规则参数 + * @return ServiceRule + */ + ServiceRule getServiceRule(ResourceFilter filter); + + /** + * 加载服务规则信息 + * + * @param svcEventKey 服务信息 + * @param notifier 获取后的回调通知 + * @throws PolarisException 异常信息 + */ + void loadServiceRule(ServiceEventKey svcEventKey, EventCompleteNotifier notifier) throws PolarisException; + + /** + * 获取实例列表 + * + * @param filter 实例获取参数 + * @return 实例列表 + */ + ServiceInstances getInstances(ResourceFilter filter); + + /** + * 非阻塞向{@link ServerConnector}发起一次缓存远程加载操作 + * 如果已经加载过了,那就直接进行notify + * 否则,加载完毕后调用notify函数 + * + * @param svcEventKey 服务标识 + * @param notifier represent an async request. + * @throws PolarisException SDK被销毁则抛出异常 + */ + void loadInstances(ServiceEventKey svcEventKey, EventCompleteNotifier notifier) throws PolarisException; + + /** + * 批量更新服务实例状态,properties存放的是状态值,当前支持2个key + * 1. ReadyToServe: 故障熔断标识,true or false + * 2. DynamicWeight:动态权重值 + * + * @param request 服务实例批量更新请求 + */ + void updateInstances(ServiceUpdateRequest request); + + /** + * 注册资源事件监听器 + * + * @param listener 监听器 + */ + void registerResourceListener(ResourceEventListener listener); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceEventListener.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceEventListener.java new file mode 100644 index 000000000..26f9636f1 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceEventListener.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 资源事件监听器 + */ +public interface ResourceEventListener { + + void onResourceAdd(ServiceEventKey svcEventKey, RegistryCacheValue newValue); + + void onResourceUpdated(ServiceEventKey svcEventKey, RegistryCacheValue oldValue, RegistryCacheValue newValue); + + void onResourceDeleted(ServiceEventKey svcEventKey, RegistryCacheValue oldValue); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceFilter.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceFilter.java new file mode 100644 index 000000000..21cd38c9e --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ResourceFilter.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 实例过滤器,用于获取的时候进行过滤操作 + * + * @author andrewshan + * @date 2019/9/3 + */ +public class ResourceFilter { + + private final ServiceEventKey svcEventKey; + + //是否内部触发的请求 + private final boolean internalRequest; + + //使用缓存 + private final boolean includeCache; + + public ResourceFilter(ServiceEventKey svcEventKey, boolean internalRequest, boolean includeCache) { + this.svcEventKey = svcEventKey; + this.internalRequest = internalRequest; + this.includeCache = includeCache; + } + + public ServiceEventKey getSvcEventKey() { + return svcEventKey; + } + + public boolean isInternalRequest() { + return internalRequest; + } + + public boolean isIncludeCache() { + return includeCache; + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/RuleFilter.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/RuleFilter.java new file mode 100644 index 000000000..300834e1d --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/RuleFilter.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 路由规则过滤器,用于获取路由规则的时候进行过滤操作 + * + * @author andrewshan + * @date 2019/9/3 + */ +public class RuleFilter { + + private final ServiceEventKey svcEventKey; + + private final boolean internalRequest; + + public RuleFilter(ServiceEventKey svcEventKey, boolean internalRequest) { + this.svcEventKey = svcEventKey; + this.internalRequest = internalRequest; + } + + public ServiceEventKey getSvcEventKey() { + return svcEventKey; + } + + public boolean isInternalRequest() { + return internalRequest; + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ServiceUpdateRequest.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ServiceUpdateRequest.java new file mode 100644 index 000000000..e1c1fd348 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/registry/ServiceUpdateRequest.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.registry; + + +import com.tencent.polaris.api.pojo.ServiceKey; +import java.util.Collection; + +/** + * 服务实例批量更新请求 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class ServiceUpdateRequest { + + private final ServiceKey serviceKey; + + private final Collection properties; + + public ServiceUpdateRequest(ServiceKey serviceKey, Collection instances) { + this.serviceKey = serviceKey; + this.properties = instances; + } + + public ServiceKey getServiceKey() { + return serviceKey; + } + + public Collection getProperties() { + return properties; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceUpdateRequest{" + + "serviceKey=" + serviceKey + + ", properties=" + properties + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java new file mode 100644 index 000000000..cb34cc173 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/LocationLevel.java @@ -0,0 +1,8 @@ +package com.tencent.polaris.api.plugin.route; + +/** + * 地域类型 + */ +public enum LocationLevel { + campus, zone, region, all, +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java new file mode 100644 index 000000000..79c0fee98 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteInfo.java @@ -0,0 +1,200 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.api.plugin.route; + +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.StatusDimension.Level; +import com.tencent.polaris.api.rpc.MetadataFailoverType; +import com.tencent.polaris.api.utils.StringUtils; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * 服务路由信息 + * + * @author andrewshan + * @date 2019/9/23 + */ +public class RouteInfo { + + //源服务信息 + private final ServiceMetadata sourceService; + //目的服务信息 + private final ServiceMetadata destService; + + //源路由规则 + private ServiceRule sourceRouteRule; + //目的路由规则 + private ServiceRule destRouteRule; + + private String canary; + + private final Map statusDimensions; + + //开启哪些路由 + private final Map chainEnable = new HashMap<>(); + // 是否包含不健康的服务实例,默认false + private boolean includeUnhealthyInstances; + // 是否包含被熔断的服务实例,默认false + private boolean includeCircuitBreakInstances; + //元数据路由降级类型 + private MetadataFailoverType metadataFailoverType; + + /** + * 下一步的路由信息 + */ + private RouteResult.NextRouterInfo nextRouterInfo; + + /** + * 构造器 + * + * @param sourceService 源服务 + * @param sourceRouteRule 源规则 + * @param destService 目标服务 + * @param destRouteRule 目标规则 + * @param method 接口名 + */ + public RouteInfo(ServiceMetadata sourceService, ServiceRule sourceRouteRule, + ServiceMetadata destService, ServiceRule destRouteRule, String method) { + this.sourceService = sourceService; + this.sourceRouteRule = sourceRouteRule; + this.destService = destService; + this.destRouteRule = destRouteRule; + Map dimensionMap = new HashMap<>(); + dimensionMap.put(Level.SERVICE, StatusDimension.EMPTY_DIMENSION); + if (StringUtils.isNotBlank(method)) { + dimensionMap.put(Level.ALL_CALLER, new StatusDimension(method, null)); + } + if (null != sourceService) { + dimensionMap.put(Level.ALL_METHOD, new StatusDimension("", sourceService)); + } + if (StringUtils.isNotBlank(method) && null != sourceService) { + dimensionMap.put(Level.CALLER_METHOD, new StatusDimension(method, sourceService)); + } + this.statusDimensions = Collections.unmodifiableMap(dimensionMap); + } + + /** + * 构造器 + * + * @param sourceService 源服务 + * @param destService 目标服务 + * @param method 接口名 + */ + public RouteInfo(ServiceMetadata sourceService, ServiceMetadata destService, String method) { + this(sourceService, null, destService, null, method); + } + + public MetadataFailoverType getMetadataFailoverType() { + return metadataFailoverType; + } + + public void setMetadataFailoverType(MetadataFailoverType metadataFailoverType) { + this.metadataFailoverType = metadataFailoverType; + } + + public Map getStatusDimensions() { + return statusDimensions; + } + + public void setSourceRouteRule(ServiceRule sourceRouteRule) { + this.sourceRouteRule = sourceRouteRule; + } + + public void setDestRouteRule(ServiceRule destRouteRule) { + this.destRouteRule = destRouteRule; + } + + public RouteResult.NextRouterInfo getNextRouterInfo() { + return nextRouterInfo; + } + + public void setNextRouterInfo(RouteResult.NextRouterInfo nextRouterInfo) { + this.nextRouterInfo = nextRouterInfo; + } + + public String getCanary() { + return canary; + } + + public void setCanary(String canary) { + this.canary = canary; + } + + /** + * 判断某个路由是否开启 + * + * @param routerType 路由类型 + * @return 是否开启 + */ + public Boolean routerIsEnabled(String routerType) { + return chainEnable.get(routerType); + } + + /** + * 开启某个路由 + * + * @param routerType 路由类型 + */ + public void enableRouter(String routerType) { + chainEnable.put(routerType, true); + } + + /** + * 关闭某个路由 + * + * @param routerType routerType + */ + public void disableRouter(String routerType) { + chainEnable.put(routerType, false); + } + + public ServiceMetadata getSourceService() { + return sourceService; + } + + public ServiceRule getSourceRouteRule() { + return sourceRouteRule; + } + + public ServiceMetadata getDestService() { + return destService; + } + + public ServiceRule getDestRouteRule() { + return destRouteRule; + } + + public boolean isIncludeUnhealthyInstances() { + return includeUnhealthyInstances; + } + + public void setIncludeUnhealthyInstances(boolean includeUnhealthyInstances) { + this.includeUnhealthyInstances = includeUnhealthyInstances; + } + + public boolean isIncludeCircuitBreakInstances() { + return includeCircuitBreakInstances; + } + + public void setIncludeCircuitBreakInstances(boolean includeCircuitBreakInstances) { + this.includeCircuitBreakInstances = includeCircuitBreakInstances; + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java new file mode 100644 index 000000000..31bd59570 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouteResult.java @@ -0,0 +1,114 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.route; + +import com.tencent.polaris.api.pojo.Instance; +import java.util.List; + +/** + * 路由结果 + * + * @author andrewshan + * @date 2019/9/23 + */ +public class RouteResult { + + private final List instances; + + private final NextRouterInfo nextRouterInfo; + + private final int hashCode; + + public RouteResult(List instances, State state) { + this.instances = instances; + nextRouterInfo = new NextRouterInfo(state); + this.hashCode = 0; + } + + public RouteResult(List instances, + NextRouterInfo nextRouterInfo, int hashCode) { + this.instances = instances; + this.nextRouterInfo = nextRouterInfo; + this.hashCode = hashCode; + } + + public int getHashCode() { + return hashCode; + } + + public List getInstances() { + return instances; + } + + public NextRouterInfo getNextRouterInfo() { + return nextRouterInfo; + } + + /** + * 路由链的下一步状态 + */ + public enum State { + /** + * 走下一个插件 + */ + Next, + /** + * 重试 + */ + Retry + } + + /** + * 下一步链路的信息 + */ + public static class NextRouterInfo { + + private final State state; + + private LocationLevel locationLevel; + + /** + * 最小的存在实例的级别 + */ + private LocationLevel minAvailableLevel; + + public NextRouterInfo(State state) { + this.state = state; + } + + public State getState() { + return state; + } + + public LocationLevel getLocationLevel() { + return locationLevel; + } + + public void setLocationLevel(LocationLevel locationLevel) { + this.locationLevel = locationLevel; + } + + public LocationLevel getMinAvailableLevel() { + return minAvailableLevel; + } + + public void setMinAvailableLevel(LocationLevel minAvailableLevel) { + this.minAvailableLevel = minAvailableLevel; + } + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java new file mode 100644 index 000000000..3cae87789 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/RouterConstants.java @@ -0,0 +1,12 @@ +package com.tencent.polaris.api.plugin.route; + +public interface RouterConstants { + + //set路由 + String SET_ENABLE_KEY = "internal-enable-set"; + String SET_NAME_KEY = "internal-set-name"; + String SET_ENABLED = "Y"; + + //金丝雀路由 + String CANARY_KEY = "canary"; +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/ServiceRouter.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/ServiceRouter.java new file mode 100644 index 000000000..06195c541 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/route/ServiceRouter.java @@ -0,0 +1,61 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.route; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; + +/** + * 【扩展点接口】服务路由 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface ServiceRouter extends Plugin { + + enum Aspect { + BEFORE, MIDDLE, AFTER + } + + /** + * 获取运行切面 + * + * @return 运行切面 + */ + Aspect getAspect(); + + /** + * 是否启动该路由插件 + * + * @param routeInfo 路由参数 + * @param dstSvcInfo 被调服务 + * @return 是否启用 + */ + boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo); + + /** + * 获取通过规则过滤后的服务集群信息以及服务实例列表 + * + * @param routeInfo 路由请求信息 + * @param instances 服务实例列表 + * @return 经过过滤后的服务实例列表 + */ + RouteResult getFilteredInstances(RouteInfo routeInfo, ServiceInstances instances) throws PolarisException; +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderRequest.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderRequest.java new file mode 100644 index 000000000..8d31de29a --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderRequest.java @@ -0,0 +1,162 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.server; + +import java.util.Map; + +public class CommonProviderRequest { + + private String instanceID; + + private String namespace; + + private String service; + + private String token; + + private String host; + + private Integer port; + + private String version; + + private String protocol; + + private Integer weight; + + private Integer priority; + + private Map metadata; + + private Integer ttl; + + public String getInstanceID() { + return instanceID; + } + + public void setInstanceID(String instanceID) { + this.instanceID = instanceID; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public Integer getWeight() { + return weight; + } + + public void setWeight(Integer weight) { + this.weight = weight; + } + + public Integer getPriority() { + return priority; + } + + public void setPriority(Integer priority) { + this.priority = priority; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + + public Integer getTtl() { + return ttl; + } + + public void setTtl(Integer ttl) { + this.ttl = ttl; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "CommonProviderRequest{" + + "instanceID='" + instanceID + '\'' + + ", namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + ", token='" + token + '\'' + + ", host='" + host + '\'' + + ", port=" + port + + ", version='" + version + '\'' + + ", protocol='" + protocol + '\'' + + ", weight=" + weight + + ", priority=" + priority + + ", metadata=" + metadata + + ", ttl=" + ttl + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderResponse.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderResponse.java new file mode 100644 index 000000000..cb58d55bf --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/CommonProviderResponse.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.server; + +public class CommonProviderResponse { + + private String instanceID; + + private boolean exists; + + public String getInstanceID() { + return instanceID; + } + + public void setInstanceID(String instanceID) { + this.instanceID = instanceID; + } + + public boolean isExists() { + return exists; + } + + public void setExists(boolean exists) { + this.exists = exists; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/EventHandler.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/EventHandler.java new file mode 100644 index 000000000..7388e2e2b --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/EventHandler.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.server; + +/** + * 事件回调函数 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface EventHandler { + + /** + * 服务时间变更后的回调逻辑 + * + * @param event 服务变更事件 + * @return 是否产生服务删除事件 + */ + boolean onEventUpdate(ServerEvent event); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientRequest.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientRequest.java new file mode 100644 index 000000000..bd842d736 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientRequest.java @@ -0,0 +1,59 @@ +package com.tencent.polaris.api.plugin.server; + +/** + * 客户端上报请求 + * + * @author vickliu + * @date 2019/9/22 + */ +public class ReportClientRequest { + + private String namespace; + + private String service; + + private String clientHost; + + private String version; + + public String getClientHost() { + return clientHost; + } + + public void setClientHost(String clientHost) { + this.clientHost = clientHost; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ReportClientRequest{" + + "clientHost='" + clientHost + '\'' + + ", version='" + version + '\'' + + "}" + super.toString(); + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientResponse.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientResponse.java new file mode 100644 index 000000000..7442e3a45 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ReportClientResponse.java @@ -0,0 +1,73 @@ +package com.tencent.polaris.api.plugin.server; + +/** + * 客户端上报应答 + * + * @author vickliu + * @date 2019/9/22 + */ +public class ReportClientResponse { + + enum RunMode { + ModeNoAgent, //ModeNoAgent 以no agent模式运行————SDK + ModeWithAgent //ModeWithAgent 带agent模式运行 + } + + private RunMode mode; + private String version; + private String region; + private String zone; + private String campus; + + public RunMode getMode() { + return mode; + } + + public void setMode(RunMode mode) { + this.mode = mode; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public String getZone() { + return zone; + } + + public void setZone(String zone) { + this.zone = zone; + } + + public String getCampus() { + return campus; + } + + public void setCampus(String campus) { + this.campus = campus; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ReportClientResponse{" + + "mode=" + mode + + ", version='" + version + '\'' + + ", region='" + region + '\'' + + ", zone='" + zone + '\'' + + ", campus='" + campus + '\'' + + "}" + super.toString(); + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerConnector.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerConnector.java new file mode 100644 index 000000000..c1b897aa6 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerConnector.java @@ -0,0 +1,89 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.server; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 【扩展点接口】services server代理,封装了server对接的逻辑 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface ServerConnector extends Plugin { + + /** + * 注册服务监听器 + * + * @param handler 服务事件处理器 + * @throws PolarisException SDK被释放后,再进行注册会抛异常 + */ + void registerServiceHandler(ServiceEventHandler handler) throws PolarisException; + + /** + * 反注册服务监听器 + * + * @param eventKey 服务标识 + * @throws PolarisException SDK被释放后,再进行注册会抛异常 + */ + void deRegisterServiceHandler(ServiceEventKey eventKey) throws PolarisException; + + /** + * 同步注册服务 + * + * @param req 注册请求 + * @return 注册应答 + * @throws PolarisException 注册过程出现的错误 + */ + CommonProviderResponse registerInstance(CommonProviderRequest req) throws PolarisException; + + /** + * 同步反注册服务 + * + * @param req 反注册请求 + * @throws PolarisException 反注册过程出现的错误 + */ + void deregisterInstance(CommonProviderRequest req) throws PolarisException; + + /** + * 同步心跳请求 + * + * @param req 心跳请求 + * @throws PolarisException 心跳上报过程出现的错误 + */ + void heartbeat(CommonProviderRequest req) throws PolarisException; + + /** + * 上报客户端信息 + * + * @param req 上报客户端信息 + * @return 上报应答 + * @throws PolarisException PolarisException + */ + ReportClientResponse reportClient(ReportClientRequest req) throws PolarisException; + + /** + * 更新服务端地址 + * + * @param svcEventKey 新的资源key + * @throws PolarisException 异常场景:当地址列表为空,或者地址全部连接失败,则返回error,调用者需进行重试 + */ + void updateServers(ServiceEventKey svcEventKey); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerEvent.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerEvent.java new file mode 100644 index 000000000..47f772d15 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServerEvent.java @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.server; + + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 服务变更事件 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class ServerEvent { + + /** + * 获取服务标识 + */ + private final ServiceEventKey serviceEventKey; + + /** + * 获取泛型的值 + */ + private final Object value; + + /** + * 获取错误信息,只有当出错的时候才返回 + */ + private final PolarisException error; + + public ServerEvent(ServiceEventKey serviceEventKey, Object value, PolarisException error) { + this.serviceEventKey = serviceEventKey; + this.value = value; + this.error = error; + } + + public ServiceEventKey getServiceEventKey() { + return serviceEventKey; + } + + public Object getValue() { + return value; + } + + public PolarisException getError() { + return error; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServiceEventHandler.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServiceEventHandler.java new file mode 100644 index 000000000..72b05a0bd --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/server/ServiceEventHandler.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.server; + +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.pojo.ServiceEventKey; + +/** + * 服务监听器 + * + * @author andrewshan + * @date 2019/8/21 + */ +public class ServiceEventHandler { + + /** + * 服务的唯一KEY + */ + private final ServiceEventKey serviceEventKey; + + /** + * 集群类型 + */ + private ClusterType targetCluster; + + /** + * 服务定期刷新时间 + */ + private long refreshInterval; + + /** + * 事件回调函数 + */ + private final EventHandler eventHandler; + + /** + * 保存上一次的更新时间 + */ + private long lastUpdateTimeMs; + + public ServiceEventHandler(ServiceEventKey serviceEventKey, EventHandler eventHandler) { + this.serviceEventKey = serviceEventKey; + this.eventHandler = eventHandler; + } + + public ClusterType getTargetCluster() { + return targetCluster; + } + + public void setTargetCluster(ClusterType targetCluster) { + this.targetCluster = targetCluster; + } + + public void setRefreshInterval(long refreshInterval) { + this.refreshInterval = refreshInterval; + } + + public ServiceEventKey getServiceEventKey() { + return serviceEventKey; + } + + public long getRefreshIntervalMs() { + return refreshInterval; + } + + public EventHandler getEventHandler() { + return eventHandler; + } + + public long getLastUpdateTimeMs() { + return lastUpdateTimeMs; + } + + public void setLastUpdateTimeMs(long lastUpdateTimeMs) { + this.lastUpdateTimeMs = lastUpdateTimeMs; + } + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/CircuitBreakGauge.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/CircuitBreakGauge.java new file mode 100644 index 000000000..2e3a07565 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/CircuitBreakGauge.java @@ -0,0 +1,79 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.api.plugin.stat; + +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.Service; + +public interface CircuitBreakGauge extends Service { + + /** + * 获取方法名 + * + * @return method + */ + String getMethod(); + + /** + * 获取实例分组 + * + * @return subsets + */ + String getSubset(); + + /** + * 获取节点信息 + * + * @return host + */ + String getHost(); + + /** + * 获取端口信息 + * + * @return port + */ + int getPort(); + + /** + * 获取服务实例ID + * + * @return String + */ + String getInstanceId(); + + /** + * 回写实例ID + * + * @param instanceId 实例ID + */ + void setInstanceId(String instanceId); + + /** + * 获取主调服务信息 + * + * @return Service + */ + Service getCallerService(); + + /** + * 获取熔断状态 + * + * @return 熔断状态 + */ + CircuitBreakerStatus getCircuitBreakStatus(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultCircuitBreakResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultCircuitBreakResult.java new file mode 100644 index 000000000..34af21ce3 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultCircuitBreakResult.java @@ -0,0 +1,98 @@ +package com.tencent.polaris.api.plugin.stat; + +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.Service; + +public class DefaultCircuitBreakResult implements CircuitBreakGauge { + private String service; + private String namespace; + private String method; + private String subset; + private String host; + private int port; + private String instanceId; + private Service callerService; + private CircuitBreakerStatus circuitBreakStatus; + + public void setService(String service) { + this.service = service; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public void setCircuitBreakStatus(CircuitBreakerStatus circuitBreakStatus) { + this.circuitBreakStatus = circuitBreakStatus; + } + + public void setCallerService(Service callerService) { + this.callerService = callerService; + } + + public void setMethod(String method) { + this.method = method; + } + + public void setSubset(String subset) { + this.subset = subset; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + @Override + public String getMethod() { + return method; + } + + @Override + public String getSubset() { + return subset; + } + + @Override + public String getHost() { + return host; + } + + @Override + public int getPort() { + return port; + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + @Override + public Service getCallerService() { + return callerService; + } + + @Override + public CircuitBreakerStatus getCircuitBreakStatus() { + return circuitBreakStatus; + } + + @Override + public String getService() { + return service; + } + + @Override + public String getNamespace() { + return namespace; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultRateLimitResult.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultRateLimitResult.java new file mode 100644 index 000000000..cdf5950a4 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/DefaultRateLimitResult.java @@ -0,0 +1,54 @@ +package com.tencent.polaris.api.plugin.stat; + +public class DefaultRateLimitResult implements RateLimitGauge { + private String method; + private String labels; + private Result result; + private String service; + private String namespace; + + @Override + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + @Override + public String getLabels() { + return labels; + } + + public void setLabels(String labels) { + this.labels = labels; + } + + @Override + public Result getResult() { + return result; + } + + public void setResult(Result result) { + this.result = result; + } + + @Override + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + @Override + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/RateLimitGauge.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/RateLimitGauge.java new file mode 100644 index 000000000..7eb75cb90 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/RateLimitGauge.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.stat; + +import com.tencent.polaris.api.pojo.Service; + +public interface RateLimitGauge extends Service { + + enum Result { + PASSED, LIMITED + } + + /** + * 获取方法名 + * + * @return method + */ + String getMethod(); + + /** + * 获取请求标签 + * + * @return labels + */ + String getLabels(); + + /** + * 获取限流结果 + * + * @return 是否通过 + */ + Result getResult(); +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatInfo.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatInfo.java new file mode 100644 index 000000000..84b4c9228 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatInfo.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.stat; + +import com.tencent.polaris.api.pojo.InstanceGauge; + +public class StatInfo { + + private InstanceGauge routerGauge; + + private RateLimitGauge rateLimitGauge; + + private CircuitBreakGauge circuitBreakGauge; + + public InstanceGauge getRouterGauge() { + return routerGauge; + } + + public void setRouterGauge(InstanceGauge routerGauge) { + this.routerGauge = routerGauge; + } + + public RateLimitGauge getRateLimitGauge() { + return rateLimitGauge; + } + + public void setRateLimitGauge(RateLimitGauge rateLimitGauge) { + this.rateLimitGauge = rateLimitGauge; + } + + public CircuitBreakGauge getCircuitBreakGauge() { + return circuitBreakGauge; + } + + public void setCircuitBreakGauge(CircuitBreakGauge circuitBreakGauge) { + this.circuitBreakGauge = circuitBreakGauge; + } +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatReporter.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatReporter.java new file mode 100644 index 000000000..6d675d371 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/stat/StatReporter.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.stat; + +import com.tencent.polaris.api.plugin.Plugin; + +/** + * 【扩展点接口】上报统计结果 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface StatReporter extends Plugin { + + /** + * 上报服务调用结果 + * + * @param statInfo 单次调用统计数据 + */ + void reportStat(StatInfo statInfo); + + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightAdjuster.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightAdjuster.java new file mode 100644 index 000000000..8fe62d1f8 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightAdjuster.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.weight; + +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.InstanceWeight; +import com.tencent.polaris.api.pojo.ServiceInstances; +import java.util.List; + +/** + * 【扩展点接口】动态权重调整接口 + * + * @author andrewshan + * @date 2019/8/21 + */ +public interface WeightAdjuster extends Plugin { + + /** + * Update dynamic weight. + * + * @param instances service's instances + * @return new dynamic weight for instances + */ + List timingAdjustDynamicWeight(ServiceInstances instances); + + /** + * 实时上报健康状态,并判断是否需要立刻进行动态权重调整,用于流量削峰. + * + * @param metric 实例调用信息 + * @return 是否需要立刻调整动态权重 + */ + boolean realTimeAdjustDynamicWeight(InstanceGauge metric); + +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightType.java b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightType.java new file mode 100644 index 000000000..f5b524e39 --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/java/com/tencent/polaris/api/plugin/weight/WeightType.java @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.api.plugin.weight; + +/** + * 权重类型枚举 + * + * @author andrewshan + * @date 2019/8/21 + */ +public enum WeightType { + /** + * 静态权重 + */ + STATIC, + + /** + * 动态权重 + */ + DYNAMIC +} diff --git a/polaris-plugins/polaris-plugin-api/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.TypeProvider b/polaris-plugins/polaris-plugin-api/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.TypeProvider new file mode 100644 index 000000000..22558533d --- /dev/null +++ b/polaris-plugins/polaris-plugin-api/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.TypeProvider @@ -0,0 +1 @@ +com.tencent.polaris.api.plugin.common.RpcTypeProviders \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/pom.xml new file mode 100644 index 000000000..17c153a93 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/pom.xml @@ -0,0 +1,23 @@ + + + + circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + circuitbreaker-common + jar + + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/AbstractStateMachine.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/AbstractStateMachine.java new file mode 100644 index 000000000..71c8fcbc0 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/AbstractStateMachine.java @@ -0,0 +1,178 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig.When; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.client.pojo.InstanceByProto; +import java.util.Collections; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 默认状态机实现,包括熔断后到半开到打开的逻辑,子类需要去实现具体到达熔断状态的逻辑 + * + * @author andrewshan + * @date 2019/8/26 + */ +public abstract class AbstractStateMachine implements StateMachine { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractStateMachine.class); + + protected final ConfigGroup configGroup; + + protected final int pluginId; + + protected final ConfigSetLocator configSetLocator; + + public AbstractStateMachine(ConfigGroup configGroup, int pluginId, ConfigSetLocator configSetLocator) { + this.configGroup = configGroup; + this.pluginId = pluginId; + this.configSetLocator = configSetLocator; + } + + @Override + public Set getStatusDimensions(Instance instance, Parameter parameter) { + HalfOpenCounter halfOpenCounter = getHalfOpenCounter(parameter.getPluginId(), instance); + if (null == halfOpenCounter) { + return Collections.emptySet(); + } + return halfOpenCounter.getStatusDimensions(); + } + + @Override + public boolean openToHalfOpen(Instance instance, StatusDimension statusDimension, Parameter parameter) { + String cbName = parameter.getCircuitBreakerName(); + CircuitBreakerStatus cbStatus = instance.getCircuitBreakerStatus(statusDimension); + if (null == cbStatus || !cbStatus.getCircuitBreaker().equals(cbName) + || cbStatus.getStatus() != CircuitBreakerStatus.Status.OPEN) { + return false; + } + boolean detectSuccess = false; + if (instance instanceof InstanceByProto) { + DetectResult detectResult = ((InstanceByProto)instance).getDetectResult(); + detectSuccess = null != detectResult && detectResult.getRetStatus() == RetStatus.RetSuccess; + } + //清空halfOpen的计数器 + HalfOpenCounter halfOpenCounter = getHalfOpenCounter(parameter.getPluginId(), instance); + if (null == halfOpenCounter) { + return false; + } + long startTimeMs = cbStatus.getStartTimeMs(); + HalfOpenConfig halfOpenConfig = getHalfOpenConfigOnHalfOpen(instance, statusDimension); + boolean matched; + if (detectSuccess && halfOpenConfig.getWhenToDetect() != When.never) { + matched = true; + } else { + matched = parameter.getCurrentTimeMs() - startTimeMs >= halfOpenConfig.getSleepWindowMs(); + } + if (matched) { + //时间窗已经过去, 则恢复半开 + halfOpenCounter.resetHalfOpen(statusDimension); + } + return matched; + } + + @Override + public boolean halfOpenToOpen(Instance instance, StatusDimension statusDimension, Parameter parameter) { + HalfOpenCounter halfOpenCounter = getHalfOpenCounterByParameter( + instance, statusDimension, parameter); + if (halfOpenCounter == null) { + return false; + } + //获取最近是否存在失败,存在足够错误则熔断器重新开启 + long failCountAfterHalfOpen = halfOpenCounter.getHalfOpenFailCount(statusDimension); + return failCountAfterHalfOpen >= getHalfOpenConfigOnHalfOpen(instance, statusDimension).getHalfOpenFailCount(); + } + + private HalfOpenCounter getHalfOpenCounterByParameter(Instance instance, StatusDimension statusDimension, + Parameter parameter) { + String cbName = parameter.getCircuitBreakerName(); + CircuitBreakerStatus cbStatus = instance.getCircuitBreakerStatus(statusDimension); + if (null == cbStatus || !cbStatus.getCircuitBreaker().equals(cbName) + || cbStatus.getStatus() != CircuitBreakerStatus.Status.HALF_OPEN) { + return null; + } + HalfOpenCounter halfOpenCounter = getHalfOpenCounter(parameter.getPluginId(), instance); + if (null == halfOpenCounter) { + return null; + } + return halfOpenCounter; + } + + @Override + public boolean halfOpenToClose(Instance instance, StatusDimension statusDimension, Parameter parameter) { + HalfOpenCounter halfOpenCounter = getHalfOpenCounterByParameter( + instance, statusDimension, parameter); + if (halfOpenCounter == null) { + return false; + } + //获取最近是否存在失败,存足够成功数则熔断器重新关闭 + HalfOpenConfig halfOpenConfig = getHalfOpenConfigOnHalfOpen(instance, statusDimension); + long sucCountAfterHalfOpen = halfOpenCounter.getHalfOpenSuccessCount(statusDimension); + boolean matched = sucCountAfterHalfOpen >= halfOpenConfig.getHalfOpenSuccessCount(); + if (matched) { + halfOpenCounter.resetCounter(statusDimension); + } + return matched; + } + + protected HalfOpenCounter getHalfOpenCounter(int pluginId, Instance instance) { + InstanceByProto instanceByProto = (InstanceByProto) instance; + InstanceLocalValue instanceLocalValue = instanceByProto.getInstanceLocalValue(); + if (null == instanceLocalValue) { + return null; + } + Object pluginValue = instanceLocalValue.getPluginValue(pluginId, null); + if (null == pluginValue) { + return null; + } + return (HalfOpenCounter) pluginValue; + } + + private HalfOpenConfig getHalfOpenConfigOnHalfOpen(Instance instance, StatusDimension statusDimension) { + RuleIdentifier ruleIdentifier = new RuleIdentifier(instance.getNamespace(), instance.getService(), + statusDimension.getCallerService(), statusDimension.getMethod()); + ConfigSet configSet = configSetLocator.getConfigSet(ruleIdentifier); + return configSet.getHalfOpenConfig(); + } + + protected HalfOpenCounter getHalfOpenCounterOnClose(Instance instance, StatusDimension statusDimension) { + CircuitBreakerStatus cbStatus = instance.getCircuitBreakerStatus(statusDimension); + if (null != cbStatus && cbStatus.getStatus() != CircuitBreakerStatus.Status.CLOSE) { + return null; + } + //统计错误率 + return getHalfOpenCounter(pluginId, instance); + } + + protected ConfigSet getConfigSetByLocator(Instance instance, + StatusDimension statusDimension, ConfigSetLocator configSetLocator) { + RuleIdentifier ruleIdentifier = new RuleIdentifier(instance.getNamespace(), instance.getService(), + statusDimension.getCallerService(), statusDimension.getMethod()); + return configSetLocator.getConfigSet(ruleIdentifier); + } + +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ChangeStateUtils.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ChangeStateUtils.java new file mode 100644 index 000000000..a02a1af6e --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ChangeStateUtils.java @@ -0,0 +1,155 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreakResult; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreakResult.ResultKey; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.pojo.InstanceByProto; +import com.tencent.polaris.client.pojo.Node; +import com.tencent.polaris.client.pojo.ServiceInstancesByProto; +import java.util.Collection; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 状态转换相关逻辑 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class ChangeStateUtils { + + private static final Logger LOG = LoggerFactory.getLogger(ChangeStateUtils.class); + + /** + * 构建熔断结果 + * + * @param stateMachine 熔断状态机 + * @param instances 实例集 + * @param parameter 熔断参数 + * @param 配置类型 + * @return 结果 + */ + public static CircuitBreakResult buildCircuitBreakResult( + StateMachine stateMachine, Collection instances, StateMachine.Parameter parameter) { + long curTimeMs = System.currentTimeMillis(); + CircuitBreakResult result = new CircuitBreakResult(curTimeMs, + parameter.getHalfOpenMaxReqCount()); + String cbName = parameter.getCircuitBreakerName(); + for (Instance instance : instances) { + Set statusDimensions = stateMachine.getStatusDimensions(instance, parameter); + if (CollectionUtils.isEmpty(statusDimensions)) { + continue; + } + String instanceId = instance.getId(); + for (StatusDimension statusDimension : statusDimensions) { + if (stateMachine.closeToOpen(instance, statusDimension, parameter)) { + result.getInstancesToOpen().put(new ResultKey(instanceId, statusDimension), instance); + LOG.info("circuitBreaker: instance {} and dimension {} changed from close to open by {}", + instanceId, statusDimension, cbName); + continue; + } + if (stateMachine.openToHalfOpen(instance, statusDimension, parameter)) { + result.getInstancesToHalfOpen().put(new ResultKey(instanceId, statusDimension), instance); + LOG.info("circuitBreaker: instance {} and dimension {} changed from open to halfOpen by {}", + instanceId, statusDimension, cbName); + continue; + } + if (stateMachine.halfOpenToOpen(instance, statusDimension, parameter)) { + result.getInstancesToOpen().put(new ResultKey(instanceId, statusDimension), instance); + LOG.info("circuitBreaker: instance {} and dimension {} changed from halfOpen to open by {}", + instanceId, statusDimension, cbName); + continue; + } + if (stateMachine.halfOpenToClose(instance, statusDimension, parameter)) { + result.getInstancesToClose().put(new ResultKey(instanceId, statusDimension), instance); + LOG.info("circuitBreaker: instance {} and dimension {} changed from halfOpen to close by {}", + instanceId, statusDimension, cbName); + } + } + } + return result; + } + + /** + * 获取实例ID + * + * @param instanceGauge 实例统计数据 + * @param localRegistry 本地缓存插件 + * @return ID + */ + public static InstanceByProto getInstance(InstanceGauge instanceGauge, LocalRegistry localRegistry) { + ServiceEventKey serviceEventKey = new ServiceEventKey( + new ServiceKey(instanceGauge.getNamespace(), instanceGauge.getService()), EventType.INSTANCE); + ResourceFilter resourceFilter = new ResourceFilter(serviceEventKey, true, true); + ServiceInstances instances = localRegistry.getInstances(resourceFilter); + if (!instances.isInitialized()) { + return null; + } + ServiceInstancesByProto serviceInstancesByProto = (ServiceInstancesByProto) instances; + Instance instance = instanceGauge.getInstance(); + if (instance instanceof InstanceByProto) { + return (InstanceByProto) instance; + } + InstanceByProto instanceByProto; + String instanceId = instanceGauge.getInstanceId(); + if (StringUtils.isNotBlank(instanceId)) { + instanceByProto = serviceInstancesByProto.getInstance(instanceId); + } else { + Node node = new Node(instanceGauge.getHost(), instanceGauge.getPort()); + instanceByProto = serviceInstancesByProto.getInstanceByNode(node); + } + if (null != instanceByProto) { + instanceGauge.setInstance(instanceByProto); + } + return instanceByProto; + } + + /** + * 构建状态维度 + * + * @param instanceGauge 统计数据 + * @param level 熔断级别 + * @return 维度 + */ + public static StatusDimension buildStatusDimension(InstanceGauge instanceGauge, StatusDimension.Level level) { + switch (level) { + case CALLER_METHOD: + return new StatusDimension(instanceGauge.getMethod(), instanceGauge.getCallerService()); + case ALL_CALLER: + return new StatusDimension(instanceGauge.getMethod(), null); + case ALL_METHOD: + return new StatusDimension("", instanceGauge.getCallerService()); + default: + return StatusDimension.EMPTY_DIMENSION; + } + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/CircuitBreakUtils.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/CircuitBreakUtils.java new file mode 100644 index 000000000..f312f19a0 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/CircuitBreakUtils.java @@ -0,0 +1,269 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus.Status; +import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.Service; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.RuleUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.FlowControlParam; +import com.tencent.polaris.client.flow.ResourcesResponse; +import com.tencent.polaris.client.pb.CircuitBreakerProto; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CbRule; +import com.tencent.polaris.client.pb.CircuitBreakerProto.DestinationSet; +import com.tencent.polaris.client.pb.CircuitBreakerProto.SourceMatcher; +import com.tencent.polaris.client.pb.ModelProto.MatchString; +import com.tencent.polaris.client.pb.ModelProto.MatchString.MatchStringType; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +public class CircuitBreakUtils { + + public static boolean instanceHalfOpen(Instance instance, StatusDimension statusDimension) { + CircuitBreakerStatus circuitBreakerStatus = instance.getCircuitBreakerStatus(statusDimension); + if (null == circuitBreakerStatus) { + return false; + } + return circuitBreakerStatus.getStatus() == Status.HALF_OPEN; + } + + public static boolean instanceClose(Instance instance, StatusDimension statusDimension) { + CircuitBreakerStatus circuitBreakerStatus = instance.getCircuitBreakerStatus(statusDimension); + return null == circuitBreakerStatus || circuitBreakerStatus.getStatus() == Status.CLOSE; + } + + private static CircuitBreakerProto.CircuitBreaker getCircuitBreakerRule(ServiceRule serviceRule) { + if (null == serviceRule) { + return null; + } + if (!serviceRule.isInitialized()) { + return null; + } + return (CircuitBreakerProto.CircuitBreaker) serviceRule.getRule(); + } + + private static List getRules(CircuitBreakerProto.CircuitBreaker dstRule, + CircuitBreakerProto.CircuitBreaker srcRule) { + if (null != dstRule && dstRule.getInboundsCount() > 0) { + return dstRule.getInboundsList(); + } + if (null != srcRule && srcRule.getOutboundsCount() > 0) { + return srcRule.getOutboundsList(); + } + return null; + } + + private static final String matchAll = "*"; + + + private static class MatchSourceResult { + + final boolean matched; + final boolean allSourcesMatched; + + public MatchSourceResult(boolean matched, boolean allSourcesMatched) { + this.matched = matched; + this.allSourcesMatched = allSourcesMatched; + } + } + + private static MatchSourceResult matchSource(CbRule rule, RuleIdentifier ruleIdentifier) { + if (rule.getSourcesCount() == 0) { + return new MatchSourceResult(true, true); + } + Service callerService = ruleIdentifier.getCallerService(); + for (SourceMatcher sourceMatcher : rule.getSourcesList()) { + boolean matchAllNamespace = sourceMatcher.getNamespace().getValue().equals(matchAll); + boolean matchAllService = sourceMatcher.getService().getValue().equals(matchAll); + if (matchAllNamespace && matchAllService) { + return new MatchSourceResult(true, true); + } + if (null == callerService) { + continue; + } + boolean namespaceMatch = matchAllNamespace; + boolean serviceMatch = matchAllService; + if (!namespaceMatch) { + namespaceMatch = sourceMatcher.getNamespace().getValue().equals(callerService.getNamespace()); + } + if (!serviceMatch) { + serviceMatch = sourceMatcher.getService().getValue().equals(callerService.getService()); + } + if (namespaceMatch && serviceMatch) { + return new MatchSourceResult(true, false); + } + } + return new MatchSourceResult(false, false); + } + + private static class MatchDestResult { + + final DestinationSet destinationSet; + final boolean allMethod; + + public MatchDestResult(DestinationSet destinationSet, boolean allMethod) { + this.destinationSet = destinationSet; + this.allMethod = allMethod; + } + } + + private static MatchDestResult matchDestination(CbRule rule, RuleIdentifier ruleIdentifier, FlowCache flowCache) { + if (rule.getDestinationsCount() == 0) { + return new MatchDestResult(null, false); + } + for (DestinationSet destinationSet : rule.getDestinationsList()) { + boolean namespaceMatch = destinationSet.getNamespace().getValue().equals(matchAll); + boolean serviceMatch = destinationSet.getService().getValue().equals(matchAll); + if (!namespaceMatch) { + namespaceMatch = destinationSet.getNamespace().getValue().equals(ruleIdentifier.getNamespace()); + } + if (!serviceMatch) { + serviceMatch = destinationSet.getService().getValue().equals(ruleIdentifier.getService()); + } + if (!namespaceMatch || !serviceMatch) { + continue; + } + MatchString methodMatcher = destinationSet.getMethod(); + if (null == methodMatcher) { + return new MatchDestResult(destinationSet, true); + } + if (RuleUtils.isMatchAllValue(methodMatcher)) { + return new MatchDestResult(destinationSet, true); + } + String method = ruleIdentifier.getMethod(); + if (methodMatcher.getType() == MatchStringType.EXACT) { + if (StringUtils.equals(methodMatcher.getValue().getValue(), method)) { + return new MatchDestResult(destinationSet, false); + } + } + Pattern pattern = flowCache.loadOrStoreCompiledRegex(methodMatcher.getValue().getValue()); + if (pattern.matcher(method).find()) { + return new MatchDestResult(destinationSet, false); + } + } + return new MatchDestResult(null, false); + } + + public static class RuleDestinationResult { + + private final DestinationSet destinationSet; + private final boolean matchAllSource; + private final boolean matchAllMethod; + + public RuleDestinationResult(DestinationSet destinationSet, boolean matchAllSource, boolean matchAllMethod) { + this.destinationSet = destinationSet; + this.matchAllSource = matchAllSource; + this.matchAllMethod = matchAllMethod; + } + + public DestinationSet getDestinationSet() { + return destinationSet; + } + + public StatusDimension.Level getMatchLevel() { + StatusDimension.Level level; + if (matchAllSource && matchAllMethod) { + level = StatusDimension.Level.SERVICE; + } else if (matchAllMethod) { + level = StatusDimension.Level.ALL_METHOD; + } else if (matchAllSource) { + level = StatusDimension.Level.ALL_CALLER; + } else { + level = StatusDimension.Level.CALLER_METHOD; + } + return level; + } + } + + /** + * 通过配置获取远程规则 + * + * @param ruleIdentifier 规则标识 + * @param extensions 插件集合 + * @param controlParam 控制参数 + * @return 过滤信息 + */ + public static RuleDestinationResult getRuleDestinationSet(RuleIdentifier ruleIdentifier, Extensions extensions, + FlowControlParam controlParam) { + Set svcEventKeys = new HashSet<>(); + ServiceEventKey dstSvcEventKey = new ServiceEventKey( + new ServiceKey(ruleIdentifier.getNamespace(), ruleIdentifier.getService()), + EventType.CIRCUIT_BREAKING); + svcEventKeys.add(dstSvcEventKey); + ServiceEventKey srcSvcEventKey = null; + Service callerService = ruleIdentifier.getCallerService(); + if (null != callerService) { + srcSvcEventKey = new ServiceEventKey( + new ServiceKey(callerService.getNamespace(), callerService.getService()), + EventType.CIRCUIT_BREAKING); + svcEventKeys.add(srcSvcEventKey); + } + DefaultServiceEventKeysProvider serviceEventKeysProvider = new DefaultServiceEventKeysProvider(); + serviceEventKeysProvider.setSvcEventKeys(svcEventKeys); + serviceEventKeysProvider.setUseCache(true); + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(extensions, false, serviceEventKeysProvider, controlParam); + CircuitBreakerProto.CircuitBreaker dstRule = getCircuitBreakerRule( + resourcesResponse.getServiceRule(dstSvcEventKey)); + CircuitBreakerProto.CircuitBreaker srcRule = null; + if (null != srcSvcEventKey) { + srcRule = getCircuitBreakerRule(resourcesResponse.getServiceRule(srcSvcEventKey)); + } + List rules = getRules(dstRule, srcRule); + if (CollectionUtils.isEmpty(rules)) { + return new RuleDestinationResult(null, false, false); + } + for (CbRule rule : rules) { + MatchSourceResult matchSourceResult = matchSource(rule, ruleIdentifier); + if (!matchSourceResult.matched) { + continue; + } + + MatchDestResult matchDestResult = matchDestination(rule, ruleIdentifier, extensions.getFlowCache()); + if (null == matchDestResult.destinationSet) { + continue; + } + return new RuleDestinationResult(matchDestResult.destinationSet, matchSourceResult.allSourcesMatched, + matchDestResult.allMethod); + } + return new RuleDestinationResult(null, false, false); + } + + public static ConfigSet getConfigSet(InstanceGauge gauge, ConfigSetLocator locator) { + RuleIdentifier ruleIdentifier = new RuleIdentifier(gauge.getNamespace(), gauge.getService(), + gauge.getCallerService(), gauge.getMethod()); + return locator.getConfigSet(ruleIdentifier); + } + +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigGroup.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigGroup.java new file mode 100644 index 000000000..e1dd2bef4 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigGroup.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.verify.Verifier; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class ConfigGroup { + + private final ConfigSet localConfig; + + private final Map> serviceConfigs = new ConcurrentHashMap<>(); + + public ConfigGroup(ConfigSet localConfig) { + this.localConfig = localConfig; + } + + public ConfigSet getLocalConfig() { + return localConfig; + } + + public ConfigSet getServiceConfig(RuleIdentifier ruleIdentifier, Function> create) { + ConfigSet tConfigSet = serviceConfigs.computeIfAbsent(ruleIdentifier, create); + if (tConfigSet.isUseDefault()) { + return localConfig; + } + return tConfigSet; + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSet.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSet.java new file mode 100644 index 000000000..b098e3615 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSet.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.pojo.StatusDimension; + +/** + * 配置集合 + */ +public class ConfigSet { + + private final StatusDimension.Level level; + + private final boolean useDefault; + + private final HalfOpenConfig halfOpenConfig; + + private final T plugConfig; + + public ConfigSet(StatusDimension.Level level, boolean useDefault, HalfOpenConfig halfOpenConfig, T plugConfig) { + this.level = level; + this.useDefault = useDefault; + this.halfOpenConfig = halfOpenConfig; + this.plugConfig = plugConfig; + } + + public StatusDimension.Level getLevel() { + return level; + } + + public boolean isUseDefault() { + return useDefault; + } + + public HalfOpenConfig getHalfOpenConfig() { + return halfOpenConfig; + } + + public T getPlugConfig() { + return plugConfig; + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSetLocator.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSetLocator.java new file mode 100644 index 000000000..4ef9a250d --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/ConfigSetLocator.java @@ -0,0 +1,32 @@ + +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.verify.Verifier; + +public interface ConfigSetLocator { + + /** + * 拉取配置信息 + * + * @param ruleIdentifier 配置唯一标识 + * @return 配置对象 + */ + ConfigSet getConfigSet(RuleIdentifier ruleIdentifier); +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenConfig.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenConfig.java new file mode 100644 index 000000000..2faf1b422 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenConfig.java @@ -0,0 +1,85 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.consumer.CircuitBreakerConfig; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig.When; +import com.tencent.polaris.client.pb.CircuitBreakerProto.RecoverConfig; + +public class HalfOpenConfig { + + private final int halfOpenMaxReqCount; + + private final int halfOpenSuccessCount; + + private final int halfOpenFailCount; + + private final long sleepWindowMs; + + private final OutlierDetectionConfig.When whenToDetect; + + public HalfOpenConfig(CircuitBreakerConfig circuitBreakerConfig, OutlierDetectionConfig outlierDetectionConfig) { + halfOpenMaxReqCount = circuitBreakerConfig.getRequestCountAfterHalfOpen(); + int successCountAfterHalfOpen = circuitBreakerConfig.getSuccessCountAfterHalfOpen(); + if (successCountAfterHalfOpen > halfOpenMaxReqCount) { + successCountAfterHalfOpen = halfOpenMaxReqCount; + } + int halfOpenFailCount = halfOpenMaxReqCount - successCountAfterHalfOpen + 1; + if (halfOpenFailCount > halfOpenMaxReqCount) { + halfOpenFailCount = halfOpenMaxReqCount; + } + this.sleepWindowMs = circuitBreakerConfig.getSleepWindow(); + this.halfOpenSuccessCount = successCountAfterHalfOpen; + this.halfOpenFailCount = halfOpenFailCount; + this.whenToDetect = outlierDetectionConfig.getWhen(); + } + + public HalfOpenConfig(HalfOpenConfig halfOpenConfig, RecoverConfig recoverConfig) { + this.halfOpenMaxReqCount = halfOpenConfig.getHalfOpenMaxReqCount(); + this.sleepWindowMs = halfOpenConfig.getSleepWindowMs(); + this.halfOpenSuccessCount = halfOpenConfig.getHalfOpenSuccessCount(); + this.halfOpenFailCount = halfOpenConfig.getHalfOpenFailCount(); + if (null != recoverConfig) { + this.whenToDetect = OutlierDetectionConfig.When.values()[recoverConfig.getOutlierDetectWhen().getNumber()]; + } else { + this.whenToDetect = halfOpenConfig.getWhenToDetect(); + } + } + + public int getHalfOpenMaxReqCount() { + return halfOpenMaxReqCount; + } + + public int getHalfOpenSuccessCount() { + return halfOpenSuccessCount; + } + + public int getHalfOpenFailCount() { + return halfOpenFailCount; + } + + public long getSleepWindowMs() { + return sleepWindowMs; + } + + public When getWhenToDetect() { + return whenToDetect; + } + +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenCounter.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenCounter.java new file mode 100644 index 000000000..6b42d9c56 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/HalfOpenCounter.java @@ -0,0 +1,94 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.StatusDimension; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +public abstract class HalfOpenCounter { + + private static class CounterGroup { + + final AtomicInteger successCounter = new AtomicInteger(0); + + final AtomicInteger failCounter = new AtomicInteger(0); + + void reset() { + successCounter.set(0); + failCounter.set(0); + } + + int getHalfOpenSuccessCount() { + return successCounter.get(); + } + + int getHalfOpenFailCount() { + return failCounter.get(); + } + } + + private final Map counterGroups = new ConcurrentHashMap<>(); + + private final Function create = new Function() { + @Override + public CounterGroup apply(StatusDimension statusDimension) { + return new CounterGroup(); + } + }; + + private CounterGroup getCounter(StatusDimension statusDimension) { + return counterGroups.computeIfAbsent(statusDimension, create); + } + + public abstract Set getStatusDimensions(); + + public void resetHalfOpen(StatusDimension statusDimension) { + CounterGroup counter = getCounter(statusDimension); + counter.reset(); + } + + public int getHalfOpenSuccessCount(StatusDimension statusDimension) { + CounterGroup counter = getCounter(statusDimension); + return counter.getHalfOpenSuccessCount(); + } + + public int getHalfOpenFailCount(StatusDimension statusDimension) { + CounterGroup counter = getCounter(statusDimension); + return counter.getHalfOpenFailCount(); + } + + public boolean triggerHalfOpenConversion(StatusDimension statusDimension, RetStatus retStatus, + HalfOpenConfig halfOpenConfig) { + CounterGroup counter = getCounter(statusDimension); + if (retStatus == RetStatus.RetFail || retStatus == RetStatus.RetTimeout) { + int failCount = counter.failCounter.incrementAndGet(); + return failCount == halfOpenConfig.getHalfOpenFailCount(); + } else if (retStatus == RetStatus.RetSuccess) { + int successCount = counter.successCounter.incrementAndGet(); + return successCount == halfOpenConfig.getHalfOpenSuccessCount(); + } + return false; + } + + public abstract void resetCounter(StatusDimension statusDimension); +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/RuleIdentifier.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/RuleIdentifier.java new file mode 100644 index 000000000..f8173eb2b --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/RuleIdentifier.java @@ -0,0 +1,88 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.pojo.Service; +import java.util.Objects; + +public class RuleIdentifier { + + private final String namespace; + + private final String service; + + private final Service callerService; + + private final String method; + + public RuleIdentifier(String namespace, String service, Service callerService, String method) { + this.namespace = namespace; + this.service = service; + this.callerService = callerService; + this.method = method; + } + + public String getNamespace() { + return namespace; + } + + public String getService() { + return service; + } + + public Service getCallerService() { + return callerService; + } + + public String getMethod() { + return method; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RuleIdentifier that = (RuleIdentifier) o; + return Objects.equals(namespace, that.namespace) && + Objects.equals(service, that.service) && + Objects.equals(callerService, that.callerService) && + Objects.equals(method, that.method); + } + + @Override + public int hashCode() { + return Objects.hash(namespace, service, callerService, method); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "RuleIdentifier{" + + "namespace=" + namespace + + "service=" + service + + ", callerService=" + callerService + + ", method='" + method + '\'' + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/StateMachine.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/StateMachine.java new file mode 100644 index 000000000..00a433d23 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/StateMachine.java @@ -0,0 +1,118 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common; + +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.StatusDimension; +import java.util.Set; + +/** + * 状态机通用接口(用于处理熔断状态的转换的计算) + * + * @author andrewshan + * @date 2019/8/26 + */ +public interface StateMachine { + + /** + * 获取所有的实例统计维度 + * + * @param instance 实例 + * @param parameter 参数 + * @return 统计维度列表 + */ + Set getStatusDimensions(Instance instance, Parameter parameter); + + /** + * 熔断器从关闭状态转换为开启状态 + * + * @param instance 节点实例信息 + * @param statusDimension 维度 + * @param parameter 状态机转换参数 + * @return 是否打开熔断器 + */ + boolean closeToOpen(Instance instance, StatusDimension statusDimension, Parameter parameter); + + /** + * 熔断器从打开状态转换为半开状态 + * + * @param instance 节点实例信息 + * @param statusDimension 维度 + * @param parameter 状态机转换参数 + * @return 是否半开熔断器 + */ + boolean openToHalfOpen(Instance instance, StatusDimension statusDimension, Parameter parameter); + + /** + * 熔断器从半开状态转换为打开状态 + * + * @param instance 节点实例信息 + * @param statusDimension 维度 + * @param parameter 状态机转换参数 + * @return 是否打开熔断器 + */ + boolean halfOpenToOpen(Instance instance, StatusDimension statusDimension, Parameter parameter); + + /** + * 熔断器从半开状态转换为关闭状态 + * + * @param instance 节点实例信息 + * @param statusDimension 维度 + * @param parameter 状态机转换参数 + * @return 是否关闭熔断器 + */ + boolean halfOpenToClose(Instance instance, StatusDimension statusDimension, Parameter parameter); + + /** + * 状态机转换所需要的参数类 + */ + class Parameter { + + private final int pluginId; + + private final String circuitBreakerName; + + private final long currentTimeMs; + + private final int halfOpenMaxReqCount; + + public Parameter(int pluginId, String circuitBreakerName, int halfOpenMaxReqCount) { + this.pluginId = pluginId; + this.circuitBreakerName = circuitBreakerName; + this.currentTimeMs = System.currentTimeMillis(); + this.halfOpenMaxReqCount = halfOpenMaxReqCount; + } + + public int getPluginId() { + return pluginId; + } + + public String getCircuitBreakerName() { + return circuitBreakerName; + } + + public long getCurrentTimeMs() { + return currentTimeMs; + } + + public int getHalfOpenMaxReqCount() { + return halfOpenMaxReqCount; + } + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/Bucket.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/Bucket.java new file mode 100644 index 000000000..39d98d816 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/Bucket.java @@ -0,0 +1,130 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common.stat; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +/** + * 单个滑桶具体实现 + * + * @author andrewshan + * @date 2019/8/25 + */ +public class Bucket { + + /** + * 统计序列集合,key为资源ID + */ + private final ResMetricArray metric; + + /** + * 滑桶的时间范围 + */ + private final TimeRange timeRange; + + /** + * 滑桶链总长,只有首节点需要 + */ + private final AtomicInteger count = new AtomicInteger(0); + + /** + * 下一个滑桶 + */ + private final AtomicReference nextBucket = new AtomicReference<>(); + + public Bucket(long start, long intervalMs, int metricSize) { + timeRange = new TimeRange(start, start + intervalMs); + metric = new ResMetricArray(metricSize); + } + + public TimeRange getTimeRange() { + return timeRange; + } + + public long getMetric(int dimension) { + return metric.getMetric(dimension); + } + + public long addMetric(int dimension, long value) { + return metric.addMetric(dimension, value); + } + + public void setMetric(int dimension, long value) { + metric.setMetric(dimension, value); + } + + public AtomicReference getNextBucket() { + return nextBucket; + } + + public AtomicInteger getCount() { + return count; + } + + /** + * 获取最末端的桶 + * + * @return Bucket + */ + public Bucket getTailBucket() { + Bucket head = this; + Bucket next = head.getNextBucket().get(); + while (null != next) { + head = next; + next = head.getNextBucket().get(); + } + return head; + } + + /** + * 计算维度下所有的时间段的值之和 + * 只要startTime或者endTime所存在的区间,或者包含在startTime-endTime之间的区间 + * 比如: 3-----6-----9-----12---15的区间, + * 对于4-10的时间范围,则返回3-----6-----9-----12 + * 对于2-7的时间范围,则返回3-----6-----9 + * + * @param dimension 维度下表 + * @param timeRange 时间段 + * @return long + */ + long calcMetricsBothIncluded(int dimension, TimeRange timeRange) { + long total = 0L; + List buckets = new ArrayList<>(); + Bucket next = this; + while (null != next) { + TimeRange bucketTimeRange = next.getTimeRange(); + TimePosition posStart = bucketTimeRange.isTimeInBucket(timeRange.getStart()); + TimePosition posEnd = bucketTimeRange.isTimeInBucket(timeRange.getEnd()); + TimePosition posBucketRangeStart = timeRange.isTimeInBucket(bucketTimeRange.getStart()); + TimePosition posBucketRangeEnd = timeRange.isTimeInBucket(bucketTimeRange.getEnd()); + if (posStart == TimePosition.inside || posEnd == TimePosition.inside) { + buckets.add(next); + } else if (posBucketRangeStart == TimePosition.inside && posBucketRangeEnd == TimePosition.inside) { + buckets.add(next); + } + next = next.getNextBucket().get(); + } + for (Bucket bucket : buckets) { + total += bucket.getMetric(dimension); + } + return total; + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/ResMetricArray.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/ResMetricArray.java new file mode 100644 index 000000000..16464e8fd --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/ResMetricArray.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common.stat; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * 资源统计序列,记录单个资源的统计项 + * 数组中每一个元素为一个统计项 + * + * @author andrewshan + * @date 2019/8/25 + */ +public class ResMetricArray { + + /** + * 统计序列,每一项都可以进行原子更新 + */ + private final AtomicLong[] metrics; + + public ResMetricArray(int metricSize) { + metrics = new AtomicLong[metricSize]; + for (int i = 0; i < metricSize; ++i) { + metrics[i] = new AtomicLong(0L); + } + } + + public long getMetric(int dimension) { + return metrics[dimension].get(); + } + + public long addMetric(int dimension, long value) { + return metrics[dimension].addAndGet(value); + } + + public void setMetric(int dimension, long value) { + metrics[dimension].set(value); + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/SliceWindow.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/SliceWindow.java new file mode 100644 index 000000000..2fbe5bd63 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/SliceWindow.java @@ -0,0 +1,196 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common.stat; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 时间滑窗的具体实现 + * + * @author andrewshan + * @date 2019/8/25 + */ +public class SliceWindow { + + private static final Logger LOG = LoggerFactory.getLogger(SliceWindow.class); + + private final String name; + + /** + * 预计滑桶总量 + */ + private final int bucketCount; + + /** + * 滑桶的长度 + */ + private final long bucketIntervalMs; + + /** + * 滑窗总时间 + */ + private final long windowLengthMs; + + /** + * 维度数量 + */ + private final int metricSize; + + /** + * 全局锁,控制滑桶的移出移入 + */ + private final Object lock = new Object(); + + /** + * 滑桶链表头 + */ + private final AtomicReference headBucket = new AtomicReference<>(); + + public SliceWindow(String name, int bucketCount, long bucketIntervalMs, int metricSize) { + this.name = name; + this.bucketCount = bucketCount; + this.bucketIntervalMs = bucketIntervalMs; + this.metricSize = metricSize; + windowLengthMs = bucketIntervalMs * bucketCount; + } + + public int getBucketCount() { + return bucketCount; + } + + public long getBucketIntervalMs() { + return bucketIntervalMs; + } + + public long getWindowLengthMs() { + return windowLengthMs; + } + + private long doAddGauge(Function operation, long now) { + //需要进行滑窗的变更,不是直接添加 + Bucket head = headBucket.get(); + Bucket tail = null; + if (null != head) { + tail = head.getTailBucket(); + } + if (null != tail && TimePosition.inside == tail.getTimeRange().isTimeInBucket(now)) { + Long retValue = operation.apply(tail); + LOG.debug("window {}: add tail bucket {}, now is {}, value is {}", name, tail.getTimeRange(), now, + retValue); + return null != retValue ? retValue : 0; + } + if (null == head || null == tail || tail.getTimeRange().getEnd() + windowLengthMs <= now) { + //首节点为空或者尾部节点已经过期,则新建一个窗口 + Bucket newBucket = new Bucket(now, bucketIntervalMs, metricSize); + newBucket.getCount().set(1); + headBucket.set(newBucket); + LOG.debug("window {}: recreated, new bucket {}, now is {}", name, newBucket.getTimeRange(), now); + Long value = operation.apply(newBucket); + return null != value ? value : 0L; + } + //计算下一个尾节点的起始和结束位置 + return addNextBucket(operation, head, tail, now); + } + + private long addNextBucket(Function operation, Bucket head, Bucket tail, long now) { + //计算下一个尾节点的起始和结束位置 + long lastTailEndTime = tail.getTimeRange().getEnd(); + long nextStartTime = lastTailEndTime + (now - lastTailEndTime) / bucketIntervalMs; + Bucket newBucket = new Bucket(nextStartTime, bucketIntervalMs, metricSize); + //添加链表 + tail.getNextBucket().set(newBucket); + //滑掉过期的节点 + slipExpireBucket(head, newBucket, now); + Long value = operation.apply(newBucket); + LOG.debug("window {}: tail add next, tail is {}, new bucket {}, now is {}, value is {}", name, + tail.getTimeRange(), newBucket.getTimeRange(), now, value); + return null != value ? value : 0L; + } + + private void slipExpireBucket(Bucket head, Bucket newBucket, long now) { + Bucket nextHead = head; + long headStartTime = nextHead.getTimeRange().getStart(); + long headWindowEndTime = headStartTime + windowLengthMs; + int slipCount = 0; + while (headWindowEndTime <= newBucket.getTimeRange().getStart()) { + nextHead = nextHead.getNextBucket().get(); + if (null == nextHead) { + break; + } + slipCount++; + headStartTime = head.getTimeRange().getStart(); + headWindowEndTime = headStartTime + windowLengthMs; + } + if (null == nextHead) { + newBucket.getCount().set(1); + headBucket.set(newBucket); + LOG.info("window {}: recreated, new bucket {}, now is {}", name, newBucket.getTimeRange(), now); + return; + } + //滑掉之前的窗 + if (slipCount > 0) { + nextHead.getCount().addAndGet(1 - slipCount); + headBucket.set(nextHead); + } + } + + public long addGauge(Function operation) { + long now = System.currentTimeMillis(); + Bucket tail = getTailBucket(); + if (null != tail && TimePosition.inside == tail.getTimeRange().isTimeInBucket(now)) { + Long retValue = operation.apply(tail); + LOG.debug("window {}: add tail bucket {}, now is {}, value is {}", name, tail.getTimeRange(), now, + retValue); + return null != retValue ? retValue : 0; + } + synchronized (lock) { + return doAddGauge(operation, now); + } + } + + public Bucket getHeadBucket() { + return headBucket.get(); + } + + public Bucket getTailBucket() { + Bucket head = headBucket.get(); + if (null != head) { + return head.getTailBucket(); + } + return null; + } + + /** + * 通过首节点计算维度统计信息 + * + * @param dimension 维度下表 + * @param timeRange 时间段 + * @return long + */ + public long calcMetricsBothIncluded(int dimension, TimeRange timeRange) { + Bucket head = getHeadBucket(); + if (null == head) { + return 0; + } + return head.calcMetricsBothIncluded(dimension, timeRange); + } + +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimePosition.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimePosition.java new file mode 100644 index 000000000..6769d2388 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimePosition.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common.stat; + +/** + * 时间位置枚举 + * + * @author andrewshan + * @date 2019/8/25 + */ +public enum TimePosition { + /** + * 在时间段之前 + */ + before, + /** + * 在时间段内 + */ + inside, + /** + * 在时间段后 + */ + after + +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimeRange.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimeRange.java new file mode 100644 index 000000000..89b7e20b7 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-common/src/main/java/com/tencent/polaris/plugins/circuitbreaker/common/stat/TimeRange.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.common.stat; + +/** + * 时间段 + * + * @author andrewshan + * @date 2019/8/25 + */ +public class TimeRange { + + private final long start; + + private final long end; + + public TimeRange(long start, long end) { + this.start = start; + this.end = end; + } + + /** + * 判断时间点是否在范围中 + * + * @param inputTime 输出时间 + * @return 时间点位置 + */ + public TimePosition isTimeInBucket(long inputTime) { + if (inputTime < start) { + return TimePosition.before; + } + if (inputTime >= start && inputTime < end) { + return TimePosition.inside; + } + return TimePosition.after; + } + + public long getStart() { + return start; + } + + public long getEnd() { + return end; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "TimeRange{" + + "start=" + start + + ", end=" + end + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/pom.xml new file mode 100644 index 000000000..e6fa7dc50 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/pom.xml @@ -0,0 +1,22 @@ + + + + circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + circuitbreaker-errcount + + + + com.tencent.nameservice + circuitbreaker-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/Config.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/Config.java new file mode 100644 index 000000000..e702e9d00 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/Config.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errcount; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 基于错误次数熔断插件配置结构 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class Config implements Verifier { + + + @JsonProperty + private Integer continuousErrorThreshold; + + public Integer getContinuousErrorThreshold() { + return continuousErrorThreshold; + } + + public void setContinuousErrorThreshold(Integer continuousErrorThreshold) { + this.continuousErrorThreshold = continuousErrorThreshold; + } + + @Override + public void verify() { + ConfigUtils.validatePositive(continuousErrorThreshold, "continuousErrorThreshold"); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + Config config = (Config) defaultObject; + if (null == continuousErrorThreshold) { + setContinuousErrorThreshold(config.getContinuousErrorThreshold()); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "Config{" + + "continuousErrorThreshold=" + continuousErrorThreshold + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCircuitBreaker.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCircuitBreaker.java new file mode 100644 index 000000000..939ff238b --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCircuitBreaker.java @@ -0,0 +1,223 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errcount; + +import com.tencent.polaris.api.config.consumer.CircuitBreakerConfig; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig; +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import com.tencent.polaris.api.config.plugin.PluginConfigProvider; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreakResult; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.Subset; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.plugins.circuitbreaker.common.ChangeStateUtils; +import com.tencent.polaris.plugins.circuitbreaker.common.CircuitBreakUtils; +import com.tencent.polaris.plugins.circuitbreaker.common.CircuitBreakUtils.RuleDestinationResult; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigGroup; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSet; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSetLocator; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenConfig; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenCounter; +import com.tencent.polaris.plugins.circuitbreaker.common.RuleIdentifier; +import com.tencent.polaris.plugins.circuitbreaker.common.StateMachine; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.FlowControlParam; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CbPolicy; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CbPolicy.ConsecutiveErrConfig; +import com.tencent.polaris.client.pb.CircuitBreakerProto.DestinationSet; +import com.tencent.polaris.client.pb.CircuitBreakerProto.RecoverConfig; +import com.tencent.polaris.client.pojo.InstanceByProto; +import java.util.Collection; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 基于错误统计次数的熔断器 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class ConsecutiveCircuitBreaker extends Destroyable implements CircuitBreaker, PluginConfigProvider, + ConfigSetLocator { + + private static final Logger LOG = LoggerFactory.getLogger(ConsecutiveCircuitBreaker.class); + + private int id; + + private StateMachine stateMachine; + + private final Function create = integer -> new ConsecutiveCounter(); + + private Extensions extensions; + + private FlowControlParam flowControlParam; + + /** + * 服务缓存 + */ + private LocalRegistry localRegistry; + + private ConfigGroup configGroup; + + @Override + public CircuitBreakResult checkInstance(Collection instances) { + if (CollectionUtils.isEmpty(instances)) { + return null; + } + StateMachine.Parameter parameter = new StateMachine.Parameter(id, getName(), + configGroup.getLocalConfig().getHalfOpenConfig().getHalfOpenMaxReqCount()); + return ChangeStateUtils.buildCircuitBreakResult(stateMachine, instances, parameter); + } + + @Override + public CircuitBreakResult checkSubset(Collection subsets) { + //TODO: + return null; + } + + + @Override + public boolean stat(InstanceGauge gauge) { + InstanceByProto instance = ChangeStateUtils.getInstance(gauge, localRegistry); + if (null == instance) { + return false; + } + InstanceLocalValue instanceLocalValue = instance.getInstanceLocalValue(); + if (null == instanceLocalValue) { + return false; + } + ConfigSet configSet = CircuitBreakUtils.getConfigSet(gauge, this); + StatusDimension statusDimension = ChangeStateUtils.buildStatusDimension(gauge, configSet.getLevel()); + if (CircuitBreakUtils.instanceClose(instance, statusDimension)) { + Object pluginValue = instanceLocalValue.getPluginValue(id, create); + ConsecutiveCounter consecutiveCounter = (ConsecutiveCounter) pluginValue; + RetStatus retStatus = gauge.getRetStatus(); + int failCount; + if (retStatus == RetStatus.RetFail) { + failCount = consecutiveCounter.onFail(statusDimension); + } else { + consecutiveCounter.resetCounter(statusDimension); + failCount = 0; + } + return failCount == configSet.getPlugConfig().getContinuousErrorThreshold(); + } else if (CircuitBreakUtils.instanceHalfOpen(instance, statusDimension)) { + //半开计数器 + Object pluginValue = instanceLocalValue.getPluginValue(id, create); + HalfOpenCounter consecutiveCounter = (HalfOpenCounter) pluginValue; + RetStatus retStatus = gauge.getRetStatus(); + return consecutiveCounter + .triggerHalfOpenConversion(statusDimension, retStatus, configSet.getHalfOpenConfig()); + } + return false; + } + + + @Override + public String getName() { + return DefaultPlugins.CIRCUIT_BREAKER_ERROR_COUNT; + } + + @Override + public Class getPluginConfigClazz() { + return Config.class; + } + + @Override + public PluginType getType() { + return PluginTypes.CIRCUIT_BREAKER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + CircuitBreakerConfig circuitBreakerConfig = ctx.getConfig().getConsumer().getCircuitBreaker(); + OutlierDetectionConfig outlierDetection = ctx.getConfig().getConsumer().getOutlierDetection(); + HalfOpenConfig halfOpenConfig = new HalfOpenConfig(circuitBreakerConfig, outlierDetection); + Config cfg = circuitBreakerConfig.getPluginConfig(getName(), Config.class); + if (cfg == null) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("plugin %s config is missing", getName())); + } + ConfigSet configSet = new ConfigSet<>(StatusDimension.Level.SERVICE, false, halfOpenConfig, cfg); + configGroup = new ConfigGroup<>(configSet); + stateMachine = new StateMachineImpl(configGroup, id, this); + flowControlParam = new DefaultFlowControlParam(ctx.getConfig().getGlobal().getAPI()); + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + this.extensions = extensions; + localRegistry = extensions.getLocalRegistry(); + } + + @Override + public int getId() { + return id; + } + + @Override + public void setId(int id) { + this.id = id; + } + + @Override + public ConfigSet getConfigSet(RuleIdentifier ruleIdentifier) { + return configGroup.getServiceConfig(ruleIdentifier, new Function>() { + @Override + public ConfigSet apply(RuleIdentifier ruleIdentifier) { + RuleDestinationResult ruleDestResultConsecutive = CircuitBreakUtils + .getRuleDestinationSet(ruleIdentifier, extensions, flowControlParam); + DestinationSet ruleDestinationSetConsecutive = ruleDestResultConsecutive.getDestinationSet(); + if (null == ruleDestinationSetConsecutive) { + return new ConfigSet<>(StatusDimension.Level.SERVICE, true, null, null); + } + HalfOpenConfig halfOpenConfigConsecutive = configGroup.getLocalConfig().getHalfOpenConfig(); + RecoverConfig recoverConfigConsecutive = ruleDestinationSetConsecutive.getRecover(); + if (null != recoverConfigConsecutive) { + halfOpenConfigConsecutive = new HalfOpenConfig(halfOpenConfigConsecutive, recoverConfigConsecutive); + } + Config targetPlugConfig = configGroup.getLocalConfig().getPlugConfig(); + CbPolicy policy = ruleDestinationSetConsecutive.getPolicy(); + if (null != policy) { + ConsecutiveErrConfig consecutive = policy.getConsecutive(); + if (null != consecutive && consecutive.hasEnable() && consecutive.getEnable().getValue()) { + targetPlugConfig = new Config(); + targetPlugConfig + .setContinuousErrorThreshold(consecutive.getConsecutiveErrorToOpen().getValue()); + } + } + return new ConfigSet<>(ruleDestResultConsecutive.getMatchLevel(), false, halfOpenConfigConsecutive, + targetPlugConfig); + } + }); + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCounter.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCounter.java new file mode 100644 index 000000000..1717c6f47 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/ConsecutiveCounter.java @@ -0,0 +1,63 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errcount; + +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenCounter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +public class ConsecutiveCounter extends HalfOpenCounter { + + private final Map consecutiveErrors = new HashMap<>(); + + private final Function create = new Function() { + @Override + public AtomicInteger apply(StatusDimension statusDimension) { + return new AtomicInteger(0); + } + }; + + private AtomicInteger getCounter(StatusDimension statusDimension) { + return consecutiveErrors.computeIfAbsent(statusDimension, create); + } + + @Override + public Set getStatusDimensions() { + return consecutiveErrors.keySet(); + } + + public int onFail(StatusDimension statusDimension) { + AtomicInteger counter = getCounter(statusDimension); + return counter.incrementAndGet(); + } + + public int getConsecutiveErrorCount(StatusDimension statusDimension) { + AtomicInteger counter = getCounter(statusDimension); + return counter.get(); + } + + @Override + public void resetCounter(StatusDimension statusDimension) { + AtomicInteger counter = getCounter(statusDimension); + counter.set(0); + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/StateMachineImpl.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/StateMachineImpl.java new file mode 100644 index 000000000..4eab4fe3b --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errcount/StateMachineImpl.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errcount; + +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.plugins.circuitbreaker.common.AbstractStateMachine; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigGroup; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSet; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSetLocator; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenCounter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 基于连续错误熔断的状态机切换逻辑 + * + * @author andrewshan + * @date 2019/8/27 + */ +public class StateMachineImpl extends AbstractStateMachine { + + private static final Logger LOG = LoggerFactory.getLogger(StateMachineImpl.class); + + public StateMachineImpl(ConfigGroup configGroup, int pluginId, ConfigSetLocator configSetLocator) { + super(configGroup, pluginId, configSetLocator); + } + + @Override + public boolean closeToOpen(Instance instance, StatusDimension statusDimension, Parameter parameter) { + HalfOpenCounter halfOpenCounter = getHalfOpenCounterOnClose( + instance, statusDimension); + if (halfOpenCounter == null) { + return false; + } + ConfigSet configSet = getConfigSetByLocator(instance, statusDimension, configSetLocator); + return ((ConsecutiveCounter) halfOpenCounter).getConsecutiveErrorCount(statusDimension) >= configSet + .getPlugConfig().getContinuousErrorThreshold(); + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider new file mode 100644 index 000000000..8b934f36b --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.plugins.circuitbreaker.errcount.ConsecutiveCircuitBreaker \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker new file mode 100644 index 000000000..8b934f36b --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errcount/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker @@ -0,0 +1 @@ +com.tencent.polaris.plugins.circuitbreaker.errcount.ConsecutiveCircuitBreaker \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/pom.xml new file mode 100644 index 000000000..7be5197c1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/pom.xml @@ -0,0 +1,22 @@ + + + + circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + circuitbreaker-errrate + + + + com.tencent.nameservice + circuitbreaker-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Config.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Config.java new file mode 100644 index 000000000..adfa2582a --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Config.java @@ -0,0 +1,131 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errrate; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CbPolicy.ErrRateConfig; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 错误率熔断插件的特定配置 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class Config implements Verifier { + + @JsonProperty + private Integer requestVolumeThreshold; + + @JsonProperty + private Integer errorRateThreshold; + + @JsonProperty + private Integer metricNumBuckets; + + @JsonIgnore + private double errRate; + + public Config() { + + } + + public Config(Config config, ErrRateConfig errRateConfig) { + setRequestVolumeThreshold(config.getRequestVolumeThreshold()); + setMetricNumBuckets(config.getMetricNumBuckets()); + setErrorRateThreshold(config.getErrorRateThreshold()); + if (null != errRateConfig) { + setErrorRateThreshold(errRateConfig.getErrorRateToOpen().getValue()); + } + } + + public Integer getRequestVolumeThreshold() { + return requestVolumeThreshold; + } + + public void setRequestVolumeThreshold(Integer requestVolumeThreshold) { + this.requestVolumeThreshold = requestVolumeThreshold; + } + + public Integer getErrorRateThreshold() { + return errorRateThreshold; + } + + public double getErrRate() { + return errRate; + } + + public void setErrorRateThreshold(Integer errorRateThreshold) { + this.errorRateThreshold = errorRateThreshold; + if (null == this.errorRateThreshold) { + return; + } + double errorRate = (double) this.errorRateThreshold / (double) 100; + if (errorRate > 1.0) { + errorRate = 1.0; + } + this.errRate = errorRate; + } + + public Integer getMetricNumBuckets() { + return metricNumBuckets; + } + + public void setMetricNumBuckets(Integer metricNumBuckets) { + this.metricNumBuckets = metricNumBuckets; + } + + @Override + public void verify() { + ConfigUtils.validatePositive(requestVolumeThreshold, "requestVolumeThreshold"); + ConfigUtils.validatePositive(errorRateThreshold, "errorRateThreshold"); + ConfigUtils.validatePositive(metricNumBuckets, "metricNumBuckets"); + if (errorRateThreshold > 100) { + throw new IllegalArgumentException("errorRateThreshold should be less than or equals to 100"); + } + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + Config config = (Config) defaultObject; + if (null == requestVolumeThreshold) { + setRequestVolumeThreshold(config.getRequestVolumeThreshold()); + } + if (null == errorRateThreshold) { + setErrorRateThreshold(config.getErrorRateThreshold()); + } + if (null == metricNumBuckets) { + setMetricNumBuckets(config.getMetricNumBuckets()); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "Config{" + + "requestVolumeThreshold=" + requestVolumeThreshold + + ", errorRateThreshold=" + errorRateThreshold + + ", metricNumBuckets=" + metricNumBuckets + + '}'; + } +} + diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Dimension.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Dimension.java new file mode 100644 index 000000000..5ab7aca07 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/Dimension.java @@ -0,0 +1,40 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errrate; + +/** + * 统计维度 + * + * @author andrewshan + * @date 2019/8/26 + */ +public enum Dimension { + + /** + * 总请求数 + */ + keyRequestCount, + /** + * 错误数 + */ + keyFailCount, + /** + * 总统计维度 + */ + maxDimension, +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCircuitBreaker.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCircuitBreaker.java new file mode 100644 index 000000000..6cbae4c2f --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCircuitBreaker.java @@ -0,0 +1,231 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errrate; + +import com.tencent.polaris.api.config.consumer.CircuitBreakerConfig; +import com.tencent.polaris.api.config.consumer.OutlierDetectionConfig; +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import com.tencent.polaris.api.config.plugin.PluginConfigProvider; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreakResult; +import com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.Subset; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.FlowControlParam; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CbPolicy; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CbPolicy.ErrRateConfig; +import com.tencent.polaris.client.pb.CircuitBreakerProto.DestinationSet; +import com.tencent.polaris.client.pb.CircuitBreakerProto.RecoverConfig; +import com.tencent.polaris.client.pojo.InstanceByProto; +import com.tencent.polaris.plugins.circuitbreaker.common.ChangeStateUtils; +import com.tencent.polaris.plugins.circuitbreaker.common.CircuitBreakUtils; +import com.tencent.polaris.plugins.circuitbreaker.common.CircuitBreakUtils.RuleDestinationResult; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigGroup; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSet; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSetLocator; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenConfig; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenCounter; +import com.tencent.polaris.plugins.circuitbreaker.common.RuleIdentifier; +import com.tencent.polaris.plugins.circuitbreaker.common.StateMachine; +import com.tencent.polaris.plugins.circuitbreaker.common.stat.SliceWindow; +import java.util.Collection; +import java.util.function.Function; + +/** + * 基于错误率的熔断器 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class ErrRateCircuitBreaker extends Destroyable implements CircuitBreaker, PluginConfigProvider, + ConfigSetLocator { + + private int id; + + private ConfigGroup configGroup; + + private long metricWindowMs; + + private Extensions extensions; + + private FlowControlParam flowControlParam; + + private StateMachine stateMachine; + + private LocalRegistry localRegistry; + + private Function create; + + private final String metricWindowName = String.format("%s_%s", getName(), "metric"); + + @Override + public void init(InitContext ctx) throws PolarisException { + CircuitBreakerConfig circuitBreakerConfig = ctx.getConfig().getConsumer().getCircuitBreaker(); + OutlierDetectionConfig outlierDetection = ctx.getConfig().getConsumer().getOutlierDetection(); + metricWindowMs = circuitBreakerConfig.getCheckPeriod(); + HalfOpenConfig halfOpenConfig = new HalfOpenConfig(circuitBreakerConfig, outlierDetection); + Config cfg = circuitBreakerConfig.getPluginConfig(getName(), Config.class); + if (cfg == null) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("plugin %s config is missing", getName())); + } + ConfigSet configSet = new ConfigSet<>(StatusDimension.Level.SERVICE, false, halfOpenConfig, cfg); + create = new Function() { + @Override + public Object apply(Integer integer) { + return new ErrRateCounter(metricWindowName, configSet.getPlugConfig(), getBucketIntervalMs()); + } + }; + configGroup = new ConfigGroup<>(configSet); + stateMachine = new StateMachineImpl(configGroup, id, this, metricWindowMs); + flowControlParam = new DefaultFlowControlParam(ctx.getConfig().getGlobal().getAPI()); + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + this.extensions = extensions; + localRegistry = extensions.getLocalRegistry(); + } + + @Override + public boolean stat(InstanceGauge gauge) { + InstanceByProto instance = ChangeStateUtils.getInstance(gauge, localRegistry); + if (null == instance) { + return false; + } + InstanceLocalValue instanceLocalValue = instance.getInstanceLocalValue(); + if (null == instanceLocalValue) { + return false; + } + ConfigSet configSet = CircuitBreakUtils.getConfigSet(gauge, this); + StatusDimension statusDimension = ChangeStateUtils.buildStatusDimension(gauge, configSet.getLevel()); + if (CircuitBreakUtils.instanceClose(instance, statusDimension)) { + Object pluginValue = instanceLocalValue.getPluginValue(id, create); + ErrRateCounter errRateCounter = (ErrRateCounter) pluginValue; + SliceWindow metricWindow = errRateCounter.getSliceWindow(statusDimension); + metricWindow.addGauge((bucket -> { + bucket.addMetric(Dimension.keyRequestCount.ordinal(), 1); + if (gauge.getRetStatus() == RetStatus.RetFail) { + return bucket + .addMetric(Dimension.keyFailCount.ordinal(), 1); + } + return bucket.getMetric(Dimension.keyFailCount.ordinal()); + })); + } else if (CircuitBreakUtils.instanceHalfOpen(instance, statusDimension)) { + //半开计数器 + Object pluginValue = instanceLocalValue.getPluginValue(id, create); + HalfOpenCounter consecutiveCounter = (HalfOpenCounter) pluginValue; + RetStatus retStatus = gauge.getRetStatus(); + return consecutiveCounter + .triggerHalfOpenConversion(statusDimension, retStatus, configSet.getHalfOpenConfig()); + } + return false; + } + + public long getBucketIntervalMs() { + double bucketIntervalMs = + (double) metricWindowMs / (double) configGroup.getLocalConfig().getPlugConfig().getMetricNumBuckets(); + return (long) Math.ceil(bucketIntervalMs); + } + + @Override + public CircuitBreakResult checkInstance(Collection instances) { + if (CollectionUtils.isEmpty(instances)) { + return null; + } + StateMachine.Parameter parameter = new StateMachine.Parameter(id, getName(), + configGroup.getLocalConfig().getHalfOpenConfig().getHalfOpenMaxReqCount()); + return ChangeStateUtils.buildCircuitBreakResult(stateMachine, instances, parameter); + } + + @Override + public CircuitBreakResult checkSubset(Collection subsets) { + return null; + } + + @Override + public String getName() { + return DefaultPlugins.CIRCUIT_BREAKER_ERROR_RATE; + } + + @Override + public Class getPluginConfigClazz() { + return Config.class; + } + + @Override + public PluginType getType() { + return PluginTypes.CIRCUIT_BREAKER.getBaseType(); + } + + @Override + public int getId() { + return id; + } + + @Override + public void setId(int id) { + this.id = id; + } + + @Override + public ConfigSet getConfigSet(RuleIdentifier ruleIdentifier) { + return configGroup.getServiceConfig(ruleIdentifier, + new Function>() { + @Override + public ConfigSet apply(RuleIdentifier ruleIdentifier) { + RuleDestinationResult ruleDestResultErrRate = CircuitBreakUtils + .getRuleDestinationSet(ruleIdentifier, extensions, flowControlParam); + DestinationSet ruleDestinationSetErrRate = ruleDestResultErrRate.getDestinationSet(); + if (null == ruleDestinationSetErrRate) { + return new ConfigSet<>(StatusDimension.Level.SERVICE, true, null, null); + } + CbPolicy policy = ruleDestinationSetErrRate.getPolicy(); + HalfOpenConfig halfOpenConfigErrRate = configGroup.getLocalConfig().getHalfOpenConfig(); + RecoverConfig recoverConfigErrRate = ruleDestinationSetErrRate.getRecover(); + if (null != recoverConfigErrRate) { + halfOpenConfigErrRate = new HalfOpenConfig(halfOpenConfigErrRate, recoverConfigErrRate); + } + Config targetPlugConfig = configGroup.getLocalConfig().getPlugConfig(); + if (null != policy) { + ErrRateConfig errorRateConfig = policy.getErrorRate(); + if (null != errorRateConfig && errorRateConfig.hasEnable() && errorRateConfig.getEnable() + .getValue()) { + targetPlugConfig = new Config(targetPlugConfig, errorRateConfig); + } + } + return new ConfigSet<>(ruleDestResultErrRate.getMatchLevel(), false, halfOpenConfigErrRate, + targetPlugConfig); + } + }); + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCounter.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCounter.java new file mode 100644 index 000000000..d8afe96ce --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/ErrRateCounter.java @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errrate; + +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenCounter; +import com.tencent.polaris.plugins.circuitbreaker.common.stat.SliceWindow; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class ErrRateCounter extends HalfOpenCounter { + + private final Map sliceWindows = new ConcurrentHashMap<>(); + + private final long bucketIntervalMs; + + private final int metricNumBuckets; + + private final String name; + + private final Function create = new Function() { + @Override + public SliceWindow apply(StatusDimension statusDimension) { + return new SliceWindow(name, metricNumBuckets, bucketIntervalMs, Dimension.maxDimension.ordinal()); + } + }; + + public ErrRateCounter(String name, Config config, long bucketIntervalMs) { + this.name = name; + this.bucketIntervalMs = bucketIntervalMs; + this.metricNumBuckets = config.getMetricNumBuckets(); + } + + public SliceWindow getSliceWindow(StatusDimension statusDimension) { + return sliceWindows.computeIfAbsent(statusDimension, create); + } + + @Override + public Set getStatusDimensions() { + return sliceWindows.keySet(); + } + + @Override + public void resetCounter(StatusDimension statusDimension) { + + } +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/StateMachineImpl.java b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/StateMachineImpl.java new file mode 100644 index 000000000..bf4c35705 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/java/com/tencent/polaris/plugins/circuitbreaker/errrate/StateMachineImpl.java @@ -0,0 +1,76 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.circuitbreaker.errrate; + +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.plugins.circuitbreaker.common.AbstractStateMachine; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigGroup; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSet; +import com.tencent.polaris.plugins.circuitbreaker.common.ConfigSetLocator; +import com.tencent.polaris.plugins.circuitbreaker.common.HalfOpenCounter; +import com.tencent.polaris.plugins.circuitbreaker.common.stat.SliceWindow; +import com.tencent.polaris.plugins.circuitbreaker.common.stat.TimeRange; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 基于错误率的状态机转换实现 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class StateMachineImpl extends AbstractStateMachine { + + private static final Logger LOG = LoggerFactory.getLogger(StateMachineImpl.class); + + private final long metricTimeWindowMs; + + public StateMachineImpl(ConfigGroup configGroup, int pluginId, ConfigSetLocator configSetLocator, + long metricTimeWindowMs) { + super(configGroup, pluginId, configSetLocator); + this.metricTimeWindowMs = metricTimeWindowMs; + } + + @Override + public boolean closeToOpen(Instance instance, StatusDimension statusDimension, Parameter parameter) { + HalfOpenCounter halfOpenCounter = getHalfOpenCounterOnClose(instance, statusDimension); + if (halfOpenCounter == null) { + return false; + } + ConfigSet configSet = getConfigSetByLocator(instance, statusDimension, configSetLocator); + Config plugConfig = configSet.getPlugConfig(); + + ErrRateCounter errRateCounter = (ErrRateCounter) halfOpenCounter; + SliceWindow metricWindow = errRateCounter.getSliceWindow(statusDimension); + long currentTimeMs = parameter.getCurrentTimeMs(); + TimeRange timeRange = new TimeRange(currentTimeMs - metricTimeWindowMs, currentTimeMs); + long requestCount = metricWindow.calcMetricsBothIncluded(Dimension.keyRequestCount.ordinal(), timeRange); + if (requestCount == 0 || requestCount < plugConfig.getRequestVolumeThreshold()) { + //未达到其实请求数阈值 + return false; + } + long failCount = metricWindow.calcMetricsBothIncluded(Dimension.keyFailCount.ordinal(), timeRange); + double failRatio = (double) failCount / (double) requestCount; + LOG.debug("errRate statistic: request count {}, fail count {}, instance {}:{}, dimension {}, failRatio {}", + requestCount, failCount, instance.getHost(), instance.getPort(), statusDimension, failRatio); + //错误率达标 + return failRatio >= plugConfig.getErrRate(); + } + +} diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider new file mode 100644 index 000000000..d909c3f49 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.plugins.circuitbreaker.errrate.ErrRateCircuitBreaker \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker new file mode 100644 index 000000000..d909c3f49 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/circuitbreaker-errrate/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.circuitbreaker.CircuitBreaker @@ -0,0 +1 @@ +com.tencent.polaris.plugins.circuitbreaker.errrate.ErrRateCircuitBreaker \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/pom.xml new file mode 100644 index 000000000..342b0ee37 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/circuitbreaker/pom.xml @@ -0,0 +1,21 @@ + + + + polaris-plugins-circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + circuitbreaker + pom + + + circuitbreaker-common + circuitbreaker-errrate + circuitbreaker-errcount + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/pom.xml new file mode 100644 index 000000000..93fe0b44f --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/pom.xml @@ -0,0 +1,16 @@ + + + + outlier-detector + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + outlier-detector-http + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/java/com/tencent/polaris/plugins/outlier/detector/http/HttpOutlierDetector.java b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/java/com/tencent/polaris/plugins/outlier/detector/http/HttpOutlierDetector.java new file mode 100644 index 000000000..4677107d0 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/java/com/tencent/polaris/plugins/outlier/detector/http/HttpOutlierDetector.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.outlier.detector.http; + +import com.tencent.polaris.api.config.verify.DefaultValues; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.detect.OutlierDetector; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.RetStatus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * HttpOutlierDetector.java + * + * @author andrewshan + * @date 2019/9/19 + */ +public class HttpOutlierDetector implements OutlierDetector { + + private static final Logger LOG = LoggerFactory.getLogger(HttpOutlierDetector.class); + + private static final int EXPECT_CODE = 200; + + @Override + public DetectResult detectInstance(Instance instance) throws PolarisException { + try { + //TODO 从配置读取 + String pattern = "/detect"; + + String path = String.format("http://%s:%d%s", instance.getHost(), instance.getPort(), pattern); + java.net.URL url = new java.net.URL(path); + java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection(); + + conn.setRequestMethod("GET"); + conn.setConnectTimeout(3 * 1000);// 连接超时 + conn.setReadTimeout(3 * 1000);// 读取超时 + + if (conn.getResponseCode() == EXPECT_CODE) { + return new DetectResult(RetStatus.RetSuccess); + } + return new DetectResult(RetStatus.RetFail); + } catch (Exception e) { + LOG.error("http detect exception, service:{}, host:{}, port:{}, e:{}", instance.getService(), + instance.getHost(), instance.getPort(), e); + return new DetectResult(RetStatus.RetFail); + } + } + + @Override + public String getName() { + return DefaultValues.DEFAULT_HTTP_OUTLIER_DETECT; + } + + @Override + public PluginType getType() { + return PluginTypes.OUTLIER_DETECTOR.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + + } + + @Override + public void destroy() { + + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector new file mode 100644 index 000000000..7ba3f8912 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-http/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector @@ -0,0 +1 @@ +com.tencent.polaris.plugins.outlier.detector.http.HttpOutlierDetector \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/pom.xml new file mode 100644 index 000000000..8223f340c --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/pom.xml @@ -0,0 +1,16 @@ + + + + outlier-detector + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + outlier-detector-tcp + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/java/com/tencent/polaris/plugins/outlier/detector/tcp/TcpOutlierDetector.java b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/java/com/tencent/polaris/plugins/outlier/detector/tcp/TcpOutlierDetector.java new file mode 100644 index 000000000..82c50f198 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/java/com/tencent/polaris/plugins/outlier/detector/tcp/TcpOutlierDetector.java @@ -0,0 +1,141 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.outlier.detector.tcp; + +import com.tencent.polaris.api.config.verify.DefaultValues; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.detect.OutlierDetector; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.RetStatus; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * TcpOutlierDetector.java + * + * @author andrewshan + * @date 2019/9/19 + */ +public class TcpOutlierDetector implements OutlierDetector { + + private static final Logger LOG = LoggerFactory.getLogger(TcpOutlierDetector.class); + + @Override + public DetectResult detectInstance(Instance instance) throws PolarisException { + String host = instance.getHost(); + int port = instance.getPort(); + + Socket socket = null; + try { + socket = new Socket(host, port); + //TODO 从配置中读取 + String sendStr = "detect"; + String expectRecvStr = "ok"; + + boolean needSendData = !(sendStr == null || "".equals(sendStr)); + if (!needSendData) { + //未配置发送包,则连接成功即可 + return new DetectResult(RetStatus.RetSuccess); + } + + byte[] sendBytes = sendStr.getBytes("UTF8"); + byte[] expectRecvBytes = expectRecvStr.getBytes("UTF8"); + + OutputStream os = socket.getOutputStream(); + //发包 + os.write(sendBytes); + + byte[] recvBytes = recvFromSocket(socket, expectRecvBytes.length); + + if (Arrays.equals(Arrays.copyOfRange(recvBytes, 0, expectRecvBytes.length), expectRecvBytes)) { + //回包符合预期 + return new DetectResult(RetStatus.RetSuccess); + } + + return new DetectResult(RetStatus.RetFail); + + } catch (IOException e) { + LOG.info("tcp detect instance, create sock exception, host:{}, port:{}, e:{}", host, port, e); + return null; + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + LOG.info("tcp detect instance, close sock exception, host:{}, port:{}, e:{}", host, port, e); + } + } + } + } + + private byte[] recvFromSocket(Socket socket, int maxLen) throws IOException { + InputStream is = socket.getInputStream(); + byte[] recvBytes = new byte[1024]; + int recvLen = 0; + int tempLen; + do { + if (recvLen + maxLen > recvBytes.length) { + break; + } + tempLen = is.read(recvBytes, recvLen, maxLen); + if (tempLen >= 0) { + recvLen += tempLen; + } else { + // 当返回-1时代表已经读完,防止死循环 + return recvBytes; + } + } while (tempLen >= 0 || recvLen >= maxLen); + + return recvBytes; + } + + @Override + public String getName() { + return DefaultValues.DEFAULT_TCP_OUTLIER_DETECT; + } + + @Override + public PluginType getType() { + return PluginTypes.OUTLIER_DETECTOR.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + + } + + @Override + public void destroy() { + + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector new file mode 100644 index 000000000..44ca45a07 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-tcp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector @@ -0,0 +1 @@ +com.tencent.polaris.plugins.outlier.detector.tcp.TcpOutlierDetector \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/pom.xml new file mode 100644 index 000000000..1df63e323 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/pom.xml @@ -0,0 +1,16 @@ + + + + outlier-detector + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + outlier-detector-udp + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/java/com/tencent/polaris/plugins/outlier/detector/udp/UdpOutlierDetector.java b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/java/com/tencent/polaris/plugins/outlier/detector/udp/UdpOutlierDetector.java new file mode 100644 index 000000000..68b39bee2 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/java/com/tencent/polaris/plugins/outlier/detector/udp/UdpOutlierDetector.java @@ -0,0 +1,109 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.outlier.detector.udp; + +import com.tencent.polaris.api.config.verify.DefaultValues; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.detect.OutlierDetector; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.RetStatus; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.util.Arrays; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * UdpOutlierDetector.java + * + * @author andrewshan + * @date 2019/9/19 + */ +public class UdpOutlierDetector implements OutlierDetector { + + private static final Logger LOG = LoggerFactory.getLogger(UdpOutlierDetector.class); + + @Override + public DetectResult detectInstance(Instance instance) throws PolarisException { + DatagramSocket socket = null; + try { + //TODO 从配置中读取 + String sendStr = "detect"; + InetAddress inet = InetAddress.getByName(instance.getHost()); + byte[] sendBytes = sendStr.getBytes("UTF8"); + + socket = new DatagramSocket(); + // 两秒接收不到数据认为超时,防止获取不到连接一直在receive阻塞 + socket.setSoTimeout(2000); + //发送数据 + DatagramPacket sendPacket = new DatagramPacket(sendBytes, sendBytes.length, inet, instance.getPort()); + socket.send(sendPacket); + byte[] recvBuf = new byte[1024]; + DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length); + socket.receive(recvPacket); + + socket.close(); + String expectRecvStr = "ok"; + byte[] expectRecvBytes = expectRecvStr.getBytes("UTF8"); + if (!Arrays.equals(Arrays.copyOfRange(recvBuf, 0, expectRecvBytes.length), expectRecvBytes)) { + return new DetectResult(RetStatus.RetFail); + } + return new DetectResult(RetStatus.RetSuccess); + + } catch (Exception e) { + LOG.error("udp detect instance exception, host:{}, port:{}, e:{}", instance.getHost(), instance.getPort(), + e); + return null; + } finally { + if (socket != null) { + socket.close(); + } + } + } + + @Override + public String getName() { + return DefaultValues.DEFAULT_UDP_OUTLIER_DETECT; + } + + @Override + public PluginType getType() { + return PluginTypes.OUTLIER_DETECTOR.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + + } + + @Override + public void destroy() { + + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector new file mode 100644 index 000000000..c29c06a61 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/outlier-detector-udp/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.detect.OutlierDetector @@ -0,0 +1 @@ +com.tencent.polaris.plugins.outlier.detector.udp.UdpOutlierDetector \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/pom.xml new file mode 100644 index 000000000..5556f376c --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/outlier-detector/pom.xml @@ -0,0 +1,21 @@ + + + + polaris-plugins-circuitbreaker + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + outlier-detector + pom + + outlier-detector-http + outlier-detector-tcp + outlier-detector-udp + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-circuitbreaker/pom.xml b/polaris-plugins/polaris-plugins-circuitbreaker/pom.xml new file mode 100644 index 000000000..7cc3a8c64 --- /dev/null +++ b/polaris-plugins/polaris-plugins-circuitbreaker/pom.xml @@ -0,0 +1,36 @@ + + + + polaris-plugins + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins-circuitbreaker + pom + + circuitbreaker + outlier-detector + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/pom.xml b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/pom.xml new file mode 100644 index 000000000..b4b662dac --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/pom.xml @@ -0,0 +1,23 @@ + + + + connector + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + connector-polaris-grpc + jar + + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/Connection.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/Connection.java new file mode 100644 index 000000000..2fed82d99 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/Connection.java @@ -0,0 +1,268 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc; + +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.pojo.ServiceKey; +import io.grpc.ManagedChannel; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 封装的连接对象 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class Connection { + + private static final Logger LOG = LoggerFactory.getLogger(Connection.class); + + /** + * 连接标识 + */ + private final ConnID connID; + + /** + * GRPC连接 + */ + private final ManagedChannel channel; + + /** + * 连接管理器 + */ + private final ConnectionManager connectionManager; + + + /** + * 创建时间 + */ + private final long createTimeMs; + + /** + * 引用数 + */ + private final AtomicInteger ref = new AtomicInteger(0); + + /** + * 是否已经开始销毁 + */ + private final AtomicBoolean lazyDestroy = new AtomicBoolean(false); + + /** + * 申请锁 + */ + private final Object lock = new Object(); + + /** + * 连接是否已经关闭 + */ + private boolean closed; + + public Connection(ManagedChannel channel, ConnID connID, ConnectionManager connectionManager) { + this.connID = connID; + this.channel = channel; + this.createTimeMs = System.currentTimeMillis(); + this.connectionManager = connectionManager; + } + + /** + * 是否可用连接 + * + * @param connection 连接对象 + * @return boolean + */ + public static boolean isAvailableConnection(Connection connection) { + if (null == connection) { + return false; + } + return !connection.lazyDestroy.get(); + } + + /** + * 尝试占据连接,ref+1 + * + * @return 占据成功返回true,否则返回false + */ + public boolean acquire() { + if (lazyDestroy.get()) { + return false; + } + synchronized (lock) { + if (lazyDestroy.get()) { + return false; + } + int curRef = ref.incrementAndGet(); + LOG.trace("connection {}: acquired, curRef is {}", connID, curRef); + return true; + } + } + + /** + * 关闭连接 + */ + public void closeConnection() { + synchronized (lock) { + if (ref.get() <= 0 && !closed) { + closed = true; + ManagedChannel shutdownChan = channel.shutdown(); + if (null == shutdownChan) { + return; + } + try { + shutdownChan.awaitTermination(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + LOG.error(String.format("interrupted while closing connection %s", connID), e); + } + } + } + } + + /** + * 懒回收 + */ + public void lazyClose() { + //设置状态,不允许该连接再继续分配 + lazyDestroy.set(true); + int curRef = ref.get(); + LOG.trace("connection {}: lazyClose, curRef is {}", connID, curRef); + if (curRef <= 0) { + closeConnection(); + } + } + + /** + * 释放连接占用 + * + * @param opKey 操作key + */ + public void release(String opKey) { + int nextValue = ref.decrementAndGet(); + LOG.trace("connection {}: pending to release for op {}, curRef is {}", connID, opKey, nextValue); + if (nextValue == 0 && lazyDestroy.get()) { + closeConnection(); + } + } + + public void reportFail() { + connectionManager.reportFailConnection(connID); + } + + public ManagedChannel getChannel() { + return channel; + } + + public long getCreateTimeMs() { + return createTimeMs; + } + + public ConnID getConnID() { + return connID; + } + + public static class ConnID { + + /** + * 连接ID,UUID + */ + private final String id; + + /** + * 所属服务 + */ + private final ServiceKey serviceKey; + + /** + * 集群类型 + */ + private final ClusterType clusterType; + + /** + * Server端主机信息 + */ + private final String host; + + /** + * Server端端口信息 + */ + private final int port; + + public ConnID(ServiceKey serviceKey, ClusterType clusterType, String host, int port) { + this.id = UUID.randomUUID().toString(); + this.serviceKey = serviceKey; + this.clusterType = clusterType; + this.host = host; + this.port = port; + } + + public String getId() { + return id; + } + + public ServiceKey getServiceKey() { + return serviceKey; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public ClusterType getClusterType() { + return clusterType; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ConnID{" + + "id='" + id + '\'' + + ", serviceKey=" + serviceKey + + ", clusterType=" + clusterType + + ", host='" + host + '\'' + + ", port=" + port + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ConnID connID = (ConnID) o; + return port == connID.port && Objects.equals(id, connID.id) && Objects.equals(serviceKey, connID.serviceKey) + && clusterType == connID.clusterType && Objects.equals(host, connID.host); + } + + @Override + public int hashCode() { + return Objects.hash(id, serviceKey, clusterType, host, port); + } + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ConnectionManager.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ConnectionManager.java new file mode 100644 index 000000000..d02e605d6 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ConnectionManager.java @@ -0,0 +1,452 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.config.global.ServerConnectorConfig; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.pojo.Node; +import com.tencent.polaris.client.util.NamedThreadFactory; +import com.tencent.polaris.plugins.connector.grpc.Connection.ConnID; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 用于管理与后端服务器的GRPC连接. + * + * @author andrewshan + * @date 2019/8/22 + */ +public class ConnectionManager extends Destroyable { + + private static final Logger LOG = LoggerFactory.getLogger(ConnectionManager.class); + + /** + * 首次连接控制锁 + */ + private final Object lock = new Object(); + + private Extensions extensions; + + private final long connectTimeoutMs; + + private final long switchIntervalMs; + + private final ScheduledExecutorService switchExecutorService; + + private final String protocol; + + private final Map serverAddresses = new HashMap<>(); + + private final Map> readyNotifiers = new HashMap<>(); + + private String clientId; + + /** + * 构造器 + * + * @param initContext 上下文 + * @param notifiers 回调函数 + */ + public ConnectionManager(InitContext initContext, Map> notifiers) { + this.clientId = initContext.getValueContext().getClientId(); + Configuration config = initContext.getConfig(); + ServerConnectorConfig serverConnectorConfig = config.getGlobal().getServerConnector(); + this.readyNotifiers.putAll(notifiers); + this.connectTimeoutMs = serverConnectorConfig.getConnectTimeout(); + this.protocol = serverConnectorConfig.getProtocol(); + List addresses = serverConnectorConfig.getAddresses(); + serverAddresses.put(ClusterType.BUILTIN_CLUSTER, new ServerAddressList(addresses, ClusterType.BUILTIN_CLUSTER)); + Collection serverServices = initContext.getServerServices(); + ServerServiceInfo discoverService = null; + ServerServiceInfo healthCheckService = null; + if (CollectionUtils.isNotEmpty(serverServices)) { + for (ServerServiceInfo serverService : serverServices) { + if (serverService.getClusterType() == ClusterType.SERVICE_DISCOVER_CLUSTER) { + discoverService = serverService; + continue; + } + if (serverService.getClusterType() == ClusterType.HEALTH_CHECK_CLUSTER) { + healthCheckService = serverService; + } + } + } + if (null == discoverService) { + serverAddresses.put(ClusterType.SERVICE_DISCOVER_CLUSTER, + new ServerAddressList(addresses, ClusterType.SERVICE_DISCOVER_CLUSTER)); + } else { + serverAddresses + .put(ClusterType.SERVICE_DISCOVER_CLUSTER, + new ServerAddressList(discoverService, ClusterType.SERVICE_DISCOVER_CLUSTER)); + } + if (null == healthCheckService) { + serverAddresses.put(ClusterType.HEALTH_CHECK_CLUSTER, + new ServerAddressList(addresses, ClusterType.HEALTH_CHECK_CLUSTER)); + } else { + serverAddresses + .put(ClusterType.HEALTH_CHECK_CLUSTER, + new ServerAddressList(healthCheckService, ClusterType.HEALTH_CHECK_CLUSTER)); + } + switchIntervalMs = serverConnectorConfig.getServerSwitchInterval(); + switchExecutorService = Executors + .newSingleThreadScheduledExecutor(new NamedThreadFactory("connection-manager")); + } + + public void setExtensions(Extensions extensions) { + synchronized (lock) { + this.extensions = extensions; + } + switchExecutorService.scheduleAtFixedRate(new SwitchServerTask(), switchIntervalMs, switchIntervalMs, + TimeUnit.MILLISECONDS); + } + + public boolean checkReady(ClusterType clusterType) { + ServerAddressList serverAddressList = serverAddresses.get(clusterType); + if (null == serverAddressList) { + return false; + } + return serverAddressList.ready.get(); + } + + /** + * 设置准备状态 + * + * @param serviceEventKey 服务信息 + */ + public void makeReady(ServiceEventKey serviceEventKey) { + for (ServerAddressList serverAddressList : serverAddresses.values()) { + if (serverAddressList.checkAndSetReady(serviceEventKey)) { + return; + } + } + } + + /** + * 获取连接 + * + * @param opKey 操作key + * @param clusterType 集群类型 + * @return 连接 + */ + public Connection getConnection(String opKey, ClusterType clusterType) { + while (true) { + Connection connection; + try { + connection = tryGetConnection(opKey, clusterType); + } catch (PolarisException e) { + LOG.error("fail to get connection, opKey is {}, cluster {}", opKey, clusterType, e); + throw e; + } + if (connection.acquire()) { + LOG.debug("connection id={} acquired", connection.getConnID()); + return connection; + } + } + } + + private Connection tryGetConnection(String opKey, ClusterType clusterType) throws PolarisException { + if (null == extensions) { + throw new PolarisException(ErrorCode.INVALID_STATE, "connection manager not ready"); + } + ServerAddressList serverAddressList = serverAddresses.get(clusterType); + if (null == serverAddressList) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, String.format("unknown clusterType %s", clusterType)); + } + return serverAddressList.tryGetConnection(opKey, connectTimeoutMs); + } + + + @Override + public void doDestroy() { + ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{switchExecutorService}); + for (Map.Entry entry : serverAddresses.entrySet()) { + ServerAddressList serverAddressList = entry.getValue(); + serverAddressList.shutdown(); + } + } + + /** + * 上报错误连接,要求重连 + * + * @param connId 连接ID + */ + public void reportFailConnection(ConnID connId) { + if (isDestroyed()) { + return; + } + LOG.debug("connection id={} reportFailConnection", connId); + switchExecutorService.execute(new SwitchTargetTask(connId)); + } + + private class SwitchTargetTask implements Runnable { + + private final ConnID connID; + + public SwitchTargetTask(ConnID connID) { + this.connID = connID; + } + + + @Override + public void run() { + ServerAddressList serverAddressList = serverAddresses.get(connID.getClusterType()); + if (null != serverAddressList) { + serverAddressList.switchClientOnFail(connID); + } + } + } + + private class SwitchServerTask implements Runnable { + + @Override + public void run() { + for (Map.Entry entry : serverAddresses.entrySet()) { + ClusterType clusterType = entry.getKey(); + if (clusterType == ClusterType.BUILTIN_CLUSTER) { + continue; + } + ServerAddressList serverAddressList = entry.getValue(); + serverAddressList.switchClient(); + } + } + } + + private class ServerAddressList { + + private final ServerServiceInfo serverServiceInfo; + + private final ClusterType clusterType; + + private final AtomicReference curConnectionValue = new AtomicReference<>(); + + private int curIndex; + + private final List nodes = new ArrayList<>(); + + private final Object lock = new Object(); + + private final AtomicBoolean ready = new AtomicBoolean(false); + + /** + * 设置准备好状态 + * + * @param serviceEventKey + * @return 是否设置成功 + */ + public boolean checkAndSetReady(ServiceEventKey serviceEventKey) { + if (null == serverServiceInfo) { + return false; + } + if (serverServiceInfo.getServiceKey().equals(serviceEventKey.getServiceKey())) { + makeReady(); + return true; + } + return false; + } + + private void makeReady() { + LOG.info("[ServerConnector]cluster {}, service {} has been made ready", clusterType, serverServiceInfo); + if (ready.compareAndSet(false, true)) { + CompletableFuture future = readyNotifiers.get(clusterType); + if (null != future) { + future.complete("ready"); + } + } + } + + /** + * 埋点集群 + * + * @param addresses 地址列表 + */ + ServerAddressList(List addresses, ClusterType clusterType) { + for (String address : addresses) { + int colonIdx = address.lastIndexOf(":"); + String host = address.substring(0, colonIdx); + int port = Integer.parseInt(address.substring(colonIdx + 1)); + nodes.add(new Node(host, port)); + } + this.clusterType = clusterType; + serverServiceInfo = null; + makeReady(); + } + + /** + * 服务发现集群 + * + * @param serverServiceInfo 服务名 + */ + ServerAddressList(ServerServiceInfo serverServiceInfo, ClusterType clusterType) { + this.clusterType = clusterType; + this.serverServiceInfo = serverServiceInfo; + } + + /** + * 获取连接 + * + * @param opKey 来源的方法 + * @return 连接对象 + * @throws PolarisException + */ + public Connection tryGetConnection(String opKey, long timeoutMs) throws PolarisException { + Connection curConnection = curConnectionValue.get(); + if (Connection.isAvailableConnection(curConnection)) { + return curConnection; + } + synchronized (lock) { + curConnection = curConnectionValue.get(); + if (Connection.isAvailableConnection(curConnection)) { + return curConnection; + } + Node servAddress = getServerAddress(); + ServiceKey svcKey = null; + if (null != serverServiceInfo) { + svcKey = serverServiceInfo.getServiceKey(); + } + ConnID connID = new ConnID(svcKey, clusterType, servAddress.getHost(), + servAddress.getPort()); + Connection connection = connectTarget(connID); + if (null != curConnection) { + curConnection.lazyClose(); + } + curConnectionValue.set(connection); + return connection; + } + } + + /** + * 停止服务 + */ + public void shutdown() { + synchronized (lock) { + Connection curConnection = curConnectionValue.get(); + if (Connection.isAvailableConnection(curConnection)) { + curConnection.lazyClose(); + } + } + } + + private Node getServerAddress() throws PolarisException { + if (null == serverServiceInfo) { + Node node = nodes.get(curIndex % nodes.size()); + curIndex++; + return node; + } + Extensions extensions = ConnectionManager.this.extensions; + Instance instance = getDiscoverInstance(extensions); + return new Node(instance.getHost(), instance.getPort()); + } + + public void switchClientOnFail(ConnID lastConn) throws PolarisException { + synchronized (lock) { + Connection curConnection = curConnectionValue.get(); + if (null != curConnection && !curConnection.getConnID().equals(lastConn)) { + //已经完成切换,不处理 + return; + } + Node servAddress = getServerAddress(); + if (null == servAddress) { + return; + } + if (null != curConnection) { + if (servAddress.getHost().equals(curConnection.getConnID().getHost()) + && servAddress.getPort() == curConnection.getConnID().getPort()) { + return; + } + curConnection.lazyClose(); + } + ConnID connID = new ConnID(serverServiceInfo.getServiceKey(), clusterType, servAddress.getHost(), + servAddress.getPort()); + Connection connection = connectTarget(connID); + curConnectionValue.set(connection); + } + } + + public void switchClient() throws PolarisException { + Connection curConnection = curConnectionValue.get(); + //只有成功后,才进行切换 + if (!Connection.isAvailableConnection(curConnection)) { + return; + } + LOG.info("start switch for {}", serverServiceInfo.getServiceKey()); + synchronized (lock) { + curConnection = curConnectionValue.get(); + if (!Connection.isAvailableConnection(curConnection)) { + return; + } + Node servAddress = getServerAddress(); + if (null == servAddress) { + return; + } + if (servAddress.getHost().equals(curConnection.getConnID().getHost()) + && servAddress.getPort() == curConnection.getConnID().getPort()) { + return; + } + ConnID connID = new ConnID(serverServiceInfo.getServiceKey(), clusterType, servAddress.getHost(), + servAddress.getPort()); + Connection connection = connectTarget(connID); + curConnection.lazyClose(); + curConnectionValue.set(connection); + } + } + + + private Instance getDiscoverInstance(Extensions extensions) throws PolarisException { + ServiceKey serviceKey = serverServiceInfo.getServiceKey(); + Instance instance = BaseFlow + .commonGetOneInstance(extensions, serviceKey, serverServiceInfo.getRouters(), + serverServiceInfo.getLbPolicy(), protocol, clientId); + LOG.info("[ConnectionManager]success to get instance for service {}, instance is {}:{}", serviceKey, + instance.getHost(), instance.getPort()); + return instance; + } + + private Connection connectTarget(ConnID connID) { + ManagedChannelBuilder builder = ManagedChannelBuilder.forAddress(connID.getHost(), connID.getPort()) + .usePlaintext(); + ManagedChannel channel = builder.build(); + return new Connection(channel, connID, ConnectionManager.this); + } + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcConnector.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcConnector.java new file mode 100644 index 000000000..ce5904912 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcConnector.java @@ -0,0 +1,488 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc; + +import com.google.protobuf.StringValue; +import com.google.protobuf.TextFormat; +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.config.global.ServerConnectorConfig; +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.RetriableException; +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.server.CommonProviderRequest; +import com.tencent.polaris.api.plugin.server.CommonProviderResponse; +import com.tencent.polaris.api.plugin.server.ReportClientRequest; +import com.tencent.polaris.api.plugin.server.ReportClientResponse; +import com.tencent.polaris.api.plugin.server.ServerConnector; +import com.tencent.polaris.api.plugin.server.ServiceEventHandler; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.client.pb.ClientProto; +import com.tencent.polaris.client.pb.PolarisGRPCGrpc; +import com.tencent.polaris.client.pb.ResponseProto; +import com.tencent.polaris.client.pb.ServiceProto; +import com.tencent.polaris.plugins.connector.grpc.ServiceUpdateTask.Status; +import com.tencent.polaris.plugins.connector.grpc.ServiceUpdateTask.Type; +import com.tencent.polaris.client.util.NamedThreadFactory; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implement of {@link ServerConnector} to connect to Polaris server. + * It provides methods to manage resources relate to a service: + * 1. registerEventHandler/deRegisterEventHandler to subscribe instance/config for a service. + * 2. registerInstance/deregisterInstance to register/deregister an instance. + * 3. heartbeat to send heartbeat manually. + * + * @author andrewshan + * @date 2019/8/21 + */ +public class GrpcConnector extends Destroyable implements ServerConnector { + + private static final Logger LOG = LoggerFactory.getLogger(GrpcConnector.class); + + private static final int TASK_RETRY_INTERVAL_MS = 500; + + private long messageTimeoutMs; + + private ConnectionManager connectionManager; + + private final Map updateTaskSet = new ConcurrentHashMap<>(); + + private final Map> streamClients = new HashMap<>(); + + private long connectionIdleTimeoutMs; + + /** + * 发送消息的线程池 + */ + private ScheduledThreadPoolExecutor sendDiscoverExecutor; + + /** + * 用于往埋点服务发送消息的线程池,高优先处理 + */ + private ScheduledThreadPoolExecutor buildInExecutor; + + /** + * 定时更新的线程池 + */ + private ScheduledThreadPoolExecutor updateServiceExecutor; + + private CompletableFuture readyFuture; + + @Override + public void init(InitContext ctx) throws PolarisException { + readyFuture = new CompletableFuture<>(); + Map> futures = new HashMap<>(); + futures.put(ClusterType.SERVICE_DISCOVER_CLUSTER, readyFuture); + connectionManager = new ConnectionManager(ctx, futures); + Configuration config = ctx.getConfig(); + ServerConnectorConfig connectorConfig = config.getGlobal().getServerConnector(); + connectionIdleTimeoutMs = connectorConfig.getConnectionIdleTimeout(); + messageTimeoutMs = connectorConfig.getMessageTimeout(); + sendDiscoverExecutor = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(getName() + "-send-discovery"), new CallerRunsPolicy()); + sendDiscoverExecutor.setMaximumPoolSize(1); + buildInExecutor = new ScheduledThreadPoolExecutor(0, + new NamedThreadFactory(getName() + "-builtin-discovery"), new CallerRunsPolicy()); + buildInExecutor.setMaximumPoolSize(1); + streamClients.put(ClusterType.BUILTIN_CLUSTER, new AtomicReference<>()); + streamClients.put(ClusterType.SERVICE_DISCOVER_CLUSTER, new AtomicReference<>()); + updateServiceExecutor = new ScheduledThreadPoolExecutor(1, + new NamedThreadFactory(getName() + "-update-service")); + updateServiceExecutor.setMaximumPoolSize(1); + } + + private void waitDiscoverReady() { + try { + readyFuture.get(messageTimeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } catch (TimeoutException e) { + throw new RetriableException(ErrorCode.API_TIMEOUT, "discover service not ready"); + } + } + + @Override + public void registerServiceHandler(ServiceEventHandler handler) { + checkDestroyed(); + ServiceUpdateTask serviceUpdateTask = new ServiceUpdateTask(handler, this); + submitServiceHandler(serviceUpdateTask, 0); + } + + public void retryServiceUpdateTask(ServiceUpdateTask updateTask) { + LOG.info("[ServerConnector]retry schedule task for {}, retry delay {}", updateTask, TASK_RETRY_INTERVAL_MS); + updateTask.setStatus(Status.RUNNING, Status.READY); + if (isDestroyed()) { + return; + } + submitServiceHandler(updateTask, TASK_RETRY_INTERVAL_MS); + } + + private void submitServiceHandler(ServiceUpdateTask updateTask, long delayMs) { + ClusterType targetCluster = updateTask.getTargetClusterType(); + if (updateTask.setStatus(Status.READY, Status.RUNNING)) { + if (targetCluster == ClusterType.BUILTIN_CLUSTER) { + LOG.info("[ServerConnector]task for service {} has been scheduled builtin", updateTask); + buildInExecutor.schedule(updateTask, delayMs, TimeUnit.MILLISECONDS); + } else { + LOG.info("[ServerConnector]task for service {} has been scheduled discover", updateTask); + sendDiscoverExecutor.schedule(updateTask, delayMs, TimeUnit.MILLISECONDS); + } + } + } + + @Override + public void deRegisterServiceHandler(ServiceEventKey eventKey) throws PolarisException { + checkDestroyed(); + ServiceUpdateTask serviceUpdateTask = updateTaskSet.get(eventKey); + if (null != serviceUpdateTask) { + boolean result = serviceUpdateTask.setType(Type.LONG_RUNNING, Type.TERMINATED); + LOG.info("[ServerConnector]success to deRegister updateServiceTask {}, result is {}", eventKey, result); + } + } + + + @Override + public CommonProviderResponse registerInstance(CommonProviderRequest req) throws PolarisException { + checkDestroyed(); + ServiceKey serviceKey = new ServiceKey(req.getNamespace(), req.getService()); + Connection connection = null; + try { + waitDiscoverReady(); + connection = connectionManager + .getConnection(GrpcUtil.OP_KEY_REGISTER_INSTANCE, ClusterType.SERVICE_DISCOVER_CLUSTER); + PolarisGRPCGrpc.PolarisGRPCBlockingStub stub = PolarisGRPCGrpc.newBlockingStub(connection.getChannel()); + GrpcUtil.attachRequestHeader(stub, GrpcUtil.nextInstanceRegisterReqId()); + ResponseProto.Response registerInstanceResponse = stub.registerInstance(buildRegisterInstanceRequest(req)); + GrpcUtil.checkResponse(registerInstanceResponse); + if (!registerInstanceResponse.hasInstance()) { + throw new PolarisException(ErrorCode.SERVER_USER_ERROR, + "invalid register response: missing instance"); + } + CommonProviderResponse resp = new CommonProviderResponse(); + resp.setInstanceID(registerInstanceResponse.getInstance().getId().getValue()); + resp.setExists(registerInstanceResponse.getCode().getValue() == ServerCodes.EXISTED_RESOURCE); + return resp; + } catch (Throwable t) { + if (t instanceof PolarisException) { + throw t; + } + throw new RetriableException(ErrorCode.NETWORK_ERROR, + String.format("fail to register host %s:%d service %s", req.getHost(), req.getPort(), serviceKey), + t); + } finally { + if (null != connection) { + connection.release(GrpcUtil.OP_KEY_REGISTER_INSTANCE); + } + } + } + + private ServiceProto.Instance buildRegisterInstanceRequest(CommonProviderRequest req) { + ServiceProto.Instance.Builder instanceBuilder = ServiceProto.Instance.newBuilder(); + instanceBuilder.setService(StringValue.newBuilder().setValue(req.getService()).build()); + instanceBuilder.setNamespace(StringValue.newBuilder().setValue(req.getNamespace()).build()); + if (StringUtils.isNotBlank(req.getToken())) { + instanceBuilder.setServiceToken(StringValue.newBuilder().setValue(req.getToken()).build()); + } + instanceBuilder.setHost(StringValue.newBuilder().setValue(req.getHost()).build()); + instanceBuilder.setPort(UInt32Value.newBuilder().setValue(req.getPort()).build()); + if (StringUtils.isNotBlank(req.getProtocol())) { + instanceBuilder.setProtocol(StringValue.newBuilder().setValue(req.getProtocol()).build()); + } + if (StringUtils.isNotBlank(req.getVersion())) { + instanceBuilder.setVersion(StringValue.newBuilder().setValue(req.getVersion()).build()); + } + if (null != req.getWeight()) { + instanceBuilder.setWeight(UInt32Value.newBuilder().setValue(req.getWeight()).build()); + } + if (null != req.getPriority()) { + instanceBuilder.setPriority(UInt32Value.newBuilder().setValue(req.getPriority()).build()); + } + if (null != req.getMetadata()) { + instanceBuilder.putAllMetadata(req.getMetadata()); + } + if (null != req.getTtl()) { + ServiceProto.HealthCheck.Builder healthCheckBuilder = ServiceProto.HealthCheck.newBuilder(); + healthCheckBuilder.setType(ServiceProto.HealthCheck.HealthCheckType.HEARTBEAT); + healthCheckBuilder.setHeartbeat( + ServiceProto.HeartbeatHealthCheck.newBuilder().setTtl( + UInt32Value.newBuilder().setValue(req.getTtl()).build()).build()); + instanceBuilder.setHealthCheck(healthCheckBuilder.build()); + } + return instanceBuilder.build(); + } + + private ServiceProto.Instance buildHeartbeatRequest(CommonProviderRequest req) { + ServiceProto.Instance.Builder instanceBuilder = ServiceProto.Instance.newBuilder(); + if (StringUtils.isNotBlank(req.getInstanceID())) { + instanceBuilder.setId(StringValue.newBuilder().setValue(req.getInstanceID()).build()); + } + if (StringUtils.isNotBlank(req.getService())) { + instanceBuilder.setService(StringValue.newBuilder().setValue(req.getService()).build()); + } + if (StringUtils.isNotBlank(req.getHost())) { + instanceBuilder.setHost(StringValue.newBuilder().setValue(req.getHost()).build()); + } + if (StringUtils.isNotBlank(req.getNamespace())) { + instanceBuilder.setNamespace(StringValue.newBuilder().setValue(req.getNamespace()).build()); + } + if (req.getPort() > 0) { + instanceBuilder.setPort(UInt32Value.of(req.getPort())); + } + if (StringUtils.isNotBlank(req.getToken())) { + instanceBuilder.setServiceToken(StringValue.newBuilder().setValue(req.getToken()).build()); + } + return instanceBuilder.build(); + } + + private ClientProto.Client buildReportRequest(ReportClientRequest req) { + return ClientProto.Client.newBuilder() + .setHost(StringValue.newBuilder().setValue(req.getClientHost())) + .setVersion(StringValue.newBuilder().setValue(req.getVersion())).build(); + } + + private ServiceProto.Instance buildDeregisterInstanceRequest(CommonProviderRequest req) { + ServiceProto.Instance.Builder instanceBuilder = ServiceProto.Instance.newBuilder(); + if (StringUtils.isNotBlank(req.getInstanceID())) { + instanceBuilder.setId(StringValue.newBuilder().setValue(req.getInstanceID()).build()); + } + if (StringUtils.isNotBlank(req.getNamespace())) { + instanceBuilder.setNamespace(StringValue.newBuilder().setValue(req.getNamespace()).build()); + } + if (StringUtils.isNotBlank(req.getService())) { + instanceBuilder.setService(StringValue.newBuilder().setValue(req.getService()).build()); + } + if (StringUtils.isNotBlank(req.getHost())) { + instanceBuilder.setHost(StringValue.newBuilder().setValue(req.getHost()).build()); + } + if (req.getPort() > 0) { + instanceBuilder.setPort(UInt32Value.of(req.getPort())); + } + if (StringUtils.isNotBlank(req.getToken())) { + instanceBuilder.setServiceToken(StringValue.newBuilder().setValue(req.getToken()).build()); + } + return instanceBuilder.build(); + } + + @Override + public void deregisterInstance(CommonProviderRequest req) throws PolarisException { + checkDestroyed(); + Connection connection = null; + ServiceKey serviceKey = new ServiceKey(req.getNamespace(), req.getService()); + try { + waitDiscoverReady(); + connection = connectionManager + .getConnection(GrpcUtil.OP_KEY_DEREGISTER_INSTANCE, ClusterType.SERVICE_DISCOVER_CLUSTER); + PolarisGRPCGrpc.PolarisGRPCBlockingStub stub = PolarisGRPCGrpc.newBlockingStub(connection.getChannel()); + GrpcUtil.attachRequestHeader(stub, GrpcUtil.nextInstanceDeRegisterReqId()); + ResponseProto.Response deregisterInstanceResponse = stub + .deregisterInstance(buildDeregisterInstanceRequest(req)); + GrpcUtil.checkResponse(deregisterInstanceResponse); + LOG.debug("received deregister response {}", deregisterInstanceResponse); + } catch (Throwable t) { + if (t instanceof PolarisException) { + //服务端异常不进行重试 + throw t; + } + throw new RetriableException(ErrorCode.NETWORK_ERROR, + String.format("fail to deregister id %s, host %s:%d service %s", + req.getInstanceID(), req.getHost(), req.getPort(), serviceKey), t); + } finally { + if (null != connection) { + connection.release(GrpcUtil.OP_KEY_DEREGISTER_INSTANCE); + } + } + } + + @Override + public void heartbeat(CommonProviderRequest req) throws PolarisException { + checkDestroyed(); + Connection connection = null; + ServiceKey serviceKey = new ServiceKey(req.getNamespace(), req.getService()); + try { + waitDiscoverReady(); + connection = connectionManager + .getConnection(GrpcUtil.OP_KEY_INSTANCE_HEARTBEAT, ClusterType.HEALTH_CHECK_CLUSTER); + PolarisGRPCGrpc.PolarisGRPCBlockingStub stub = PolarisGRPCGrpc.newBlockingStub(connection.getChannel()); + GrpcUtil.attachRequestHeader(stub, GrpcUtil.nextHeartbeatReqId()); + ResponseProto.Response heartbeatResponse = stub.heartbeat(buildHeartbeatRequest(req)); + GrpcUtil.checkResponse(heartbeatResponse); + LOG.debug("received heartbeat response {}", heartbeatResponse); + } catch (Throwable t) { + if (t instanceof PolarisException) { + //服务端异常不进行重试 + throw t; + } + throw new RetriableException(ErrorCode.NETWORK_ERROR, + String.format("fail to heartbeat id %s, host %s:%d service %s", + req.getInstanceID(), req.getHost(), req.getPort(), serviceKey), t); + } finally { + if (null != connection) { + connection.release(GrpcUtil.OP_KEY_INSTANCE_HEARTBEAT); + } + } + } + + @Override + public ReportClientResponse reportClient(ReportClientRequest req) throws PolarisException { + checkDestroyed(); + waitDiscoverReady(); + Connection connection = connectionManager + .getConnection(GrpcUtil.OP_KEY_REPORT_CLIENT, ClusterType.SERVICE_DISCOVER_CLUSTER); + PolarisGRPCGrpc.PolarisGRPCBlockingStub stub = PolarisGRPCGrpc.newBlockingStub(connection.getChannel()); + GrpcUtil.attachRequestHeader(stub, GrpcUtil.nextHeartbeatReqId()); + ServiceKey serviceKey = new ServiceKey(req.getNamespace(), req.getService()); + try { + ClientProto.Client request = buildReportRequest(req); + ResponseProto.Response response = stub.reportClient(request); + LOG.debug("reportClient req:{}, rsp:{}", req, TextFormat.shortDebugString(response)); + GrpcUtil.checkResponse(response); + ReportClientResponse rsp = new ReportClientResponse(); + if (null == response.getClient().getLocation()) { + throw new IllegalStateException( + String.format("unexpected null response from clientReport api, response:%s", + TextFormat.shortDebugString(response))); + } + rsp.setCampus(response.getClient().getLocation().getCampus().getValue()); + rsp.setZone(response.getClient().getLocation().getZone().getValue()); + rsp.setRegion(response.getClient().getLocation().getRegion().getValue()); + return rsp; + } catch (Throwable t) { + if (t instanceof PolarisException) { + //服务端异常不进行重试 + throw t; + } + throw new RetriableException(ErrorCode.NETWORK_ERROR, + String.format("fail to report client host %s, version %s service %s", + req.getClientHost(), req.getVersion(), serviceKey), t); + } finally { + connection.release(GrpcUtil.OP_KEY_REPORT_CLIENT); + } + } + + @Override + public void updateServers(ServiceEventKey svcEventKey) { + connectionManager.makeReady(svcEventKey); + } + + + @Override + public String getName() { + return DefaultPlugins.SERVER_CONNECTOR_GRPC; + } + + @Override + public PluginType getType() { + return PluginTypes.SERVER_CONNECTOR.getBaseType(); + } + + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + connectionManager.setExtensions(extensions); + this.updateServiceExecutor + .scheduleWithFixedDelay(new UpdateServiceTask(), TASK_RETRY_INTERVAL_MS, TASK_RETRY_INTERVAL_MS, + TimeUnit.MILLISECONDS); + this.updateServiceExecutor.scheduleWithFixedDelay(new ClearIdleStreamClientTask(), this.connectionIdleTimeoutMs, + this.connectionIdleTimeoutMs, TimeUnit.MILLISECONDS); + } + + @Override + public void doDestroy() { + LOG.info("start to destroy connector {}", getName()); + ThreadPoolUtils.waitAndStopThreadPools( + new ExecutorService[]{sendDiscoverExecutor, buildInExecutor, updateServiceExecutor}); + if (null != connectionManager) { + connectionManager.destroy(); + } + } + + public ConnectionManager getConnectionManager() { + return connectionManager; + } + + public void addLongRunningTask(ServiceUpdateTask serviceUpdateTask) { + updateTaskSet.put(serviceUpdateTask.getServiceEventKey(), serviceUpdateTask); + } + + public AtomicReference getStreamClient(ClusterType clusterType) { + return streamClients.get(clusterType); + } + + public long getConnectionIdleTimeoutMs() { + return connectionIdleTimeoutMs; + } + + private class UpdateServiceTask implements Runnable { + + @Override + public void run() { + for (ServiceUpdateTask serviceUpdateTask : updateTaskSet.values()) { + if (isDestroyed()) { + break; + } + if (!serviceUpdateTask.needUpdate()) { + continue; + } + submitServiceHandler(serviceUpdateTask, 0); + } + } + } + + /** + * 清理过期的streamClient + */ + private class ClearIdleStreamClientTask implements Runnable { + + @Override + public void run() { + for (AtomicReference streamClientRef : streamClients.values()) { + SpecStreamClient streamClient = streamClientRef.get(); + if (null == streamClient) { + continue; + } + streamClient.syncCloseExpireStream(); + } + } + } + + +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java new file mode 100644 index 000000000..0356072da --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/GrpcUtil.java @@ -0,0 +1,176 @@ +package com.tencent.polaris.plugins.connector.grpc; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.client.pb.RequestProto.DiscoverRequest.DiscoverRequestType; +import com.tencent.polaris.client.pb.ResponseProto; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse.DiscoverResponseType; +import io.grpc.Metadata; +import io.grpc.stub.AbstractStub; +import io.grpc.stub.MetadataUtils; +import java.util.concurrent.atomic.AtomicLong; + +/** + * GrpcUtil + * + * @author vickliu + * @date + */ +public class GrpcUtil { + + public static final String OP_KEY_REGISTER_INSTANCE = "RegisterInstance"; + + public static final String OP_KEY_DEREGISTER_INSTANCE = "DeregisterInstance"; + + public static final String OP_KEY_INSTANCE_HEARTBEAT = "InstanceHeartbeat"; + + public static final String OP_KEY_DISCOVER = "Discover"; + + public static final String OP_KEY_REPORT_CLIENT = "ReportClient"; + + /** + * 请求ID的key + */ + private static final Metadata.Key KEY_REQUEST_ID = + Metadata.Key.of("request-id", Metadata.ASCII_STRING_MARSHALLER); + + /** + * 实例注册的请求ID前缀 + */ + private static final String REQ_ID_PREFIX_REGISTERINSTANCE = "1"; + + /** + * 实例反注册的请求ID前缀 + */ + private static final String REQ_ID_PREFIX_DEREGISTERINSTANCE = "2"; + + /** + * 实例心跳的请求ID前缀 + */ + private static final String REQ_ID_PREFIX_HEARTBEAT = "3"; + + /** + * 获取实例的请求ID前缀 + */ + private static final String REQ_ID_PREFIX_GETINSTANCES = "4"; + + /** + * 实例发现ID发生器 + */ + private static final AtomicLong SEED_INSTANCE_REQ_ID = new AtomicLong(); + + /** + * 实例注册ID发生器 + */ + private static final AtomicLong SEED_REGISTER_REQ_ID = new AtomicLong(); + + /** + * 实例反注册ID发生器 + */ + private static final AtomicLong SEED_DEREGISTER_REQ_ID = new AtomicLong(); + + /** + * 心跳上报ID发生器 + */ + private static final AtomicLong SEED_HEARTBEAT_REQ_ID = new AtomicLong(); + + /** + * 获取下一个实例请求ID + * + * @return string + */ + public static String nextGetInstanceReqId() { + return String.format("%s%d", REQ_ID_PREFIX_GETINSTANCES, SEED_INSTANCE_REQ_ID.incrementAndGet()); + } + + /** + * 获取下一个注册请求ID + * + * @return string + */ + public static String nextInstanceRegisterReqId() { + return String.format("%s%d", REQ_ID_PREFIX_REGISTERINSTANCE, SEED_REGISTER_REQ_ID.incrementAndGet()); + } + + /** + * 获取下一个反注册请求ID + * + * @return string + */ + public static String nextInstanceDeRegisterReqId() { + return String.format("%s%d", REQ_ID_PREFIX_DEREGISTERINSTANCE, SEED_DEREGISTER_REQ_ID.incrementAndGet()); + } + + /** + * 获取下一个心跳请求ID + * + * @return string + */ + public static String nextHeartbeatReqId() { + return String.format("%s%d", REQ_ID_PREFIX_HEARTBEAT, SEED_HEARTBEAT_REQ_ID.incrementAndGet()); + } + + + /** + * 为GRPC请求添加请求头部 + * + * @param 桩类型 + * @param stub 请求桩 + * @param nextID 请求ID + */ + public static > void attachRequestHeader(T stub, String nextID) { + Metadata extraHeaders = new Metadata(); + extraHeaders.put(KEY_REQUEST_ID, nextID); + MetadataUtils.attachHeaders(stub, extraHeaders); + } + + + public static void checkResponse(ResponseProto.Response response) throws PolarisException { + if (!response.hasCode()) { + return; + } + int respCode = response.getCode().getValue(); + if (respCode == ServerCodes.EXECUTE_SUCCESS || respCode == ServerCodes.EXISTED_RESOURCE) { + return; + } + String info = response.getInfo().getValue(); + throw new PolarisException(ErrorCode.SERVER_USER_ERROR, + String.format("server error %d: %s", respCode, info)); + } + + + public static DiscoverRequestType buildDiscoverRequestType( + ServiceEventKey.EventType type) { + switch (type) { + case INSTANCE: + return DiscoverRequestType.INSTANCE; + case ROUTING: + return DiscoverRequestType.ROUTING; + case RATE_LIMITING: + return DiscoverRequestType.RATE_LIMIT; + case CIRCUIT_BREAKING: + return DiscoverRequestType.CIRCUIT_BREAKER; + default: + return DiscoverRequestType.UNKNOWN; + } + } + + public static EventType buildEventType(DiscoverResponseType responseType) { + switch (responseType) { + case INSTANCE: + return EventType.INSTANCE; + case ROUTING: + return EventType.ROUTING; + case RATE_LIMIT: + return EventType.RATE_LIMITING; + case CIRCUIT_BREAKER: + return EventType.CIRCUIT_BREAKING; + default: + return EventType.UNKNOWN; + } + } + +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ServiceUpdateTask.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ServiceUpdateTask.java new file mode 100644 index 000000000..6ca4ff445 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/ServiceUpdateTask.java @@ -0,0 +1,227 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc; + +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.server.EventHandler; +import com.tencent.polaris.api.plugin.server.ServerEvent; +import com.tencent.polaris.api.plugin.server.ServiceEventHandler; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import java.util.Random; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServiceUpdateTask implements Runnable, Comparable { + + private static final Logger LOG = LoggerFactory.getLogger(ServiceUpdateTask.class); + + private final GrpcConnector grpcConnector; + + @Override + public int compareTo(ServiceUpdateTask o) { + return taskType.get().ordinal() - o.taskType.get().ordinal(); + } + + + public enum Type { + /** + * 首次调度 + */ + FIRST, + /** + * 长稳调度 + */ + LONG_RUNNING, + /** + * 已经销毁 + */ + TERMINATED + } + + private final AtomicReference taskType = new AtomicReference<>(); + + public enum Status { + /** + * 调度中 + */ + RUNNING, + /** + * 已经就绪 + */ + READY, + } + + private final AtomicReference taskStatus = new AtomicReference<>(); + + private final long refreshIntervalMs; + + private final EventHandler eventHandler; + + private final ServiceEventKey serviceEventKey; + + private final AtomicLong msgSendTime = new AtomicLong(0); + + private final AtomicLong lastUpdateTime = new AtomicLong(0); + + private final AtomicLong totalRequests = new AtomicLong(0); + + private final AtomicLong successUpdates = new AtomicLong(0); + + private final AtomicReference targetClusterType = new AtomicReference<>(); + + public ServiceUpdateTask(ServiceEventHandler handler, GrpcConnector connector) { + this.grpcConnector = connector; + this.serviceEventKey = handler.getServiceEventKey(); + this.refreshIntervalMs = handler.getRefreshIntervalMs() + (new Random()).nextInt(1000); + this.eventHandler = handler.getEventHandler(); + taskType.set(Type.FIRST); + taskStatus.set(Status.READY); + targetClusterType.set(handler.getTargetCluster()); + } + + public ServiceEventKey getServiceEventKey() { + return serviceEventKey; + } + + public EventHandler getEventHandler() { + return eventHandler; + } + + public ClusterType getTargetClusterType() { + return targetClusterType.get(); + } + + public boolean setType(Type last, Type current) { + return taskType.compareAndSet(last, current); + } + + public boolean setStatus(Status last, Status current) { + return taskStatus.compareAndSet(last, current); + } + + public Type getTaskType() { + return taskType.get(); + } + + public void retry() { + grpcConnector.retryServiceUpdateTask(this); + } + + @Override + public void run() { + if (getTaskType() == Type.FIRST) { + LOG.info("[ServerConnector]start to run first task {}", this); + } else { + LOG.debug("[ServerConnector]start to run task {}", this); + } + ConnectionManager connectionManager = grpcConnector.getConnectionManager(); + ClusterType clusterType = targetClusterType.get(); + boolean clusterReady = connectionManager.checkReady(clusterType); + if (!clusterReady) { + //没有ready,就重试 + LOG.info("{} service is not ready", clusterType); + grpcConnector.retryServiceUpdateTask(this); + return; + } + if (grpcConnector.isDestroyed()) { + LOG.info("{} grpc connection is destroyed", clusterType); + grpcConnector.retryServiceUpdateTask(this); + return; + } + AtomicReference streamClientAtomicReference = grpcConnector.getStreamClient(clusterType); + SpecStreamClient specStreamClient = streamClientAtomicReference.get(); + boolean available = checkStreamClientAvailable(specStreamClient); + if (!available) { + LOG.debug("[ServerConnector]start to get connection for task {}", this); + Connection connection = null; + try { + connection = connectionManager.getConnection(GrpcUtil.OP_KEY_DISCOVER, clusterType); + } catch (PolarisException e) { + LOG.error("[ServerConnector]fail to get connection to {}", clusterType, e); + } + if (null == connection) { + LOG.error("[ServerConnector]get null connection for {}", this); + grpcConnector.retryServiceUpdateTask(this); + return; + } + specStreamClient = new SpecStreamClient(connection, grpcConnector.getConnectionIdleTimeoutMs(), this); + streamClientAtomicReference.set(specStreamClient); + LOG.info("[ServerConnector]success to create stream client for task {}", this); + } + msgSendTime.set(System.currentTimeMillis()); + totalRequests.addAndGet(1); + specStreamClient.sendRequest(this); + } + + private boolean checkStreamClientAvailable(SpecStreamClient streamClient) { + if (null == streamClient) { + return false; + } + return streamClient.checkAvailable(this); + } + + /** + * 加入调度队列 + */ + public void addUpdateTaskSet() { + if (taskType.compareAndSet(Type.FIRST, Type.LONG_RUNNING)) { + targetClusterType.set(ClusterType.SERVICE_DISCOVER_CLUSTER); + grpcConnector.addLongRunningTask(this); + } + } + + /** + * 判断是否需要更新 + * + * @return boolean + */ + public boolean needUpdate() { + if (taskType.get() != Type.LONG_RUNNING || taskStatus.get() != Status.READY) { + return false; + } + long nowMs = System.currentTimeMillis(); + return nowMs - lastUpdateTime.get() >= refreshIntervalMs; + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "ServiceUpdateTask{" + + "taskType=" + taskType.get() + + ", taskStatus=" + taskStatus.get() + + ", serviceEventKey=" + serviceEventKey + + ", targetClusterType=" + targetClusterType.get() + + '}'; + } + + public long getRefreshIntervalMs() { + return refreshIntervalMs; + } + + public boolean notifyServerEvent(ServerEvent serverEvent) { + taskStatus.compareAndSet(Status.RUNNING, Status.READY); + lastUpdateTime.set(System.currentTimeMillis()); + if (null == serverEvent.getError()) { + successUpdates.addAndGet(1); + } + return eventHandler.onEventUpdate(serverEvent); + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/SpecStreamClient.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/SpecStreamClient.java new file mode 100644 index 000000000..7057c02bb --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/SpecStreamClient.java @@ -0,0 +1,355 @@ +package com.tencent.polaris.plugins.connector.grpc; + +import com.google.protobuf.StringValue; +import com.google.protobuf.TextFormat; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.exception.ServerErrorResponseException; +import com.tencent.polaris.api.plugin.server.ServerEvent; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.pb.PolarisGRPCGrpc; +import com.tencent.polaris.client.pb.RequestProto; +import com.tencent.polaris.client.pb.ResponseProto; +import com.tencent.polaris.client.pb.ServiceProto; +import com.tencent.polaris.plugins.connector.grpc.ServiceUpdateTask.Type; +import io.grpc.stub.StreamObserver; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 用于cluster/healthcheck/heartbeat的服务发现 + * + * @date 2021/1/30 + */ +public class SpecStreamClient implements StreamObserver { + + + private static final Logger LOG = LoggerFactory.getLogger(SpecStreamClient.class); + + /** + * 同步锁 + */ + private final Object clientLock = new Object(); + + /** + * 在流中没有结束的任务 + */ + private final Map pendingTask = new HashMap<>(); + + /** + * 连接是否可用 + */ + private final AtomicBoolean endStream = new AtomicBoolean(false); + + /** + * GRPC stream客户端 + */ + private final StreamObserver discoverClient; + + /** + * 连接对象 + */ + private final Connection connection; + + /** + * 请求ID + */ + private final String reqId; + + /** + * 最近更新时间 + */ + private final AtomicLong lastRecvTimeMs = new AtomicLong(0); + + /** + * 创建时间 + */ + private final long createTimeMs; + + /** + * 连接空闲时间 + */ + private final long connectionIdleTimeoutMs; + + /** + * 构造函数 + * + * @param connection 连接 + * @param connectionIdleTimeoutMs 连接超时 + * @param serviceUpdateTask 初始更新任务 + */ + public SpecStreamClient(Connection connection, long connectionIdleTimeoutMs, ServiceUpdateTask serviceUpdateTask) { + this.connection = connection; + this.connectionIdleTimeoutMs = connectionIdleTimeoutMs; + createTimeMs = System.currentTimeMillis(); + reqId = GrpcUtil.nextGetInstanceReqId(); + PolarisGRPCGrpc.PolarisGRPCStub namingStub = PolarisGRPCGrpc.newStub(connection.getChannel()); + GrpcUtil.attachRequestHeader(namingStub, GrpcUtil.nextGetInstanceReqId()); + discoverClient = namingStub.discover(this); + pendingTask.put(serviceUpdateTask.getServiceEventKey(), serviceUpdateTask); + } + + /** + * 关闭流 + * + * @param closeSend 是否发送EOF + */ + public void closeStream(boolean closeSend) { + boolean endStreamOK = endStream.compareAndSet(false, true); + if (!endStreamOK) { + return; + } + if (closeSend) { + LOG.info("[ServerConnector]connection {} start to closeSend", connection.getConnID()); + discoverClient.onCompleted(); + } + connection.release(GrpcUtil.OP_KEY_DISCOVER); + } + + private boolean isEndStream() { + return endStream.get(); + } + + + public String getReqId() { + return reqId; + } + + /** + * 发送请求 + * + * @param serviceUpdateTask 服务更新任务 + */ + public void sendRequest(ServiceUpdateTask serviceUpdateTask) { + ServiceEventKey serviceEventKey = serviceUpdateTask.getServiceEventKey(); + ServiceProto.Service.Builder builder = ServiceProto.Service.newBuilder(); + builder.setName(StringValue.newBuilder().setValue(serviceEventKey.getServiceKey().getService()).build()); + builder.setNamespace(StringValue.newBuilder().setValue(serviceEventKey.getServiceKey().getNamespace()).build()); + + RequestProto.DiscoverRequest.Builder req = RequestProto.DiscoverRequest.newBuilder(); + req.setType(GrpcUtil.buildDiscoverRequestType(serviceEventKey.getEventType())); // switch + req.setService(builder); + if (serviceUpdateTask.getTaskType() == Type.FIRST) { + LOG.info("[ServerConnector]send request(id={}) to {} for service {}", reqId, connection.getConnID(), + serviceEventKey); + } else { + LOG.debug("[ServerConnector]send request(id={}) to {} for service {}", reqId, connection.getConnID(), + serviceEventKey); + } + discoverClient.onNext(req.build()); + } + + private static class ValidResult { + + final ServiceEventKey serviceEventKey; + final ErrorCode errorCode; + final String message; + + public ValidResult(ServiceEventKey serviceEventKey, ErrorCode errorCode, String message) { + this.serviceEventKey = serviceEventKey; + this.errorCode = errorCode; + this.message = message; + } + + public ServiceEventKey getServiceEventKey() { + return serviceEventKey; + } + + public ErrorCode getErrorCode() { + return errorCode; + } + + public String getMessage() { + return message; + } + } + + /** + * 检查该应答是否合法,不合法则走异常流程 + * + * @param response 服务端应答 + * @return 合法返回true,否则返回false + */ + private ValidResult validMessage(ResponseProto.DiscoverResponse response) { + ErrorCode errorCode = ErrorCode.Success; + if (response.hasCode()) { + errorCode = ServerCodes.convertServerErrorToRpcError(response.getCode().getValue()); + } + ServiceProto.Service service = response.getService(); + if (null == service || StringUtils.isEmpty(service.getNamespace().getValue()) || StringUtils + .isEmpty(service.getName().getValue())) { + return new ValidResult(null, ErrorCode.INVALID_SERVER_RESPONSE, + "service is empty, response text is " + response.toString()); + } + EventType eventType = GrpcUtil.buildEventType(response.getType()); + if (eventType == EventType.UNKNOWN) { + return new ValidResult(null, ErrorCode.INVALID_SERVER_RESPONSE, + "invalid event type " + response.getType()); + } + ServiceEventKey serviceEventKey = new ServiceEventKey( + new ServiceKey(service.getNamespace().getValue(), service.getName().getValue()), eventType); + if (errorCode == ErrorCode.SERVER_ERROR) { + //返回了500错误 + return new ValidResult(serviceEventKey, errorCode, "invalid event type " + response.getType()); + } + + return new ValidResult(serviceEventKey, ErrorCode.Success, ""); + } + + + /** + * 异常回调 + * + * @param validResult 异常 + */ + public void exceptionCallback(ValidResult validResult) { + this.closeStream(false); + LOG.error("[ServerConnector]exceptionCallback: errCode {}, info {}, serviceEventKey {}", + validResult.getErrorCode(), validResult.getMessage(), validResult.getServiceEventKey()); + //report down + connection.reportFail(); + List notifyTasks = new ArrayList<>(); + synchronized (clientLock) { + ServiceEventKey serviceEventKey = validResult.getServiceEventKey(); + if (null == serviceEventKey) { + if (CollectionUtils.isNotEmpty(pendingTask.values())) { + notifyTasks.addAll(pendingTask.values()); + pendingTask.clear(); + } + } else { + ServiceUpdateTask task = pendingTask.remove(serviceEventKey); + if (null != task) { + notifyTasks.add(task); + } + } + } + for (ServiceUpdateTask notifyTask : notifyTasks) { + notifyTask.retry(); + } + } + + /** + * 正常回调 + * + * @param response 应答说 + */ + public void onNext(ResponseProto.DiscoverResponse response) { + lastRecvTimeMs.set(System.currentTimeMillis()); + ValidResult validResult = validMessage(response); + if (validResult.errorCode != ErrorCode.Success) { + exceptionCallback(validResult); + return; + } + ServiceProto.Service service = response.getService(); + ServiceKey serviceKey = new ServiceKey(service.getNamespace().getValue(), service.getName().getValue()); + EventType eventType = GrpcUtil.buildEventType(response.getType()); + ServiceEventKey serviceEventKey = new ServiceEventKey(serviceKey, eventType); + ServiceUpdateTask updateTask; + synchronized (clientLock) { + updateTask = pendingTask.remove(serviceEventKey); + } + if (null == updateTask) { + LOG.error("[ServerConnector]callback not found for:{}", TextFormat.shortDebugString(service)); + return; + } + if (updateTask.getTaskType() == Type.FIRST) { + LOG.info("[ServerConnector]receive response for {}", serviceEventKey); + } else { + LOG.debug("[ServerConnector]receive response for {}", serviceEventKey); + } + PolarisException error; + if (!response.hasCode() || response.getCode().getValue() == ServerCodes.EXECUTE_SUCCESS) { + error = null; + } else { + int respCode = response.getCode().getValue(); + String info = response.getInfo().getValue(); + error = ServerErrorResponseException.build(respCode, + String.format("[ServerConnector]code %d, fail to query service %s from server(%s): %s", respCode, + serviceKey, connection.getConnID(), info)); + } + boolean svcDeleted = updateTask.notifyServerEvent(new ServerEvent(serviceEventKey, response, error)); + if (!svcDeleted) { + updateTask.addUpdateTaskSet(); + } + } + + @Override + public void onError(Throwable throwable) { + exceptionCallback(new ValidResult(null, ErrorCode.NETWORK_ERROR, + String.format("stream %s received error from server(%s), error is %s", getReqId(), + connection.getConnID().toString(), throwable.getMessage()))); + } + + @Override + public void onCompleted() { + exceptionCallback(new ValidResult(null, ErrorCode.NETWORK_ERROR, + String.format("stream %s EOF by server(%s)", getReqId(), connection.getConnID().toString()))); + } + + /** + * 同步清理过期的流对象 + */ + public void syncCloseExpireStream() { + synchronized (clientLock) { + closeExpireStream(); + } + } + + + private boolean closeExpireStream() { + //检查是否过期 + long lastRecvMs = lastRecvTimeMs.get(); + long nowMs = System.currentTimeMillis(); + long connIdleTime; + if (lastRecvMs == 0) { + //如果没有收到过消息,就通过创建时间来判断 + connIdleTime = nowMs - createTimeMs; + } else { + connIdleTime = nowMs - lastRecvMs; + } + if (connIdleTime >= connectionIdleTimeoutMs) { + //连接已经过期 + closeStream(true); + return true; + } + return false; + } + + /** + * checkAvailable + * + * @param serviceUpdateTask 更新任务 + * @return 是否可用 + */ + public boolean checkAvailable(ServiceUpdateTask serviceUpdateTask) { + if (isEndStream()) { + return false; + } + synchronized (clientLock) { + if (isEndStream()) { + return false; + } + if (closeExpireStream()) { + return false; + } + ServiceEventKey serviceEventKey = serviceUpdateTask.getServiceEventKey(); + ServiceUpdateTask lastUpdateTask = pendingTask.get(serviceEventKey); + if (null != lastUpdateTask) { + LOG.warn("[ServerConnector]pending task {} has been overwritten", lastUpdateTask); + } + pendingTask.put(serviceEventKey, serviceUpdateTask); + } + return true; + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/AbstractCacheHandler.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/AbstractCacheHandler.java new file mode 100644 index 000000000..24238b874 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/AbstractCacheHandler.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.plugin.registry.CacheHandler; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse; +import java.util.function.Function; + +public abstract class AbstractCacheHandler implements CacheHandler { + + protected abstract String getRevision(DiscoverResponse discoverResponse); + + @Override + public CachedStatus compareMessage(RegistryCacheValue oldValue, Object newValue) { + return CommonHandler.compareMessage(getTargetEventType(), oldValue, (DiscoverResponse) newValue, + new Function() { + @Override + public String apply(DiscoverResponse discoverResponse) { + return getRevision(discoverResponse); + } + }); + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java new file mode 100644 index 000000000..fb33bfa8a --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CircuitBreakCacheHandler.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CircuitBreaker; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; + +public class CircuitBreakCacheHandler extends AbstractCacheHandler { + + @Override + public EventType getTargetEventType() { + return EventType.CIRCUIT_BREAKING; + } + + @Override + protected String getRevision(DiscoverResponse discoverResponse) { + CircuitBreaker circuitBreaker = discoverResponse.getCircuitBreaker(); + if (null == circuitBreaker) { + return ""; + } + return circuitBreaker.getRevision().getValue(); + } + + @Override + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + DiscoverResponse discoverResponse = (DiscoverResponse) newValue; + CircuitBreaker circuitBreaker = discoverResponse.getCircuitBreaker(); + String revision = ""; + if (null != circuitBreaker) { + revision = circuitBreaker.getRevision().getValue(); + } + return new ServiceRuleByProto(circuitBreaker, revision, isCacheLoaded, getTargetEventType()); + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CommonHandler.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CommonHandler.java new file mode 100644 index 000000000..d51a40a47 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/CommonHandler.java @@ -0,0 +1,83 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.plugin.registry.CacheHandler.CachedStatus; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse; +import com.tencent.polaris.client.pb.ServiceProto.Service; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CommonHandler { + + private static final Logger LOG = LoggerFactory.getLogger(CommonHandler.class); + + private static final String emptyReplaceHolder = ""; + + /** + * 比较消息 + * + * @param eventType 类型 + * @param oldValue 旧值 + * @param discoverResponse proto应答 + * @param getRevision 获取版本号 + * @return 状态 + */ + public static CachedStatus compareMessage(EventType eventType, RegistryCacheValue oldValue, + DiscoverResponse discoverResponse, Function getRevision) { + Service service = discoverResponse.getService(); + ServiceEventKey serviceEventKey = new ServiceEventKey( + new ServiceKey(service.getNamespace().getValue(), service.getName().getValue()), eventType); + //判断server的错误码,是否未变更 + if (discoverResponse.getCode().getValue() == ServerCodes.DATA_NO_CHANGE) { + if (null == oldValue) { + return CachedStatus.CacheEmptyButNoData; + } + return CachedStatus.CacheNotChanged; + } + String newRevision = getRevision.apply(discoverResponse); + String oldRevision; + boolean oldLoadedFromFile = false; + CachedStatus cachedStatus; + if (null == oldValue) { + oldRevision = emptyReplaceHolder; + cachedStatus = CachedStatus.CacheNotExists; + } else { + oldLoadedFromFile = oldValue.isLoadedFromFile(); + oldRevision = oldValue.getRevision(); + cachedStatus = oldRevision.equals(newRevision) && !oldLoadedFromFile ? CachedStatus.CacheNotChanged + : CachedStatus.CacheChanged; + } + if (cachedStatus != CachedStatus.CacheNotChanged) { + LOG.info("resource {} has updated, compare status {}, old revision is {}, old loadedFromFile is {}, " + + "new revision is {}", + serviceEventKey, cachedStatus, oldRevision, oldLoadedFromFile, newRevision); + } else { + LOG.debug("resource {} is not updated, compare status {}, old revision is {}, old loadedFromFile is {}, " + + "new revision is {}", + serviceEventKey, cachedStatus, oldRevision, oldLoadedFromFile, newRevision); + } + return cachedStatus; + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RateLimitingCacheHandler.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RateLimitingCacheHandler.java new file mode 100644 index 000000000..64689e88d --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RateLimitingCacheHandler.java @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc.codec; + +import com.google.protobuf.StringValue; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.client.pb.RateLimitProto.RateLimit; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; +import java.util.ArrayList; +import java.util.List; + +public class RateLimitingCacheHandler extends AbstractCacheHandler { + + @Override + public EventType getTargetEventType() { + return EventType.RATE_LIMITING; + } + + @Override + protected String getRevision(DiscoverResponse discoverResponse) { + RateLimit rateLimit = discoverResponse.getRateLimit(); + if (null == rateLimit) { + return ""; + } + return rateLimit.getRevision().getValue(); + } + + @Override + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + DiscoverResponse discoverResponse = (DiscoverResponse) newValue; + RateLimit rateLimit = discoverResponse.getRateLimit(); + String revision = getRevision(discoverResponse); + List rulesList = rateLimit.getRulesList(); + //需要做一次排序,PB中的数据不可变,需要单独构建一份 + List sortedRules = new ArrayList<>(rulesList); + sortedRules.sort((o1, o2) -> { + if (o1.getPriority().getValue() != o2.getPriority().getValue()) { + return o1.getPriority().getValue() - o2.getPriority().getValue(); + } + return o1.getId().getValue().compareTo(o2.getId().getValue()); + }); + RateLimit newRateLimit = RateLimit.newBuilder().addAllRules(sortedRules) + .setRevision(StringValue.newBuilder().setValue(revision).build()).build(); + return new ServiceRuleByProto(newRateLimit, revision, isCacheLoaded, getTargetEventType()); + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java new file mode 100644 index 000000000..9e2eb62f8 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/RoutingCacheHandler.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse; +import com.tencent.polaris.client.pb.RoutingProto.Routing; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; + +public class RoutingCacheHandler extends AbstractCacheHandler { + + @Override + public EventType getTargetEventType() { + return EventType.ROUTING; + } + + @Override + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + DiscoverResponse discoverResponse = (DiscoverResponse) newValue; + Routing routing = discoverResponse.getRouting(); + String revision = ""; + if (null != routing) { + revision = routing.getRevision().getValue(); + } + return new ServiceRuleByProto(routing, revision, isCacheLoaded, getTargetEventType()); + } + + @Override + protected String getRevision(DiscoverResponse discoverResponse) { + Routing routing = discoverResponse.getRouting(); + if (null == routing) { + return ""; + } + return routing.getRevision().getValue(); + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java new file mode 100644 index 000000000..e9a39a150 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/java/com/tencent/polaris/plugins/connector/grpc/codec/ServiceInstancesCacheHandler.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.connector.grpc.codec; + +import com.tencent.polaris.api.plugin.registry.CacheHandler; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse; +import com.tencent.polaris.client.pojo.ServiceInstancesByProto; +import java.util.function.Function; + +public class ServiceInstancesCacheHandler implements CacheHandler { + + @Override + public EventType getTargetEventType() { + return EventType.INSTANCE; + } + + @Override + public CachedStatus compareMessage(RegistryCacheValue oldValue, Object newValue) { + DiscoverResponse discoverResponse = (DiscoverResponse) newValue; + return CommonHandler.compareMessage(getTargetEventType(), oldValue, discoverResponse, + new Function() { + @Override + public String apply(DiscoverResponse discoverResponse) { + return discoverResponse.getService().getRevision().getValue(); + } + }); + } + + @Override + public RegistryCacheValue messageToCacheValue(RegistryCacheValue oldValue, Object newValue, boolean isCacheLoaded) { + DiscoverResponse discoverResponse = (DiscoverResponse) newValue; + ServiceInstancesByProto oldServiceInstances = null; + if (null != oldValue) { + oldServiceInstances = (ServiceInstancesByProto) oldValue; + } + return new ServiceInstancesByProto(discoverResponse, oldServiceInstances, isCacheLoaded); + } +} diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler new file mode 100644 index 000000000..2b6fbfcaf --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.CacheHandler @@ -0,0 +1,4 @@ +com.tencent.polaris.plugins.connector.grpc.codec.ServiceInstancesCacheHandler +com.tencent.polaris.plugins.connector.grpc.codec.RoutingCacheHandler +com.tencent.polaris.plugins.connector.grpc.codec.CircuitBreakCacheHandler +com.tencent.polaris.plugins.connector.grpc.codec.RateLimitingCacheHandler \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.server.ServerConnector b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.server.ServerConnector new file mode 100644 index 000000000..200a0ce7a --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/connector-polaris-grpc/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.server.ServerConnector @@ -0,0 +1 @@ +com.tencent.polaris.plugins.connector.grpc.GrpcConnector \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/connector/pom.xml b/polaris-plugins/polaris-plugins-client/connector/pom.xml new file mode 100644 index 000000000..aa7bdefa2 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/connector/pom.xml @@ -0,0 +1,19 @@ + + + + polaris-plugins-client + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + connector + pom + + connector-polaris-grpc + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/pom.xml b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/pom.xml new file mode 100644 index 000000000..7d17aef8e --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/pom.xml @@ -0,0 +1,16 @@ + + + + flow-cache + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + flow-cache-expired + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/ExpiredFlowCache.java b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/ExpiredFlowCache.java new file mode 100644 index 000000000..4753fdd90 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/ExpiredFlowCache.java @@ -0,0 +1,151 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.flow.cache.expired; + +import com.tencent.polaris.api.config.global.FlowCacheConfig; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import com.tencent.polaris.client.util.NamedThreadFactory; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.regex.Pattern; + +@SuppressWarnings("unchecked") +public class ExpiredFlowCache implements FlowCache { + + private final Map regexPatterns = new ConcurrentHashMap<>(); + + private final ThreadLocal, Queue>> threadLocalValue = new ThreadLocal<>(); + + private final Map pluginValues = new ConcurrentHashMap<>(); + + private ScheduledExecutorService expireExecutor; + + private long expireIntervalMs; + + private boolean enable; + + @Override + public Pattern loadOrStoreCompiledRegex(String regexStr) { + if (!enable) { + return Pattern.compile(regexStr); + } + PatternCacheItem patternCacheItem = regexPatterns + .computeIfAbsent(regexStr, s -> new PatternCacheItem(Pattern.compile(s))); + patternCacheItem.updateLastAccessTimeMs(); + return patternCacheItem.getPattern(); + } + + private T createObject(Class clazz) { + try { + return clazz.newInstance(); + } catch (Exception e) { + throw new RuntimeException("fail to create object with type " + clazz.getCanonicalName(), e); + } + } + + @Override + public T borrowThreadCacheObject(Class clazz) { + if (!enable) { + return createObject(clazz); + } + Map, Queue> classQueueMap = threadLocalValue.get(); + if (null == classQueueMap) { + classQueueMap = new WeakHashMap<>(); + threadLocalValue.set(classQueueMap); + } + Queue objects = classQueueMap.computeIfAbsent(clazz, k -> new LinkedList<>()); + T object = (T) objects.poll(); + if (null != object) { + return object; + } + return createObject(clazz); + } + + @Override + public void giveBackThreadCacheObject(T object) { + if (!enable) { + return; + } + Class clazz = object.getClass(); + Map, Queue> classQueueMap = threadLocalValue.get(); + if (null == classQueueMap) { + classQueueMap = new WeakHashMap<>(); + threadLocalValue.set(classQueueMap); + } + Queue objects = classQueueMap.computeIfAbsent(clazz, k -> new LinkedList<>()); + objects.offer(object); + } + + @Override + public T loadPluginCacheObject(int pluginIdx, Object key, Function createFunc) { + PluginCacheValues cacheValues = pluginValues.get(pluginIdx); + if (null == cacheValues) { + cacheValues = pluginValues.computeIfAbsent(pluginIdx, + idx -> new PluginCacheValues()); + } + return cacheValues.getOrCreateValue(key, createFunc); + } + + @Override + public String getName() { + return FlowCacheConfig.DEFAULT_FLOW_CACHE_NAME; + } + + @Override + public PluginType getType() { + return PluginTypes.FLOW_CACHE.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + enable = ctx.getConfig().getGlobal().getSystem().getFlowCache().isEnable(); + expireIntervalMs = ctx.getConfig().getGlobal().getSystem().getFlowCache().getExpireInterval(); + expireExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(getName())); + } + + @Override + public void postContextInit(Extensions ctx) throws PolarisException { + expireExecutor.scheduleWithFixedDelay(new Runnable() { + @Override + public void run() { + for (PluginCacheValues pluginCacheValues : pluginValues.values()) { + pluginCacheValues.expireValues(expireIntervalMs); + } + } + }, expireIntervalMs, expireIntervalMs, TimeUnit.MILLISECONDS); + } + + @Override + public void destroy() { + ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{expireExecutor}); + } +} diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/FlowCacheItem.java b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/FlowCacheItem.java new file mode 100644 index 000000000..742e42dd7 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/FlowCacheItem.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.flow.cache.expired; + +import java.util.concurrent.atomic.AtomicLong; + +public class FlowCacheItem { + + private final AtomicLong lastAccessTimeMs = new AtomicLong(); + + /** + * 获取最近一次访问时间 + * + * @return 访问时间 + */ + public long getLastAccessTimeMs() { + return lastAccessTimeMs.get(); + } + + /** + * 更新最近一次访问时间 + */ + public void updateLastAccessTimeMs() { + long lastAccessTime = lastAccessTimeMs.get(); + lastAccessTimeMs.compareAndSet(lastAccessTime, System.currentTimeMillis()); + } +} diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PatternCacheItem.java b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PatternCacheItem.java new file mode 100644 index 000000000..e703a20f5 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PatternCacheItem.java @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.flow.cache.expired; + +import java.util.regex.Pattern; + +public class PatternCacheItem extends FlowCacheItem { + + private final Pattern pattern; + + public PatternCacheItem(Pattern pattern) { + super(); + this.pattern = pattern; + } + + public Pattern getPattern() { + return pattern; + } +} diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheItem.java b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheItem.java new file mode 100644 index 000000000..844a6fdae --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheItem.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.flow.cache.expired; + +import java.util.concurrent.atomic.AtomicLong; + +public class PluginCacheItem { + + private final Object key; + + private final Object value; + + private final AtomicLong lastAccessTime = new AtomicLong(); + + public PluginCacheItem(Object key, Object value) { + this.key = key; + this.value = value; + lastAccessTime.set(System.currentTimeMillis()); + } + + public Object getValue() { + lastAccessTime.set(System.currentTimeMillis()); + return value; + } + + public Object getKey() { + return key; + } + + public long getLastAccessTime() { + return lastAccessTime.get(); + } +} diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheValues.java b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheValues.java new file mode 100644 index 000000000..01af66e39 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/java/com/tencent/polaris/plugins/flow/cache/expired/PluginCacheValues.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.flow.cache.expired; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +public class PluginCacheValues { + + private final Map values = new ConcurrentHashMap<>(); + + @SuppressWarnings("unchecked") + public T getOrCreateValue(Object key, Function createFunc) { + PluginCacheItem pluginCacheItem = values.get(key); + if (null != pluginCacheItem) { + return (T) pluginCacheItem.getValue(); + } + pluginCacheItem = values.computeIfAbsent(key, new Function() { + + @Override + public PluginCacheItem apply(Object o) { + return new PluginCacheItem(o, createFunc.apply(o)); + } + }); + return (T) pluginCacheItem.getValue(); + } + + public void expireValues(long expireDuration) { + long curTimeMs = System.currentTimeMillis(); + for (PluginCacheItem item : values.values()) { + if (curTimeMs - item.getLastAccessTime() >= expireDuration) { + values.remove(item.getKey()); + } + } + } +} diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.cache.FlowCache b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.cache.FlowCache new file mode 100644 index 000000000..be5d24128 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/flow-cache-expired/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.cache.FlowCache @@ -0,0 +1 @@ +com.tencent.polaris.plugins.flow.cache.expired.ExpiredFlowCache \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/flow-cache/pom.xml b/polaris-plugins/polaris-plugins-client/flow-cache/pom.xml new file mode 100644 index 000000000..61c7b6efc --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/flow-cache/pom.xml @@ -0,0 +1,19 @@ + + + + polaris-plugins-client + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + flow-cache + pom + + flow-cache-expired + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/pom.xml b/polaris-plugins/polaris-plugins-client/pom.xml new file mode 100644 index 000000000..ee3f005a6 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/pom.xml @@ -0,0 +1,38 @@ + + + + polaris-plugins + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins-client + pom + + connector + resource-cache + flow-cache + + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/resource-cache/pom.xml b/polaris-plugins/polaris-plugins-client/resource-cache/pom.xml new file mode 100644 index 000000000..7f2b1d442 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/resource-cache/pom.xml @@ -0,0 +1,19 @@ + + + + polaris-plugins-client + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + resource-cache + + pom + + resource-cache-memory + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/pom.xml b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/pom.xml new file mode 100644 index 000000000..49caf96b8 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/pom.xml @@ -0,0 +1,22 @@ + + + + resource-cache + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + resource-cache-memory + + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/CacheObject.java b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/CacheObject.java new file mode 100644 index 000000000..b9efb957c --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/CacheObject.java @@ -0,0 +1,299 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.local.cache.memory; + +import com.google.protobuf.Message; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.registry.CacheHandler; +import com.tencent.polaris.api.plugin.registry.CacheHandler.CachedStatus; +import com.tencent.polaris.api.plugin.registry.EventCompleteNotifier; +import com.tencent.polaris.api.plugin.registry.ResourceEventListener; +import com.tencent.polaris.api.plugin.server.EventHandler; +import com.tencent.polaris.api.plugin.server.ServerEvent; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.client.pojo.ServiceInstancesByProto; +import com.tencent.polaris.client.pojo.ServiceRuleByProto; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 服务缓存的封装类型 + * + * @author andrewshan + * @date 2019/8/22 + */ +public class CacheObject implements EventHandler { + + public static final ServiceInstancesByProto EMPTY_SERVICES = new ServiceInstancesByProto(); + public static final ServiceRuleByProto EMPTY_SERVICE_RULE = new ServiceRuleByProto(); + + private static final Logger LOG = LoggerFactory.getLogger(CacheObject.class); + + private final AtomicReference value = new AtomicReference<>(); + + private final ServiceEventKey svcEventKey; + + private final CacheHandler cacheHandler; + + private final InMemoryRegistry registry; + + //用于守护notifier的变更 + private final Object lock = new Object(); + + private final List notifiers = new ArrayList<>(); + + private final AtomicLong lastAccessTimeMs = new AtomicLong(0); + + private final long createTime; + + //是否经过远程更新 + private final AtomicBoolean remoteUpdated = new AtomicBoolean(false); + + //是否已经注册了connector监听 + private final AtomicBoolean registered = new AtomicBoolean(false); + + //这个服务对象是否已经被删除,防止connector收到多次服务不存在的消息,导致重复删除 + private final AtomicBoolean deleted = new AtomicBoolean(false); + + //是否已经触发了资源新增回调 + private final AtomicBoolean notifyResourceAdded = new AtomicBoolean(false); + + public CacheObject(CacheHandler cacheHandler, ServiceEventKey svcEventKey, InMemoryRegistry registry) { + this.svcEventKey = svcEventKey; + this.registry = registry; + this.cacheHandler = cacheHandler; + long nowMs = System.currentTimeMillis(); + createTime = nowMs; + setLastAccessTimeMs(nowMs); + } + + public CacheObject(CacheHandler cacheHandler, ServiceEventKey svcEventKey, InMemoryRegistry registry, + Message initValue) { + this.svcEventKey = svcEventKey; + this.registry = registry; + this.cacheHandler = cacheHandler; + long nowMs = System.currentTimeMillis(); + createTime = nowMs; + setLastAccessTimeMs(nowMs); + RegistryCacheValue registryCacheValue = cacheHandler.messageToCacheValue(null, initValue, true); + value.set(registryCacheValue); + } + + /** + * 创建时间 + * + * @return long + */ + public long getCreateTime() { + return createTime; + } + + /** + * 返回是否已经经过了远程更新 + * + * @return remoteUpdated + */ + public boolean isRemoteUpdated() { + return remoteUpdated.get(); + } + + /** + * 通知并清理所有的监听器 + * + * @param error 异常 + */ + private void notifyEvent(Throwable error) { + if (CollectionUtils.isEmpty(notifiers)) { + return; + } + notifiers.forEach(notifier -> { + if (null != error) { + notifier.completeExceptionally(svcEventKey, error); + } else { + notifier.complete(svcEventKey); + } + }); + notifiers.clear(); + } + + /** + * 增加监听器 + * + * @param notifier 实例事件监听器 + */ + public void addNotifier(EventCompleteNotifier notifier) { + if (checkNotifyNow(notifier)) { + return; + } + synchronized (lock) { + if (checkNotifyNow(notifier)) { + return; + } + notifiers.add(notifier); + } + } + + /** + * 加载缓存值 + * + * @param updateVisitTime 是否更新访问时间 + * @return 值对象 + */ + public RegistryCacheValue loadValue(boolean updateVisitTime) { + if (updateVisitTime) { + lastAccessTimeMs.set(System.currentTimeMillis()); + } + RegistryCacheValue registryCacheValue = value.get(); + if (null == registryCacheValue) { + return null; + } + if (notifyResourceAdded.compareAndSet(false, true)) { + Collection resourceEventListeners = registry.getResourceEventListeners(); + if (!CollectionUtils.isEmpty(resourceEventListeners)) { + for (ResourceEventListener listener : resourceEventListeners) { + listener.onResourceAdd(svcEventKey, registryCacheValue); + } + } + } + return registryCacheValue; + } + + @Override + public boolean onEventUpdate(ServerEvent event) { + ServiceEventKey serviceEventKey = event.getServiceEventKey(); + PolarisException error = event.getError(); + remoteUpdated.set(true); + boolean svcDeleted = false; + Collection resourceEventListeners = registry.getResourceEventListeners(); + if (null != error) { + //收取消息有出错 + RegistryCacheValue registryCacheValue = loadValue(false); + //没有服务信息直接删除 + if (error.getCode() == ErrorCode.SERVICE_NOT_FOUND) { + if (deleted.compareAndSet(false, true)) { + registry.removeCache(serviceEventKey); + for (ResourceEventListener listener : resourceEventListeners) { + listener.onResourceDeleted(svcEventKey, registryCacheValue); + } + svcDeleted = true; + } + } else { + LOG.error(String.format("received error notify for service %s", serviceEventKey), error); + } + } else { + Object message = event.getValue(); + RegistryCacheValue cachedValue = value.get(); + CachedStatus cachedStatus = cacheHandler.compareMessage(cachedValue, message); + if (cachedStatus == CachedStatus.CacheChanged || cachedStatus == CachedStatus.CacheNotExists) { + LOG.info("OnServiceUpdate: cache {} is pending to update", svcEventKey); + this.registry.saveMessageToFile(serviceEventKey, (Message) message); + RegistryCacheValue newCachedValue = cacheHandler.messageToCacheValue(cachedValue, message, false); + setValue(newCachedValue); + if (cachedStatus == CachedStatus.CacheChanged) { + for (ResourceEventListener listener : resourceEventListeners) { + listener.onResourceUpdated(svcEventKey, cachedValue, newCachedValue); + } + } + } else if (cachedStatus == CachedStatus.CacheEmptyButNoData) { + LOG.error("OnServiceUpdate: {} is empty, but discover returns no data", svcEventKey); + } + boolean newRemoteCache = null == cachedValue || cachedValue.isLoadedFromFile(); + if (newRemoteCache && serviceEventKey.getEventType() == EventType.INSTANCE) { + //设置就绪状态 + registry.setServerServiceReady(serviceEventKey); + } + } + synchronized (lock) { + notifyEvent(error); + } + return svcDeleted; + } + + private void setValue(RegistryCacheValue registryCacheValue) { + value.set(registryCacheValue); + LOG.info("CacheObject: value for {} is updated, revision {}", svcEventKey, registryCacheValue.getRevision()); + } + + //发起注册,只要一个能够发起成功 + boolean startRegister() { + synchronized (lock) { + return registered.compareAndSet(false, true); + } + } + + //恢复未注册状态 + void resumeUnRegistered(PolarisException e) { + synchronized (lock) { + registered.compareAndSet(true, false); + notifyEvent(e); + } + } + + private boolean checkResourceAvailable() { + RegistryCacheValue registryCacheValue = value.get(); + if (null == registryCacheValue) { + return false; + } + return registryCacheValue.isInitialized() && !registryCacheValue.isLoadedFromFile(); + } + + private boolean checkNotifyNow(EventCompleteNotifier notifier) { + if (checkResourceAvailable()) { + notifier.complete(svcEventKey); + return true; + } + return false; + } + + /** + * 获取最近一次的访问时间 + * + * @return long + */ + public long getLastAccessTimeMs() { + return lastAccessTimeMs.get(); + } + + /** + * 设置最近一次的访问时间 + * + * @param value 访问时间值,单位毫秒 + */ + public void setLastAccessTimeMs(long value) { + lastAccessTimeMs.set(value); + } + + /** + * 获取资源标识 + * + * @return 资源标识 + */ + public ServiceEventKey getServiceEventKey() { + return svcEventKey; + } +} diff --git a/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/InMemoryRegistry.java b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/InMemoryRegistry.java new file mode 100644 index 000000000..68b5dcdde --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/InMemoryRegistry.java @@ -0,0 +1,661 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.local.cache.memory; + +import com.google.protobuf.Message; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.config.plugin.DefaultPlugins; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; +import com.tencent.polaris.api.plugin.registry.CacheHandler; +import com.tencent.polaris.api.plugin.registry.EventCompleteNotifier; +import com.tencent.polaris.api.plugin.registry.InstanceProperty; +import com.tencent.polaris.api.plugin.registry.LocalRegistry; +import com.tencent.polaris.api.plugin.registry.ResourceEventListener; +import com.tencent.polaris.api.plugin.registry.ResourceFilter; +import com.tencent.polaris.api.plugin.registry.ServiceUpdateRequest; +import com.tencent.polaris.api.plugin.server.ServerConnector; +import com.tencent.polaris.api.plugin.server.ServiceEventHandler; +import com.tencent.polaris.api.plugin.stat.CircuitBreakGauge; +import com.tencent.polaris.api.plugin.stat.DefaultCircuitBreakResult; +import com.tencent.polaris.api.plugin.stat.StatInfo; +import com.tencent.polaris.api.plugin.stat.StatReporter; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.DetectResult; +import com.tencent.polaris.api.pojo.InstanceLocalValue; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.ResourcesResponse; +import com.tencent.polaris.client.pb.ResponseProto; +import com.tencent.polaris.client.pojo.InstanceByProto; +import com.tencent.polaris.client.util.NamedThreadFactory; +import com.tencent.polaris.client.util.Utils; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 本地缓存,保存服务端返回的实例信息. + * + * @author andrewshan + * @date 2019/8/22 + */ +public class InMemoryRegistry extends Destroyable implements LocalRegistry { + + private static final Logger LOG = LoggerFactory.getLogger(InMemoryRegistry.class); + + /** + * 默认首次发现discovery服务重试间隔 + */ + private static final long defaultDiscoverServiceRetryIntervalMs = 5000; + + private ServerConnector connector; + + /** + * 存储同步下来的服务规则信息 + */ + private final Map resourceMap = new ConcurrentHashMap<>(); + + /** + * 资源变更时间监听器 + */ + private final List resourceEventListeners = new CopyOnWriteArrayList<>(); + + /** + * 存储使用到的服务列表数据 + */ + private final Map services = new ConcurrentHashMap<>(); + + /** + * 服务数据持久化处理器 + */ + private MessagePersistHandler messagePersistHandler; + + /** + * 持久化线程 + */ + private ExecutorService persistExecutor; + + /** + * 超时淘汰线程 + */ + private ScheduledExecutorService expireExecutor; + + /** + * 拉取系统服务的线程 + */ + private ExecutorService serverServicesDiscoverExecutor; + + /** + * 缓存处理器,通过SPI加载 + */ + private final Map cacheHandlers = new HashMap<>(); + + /** + * 系统服务信息 + */ + private final Map serverServiceMap = new HashMap<>(); + + /** + * 服务刷新时延 + */ + private long serviceRefreshIntervalMs; + + /** + * 启用本地文件缓存 + */ + private boolean persistEnable; + + /** + * 缓存淘汰时间 + */ + private long serviceExpireTimeMs; + + /** + * 是否有独立的服务发现集群 + */ + private boolean hasDiscoverCluster = false; + + private Collection statPlugins; + + @Override + public Set getServices() { + return services.keySet(); + } + + @Override + public ServiceRule getServiceRule(ResourceFilter filter) { + RegistryCacheValue resourceCache = getResource(filter.getSvcEventKey(), filter.isIncludeCache(), + filter.isInternalRequest()); + if (null == resourceCache) { + return CacheObject.EMPTY_SERVICE_RULE; + } + return (ServiceRule) resourceCache; + } + + private RegistryCacheValue getResource(ServiceEventKey svcEventKey, boolean includeCache, boolean internalRequest) { + CacheObject cacheObject = resourceMap.get(svcEventKey); + if (null == cacheObject) { + return null; + } + RegistryCacheValue registryCacheValue = cacheObject.loadValue(!internalRequest); + if (null == registryCacheValue) { + return null; + } + if (cacheObject.isRemoteUpdated() || includeCache) { + return registryCacheValue; + } + return null; + } + + @Override + public ServiceInstances getInstances(ResourceFilter filter) { + RegistryCacheValue resourceCache = getResource(filter.getSvcEventKey(), filter.isIncludeCache(), + filter.isInternalRequest()); + if (null == resourceCache) { + return CacheObject.EMPTY_SERVICES; + } + return (ServiceInstances) resourceCache; + } + + @Override + public void loadServiceRule(ServiceEventKey svcEventKey, EventCompleteNotifier notifier) throws PolarisException { + loadRemoteValue(svcEventKey, notifier); + } + + /** + * 获取connector + * + * @return 资源connector + */ + private ServerConnector getConnector() { + return connector; + } + + /** + * 加载资源 + * + * @param svcEventKey 服务资源名 + * @param notifier 通知器 + * @throws PolarisException 异常 + */ + private void loadRemoteValue(ServiceEventKey svcEventKey, EventCompleteNotifier notifier) throws PolarisException { + checkDestroyed(); + CacheHandler handler = cacheHandlers.get(svcEventKey.getEventType()); + if (null == handler) { + throw new PolarisException(ErrorCode.INTERNAL_ERROR, + String.format("[LocalRegistry] unRegistered resource type %s", svcEventKey.getEventType())); + } + CacheObject cacheObject = resourceMap + .computeIfAbsent(svcEventKey, + serviceEventKey -> new CacheObject(handler, svcEventKey, InMemoryRegistry.this) + ); + //添加监听器 + cacheObject.addNotifier(notifier); + //触发往serverConnector注册 + if (cacheObject.startRegister()) { + LOG.info("[LocalRegistry]start to register service handler for {}", svcEventKey); + try { + connector.registerServiceHandler( + enhanceServiceEventHandler(new ServiceEventHandler(svcEventKey, cacheObject))); + } catch (Throwable e) { + PolarisException polarisException; + if (e instanceof PolarisException) { + polarisException = (PolarisException) e; + } else { + polarisException = new PolarisException(ErrorCode.INTERNAL_ERROR, + String.format("exception occurs while registering service handler for %s", svcEventKey)); + } + cacheObject.resumeUnRegistered(polarisException); + throw polarisException; + } + if (svcEventKey.getEventType() == EventType.INSTANCE) { + //注册了监听后,认为是被用户需要的服务,加入serviceSet + services.put(svcEventKey.getServiceKey(), true); + } + } + } + + private ServiceEventHandler enhanceServiceEventHandler(ServiceEventHandler eventHandler) { + ServiceKey serviceKey = eventHandler.getServiceEventKey().getServiceKey(); + ServerServiceInfo info = serverServiceMap.get(serviceKey); + if (null != info) { + //系统服务 + eventHandler.setRefreshInterval(info.getRefreshIntervalMs()); + if (info.getClusterType() != ClusterType.SERVICE_DISCOVER_CLUSTER) { + eventHandler.setTargetCluster(ClusterType.SERVICE_DISCOVER_CLUSTER); + } else { + eventHandler.setTargetCluster(ClusterType.BUILTIN_CLUSTER); + } + } else { + eventHandler.setRefreshInterval(serviceRefreshIntervalMs); + eventHandler.setTargetCluster(ClusterType.SERVICE_DISCOVER_CLUSTER); + } + return eventHandler; + } + + @Override + public void loadInstances(ServiceEventKey svcEventKey, EventCompleteNotifier notifier) throws PolarisException { + loadRemoteValue(svcEventKey, notifier); + } + + @SuppressWarnings("unchecked") + private void onCircuitBreakStatus(Object value, InstanceLocalValue instanceLocalValue, Instance instance) { + Map statusMap = (Map) value; + if (MapUtils.isNotEmpty(statusMap)) { + for (Map.Entry entry : statusMap.entrySet()) { + instanceLocalValue.setCircuitBreakerStatus(entry.getKey(), entry.getValue()); + reportCircuitStat(entry, instance); + } + } + } + + private void reportCircuitStat(Entry dimensionEntry, + Instance instance) { + if (null != statPlugins) { + try { + for (Plugin statPlugin : statPlugins) { + if (statPlugin instanceof StatReporter) { + StatInfo info = new StatInfo(); + info.setCircuitBreakGauge(convertToCircuitBreakGauge(dimensionEntry, instance)); + ((StatReporter) statPlugin).reportStat(info); + } + } + } catch (Exception ex) { + LOG.info("circuit breaker report encountered exception, e: {}", ex.getMessage()); + } + } + } + + private CircuitBreakGauge convertToCircuitBreakGauge(Entry dimensionEntry, + Instance instance) { + DefaultCircuitBreakResult result = new DefaultCircuitBreakResult(); + result.setMethod(dimensionEntry.getKey().getMethod()); + result.setCallerService(dimensionEntry.getKey().getCallerService()); + result.setCircuitBreakStatus(dimensionEntry.getValue()); + result.setHost(instance.getHost()); + result.setPort(instance.getPort()); + result.setInstanceId(instance.getId()); + result.setService(instance.getService()); + result.setNamespace(instance.getNamespace()); + return result; + } + + @Override + public void updateInstances(ServiceUpdateRequest request) { + Collection instanceProperties = request.getProperties(); + if (CollectionUtils.isEmpty(instanceProperties)) { + return; + } + RegistryCacheValue cacheValue = getResource(new ServiceEventKey(request.getServiceKey(), EventType.INSTANCE), + true, true); + if (null == cacheValue) { + //服务不存在,忽略 + return; + } + for (InstanceProperty instanceProperty : instanceProperties) { + InstanceByProto instance = (InstanceByProto) instanceProperty.getInstance(); + InstanceLocalValue instanceLocalValue = instance.getInstanceLocalValue(); + Map properties = instanceProperty.getProperties(); + LOG.info("update instance properties for instance {}, properties {}", instance.getId(), properties); + for (Map.Entry entry : properties.entrySet()) { + switch (entry.getKey()) { + case InstanceProperty.PROPERTY_CIRCUIT_BREAKER_STATUS: + onCircuitBreakStatus(entry.getValue(), instanceLocalValue, instance); + break; + case InstanceProperty.PROPERTY_DETECT_RESULT: + instanceLocalValue.setDetectResult((DetectResult) entry.getValue()); + break; + default: + break; + } + } + } + } + + @Override + public void registerResourceListener(ResourceEventListener listener) { + resourceEventListeners.add(listener); + } + + public Collection getResourceEventListeners() { + return resourceEventListeners; + } + + @Override + public String getName() { + return DefaultPlugins.LOCAL_REGISTRY_IN_MEMORY; + } + + @Override + public PluginType getType() { + return PluginTypes.LOCAL_REGISTRY.getBaseType(); + } + + + @Override + public void init(InitContext ctx) throws PolarisException { + //获取系统服务配置 + Collection serverServices = ctx.getServerServices(); + for (ServerServiceInfo serverServiceInfo : serverServices) { + if (serverServiceInfo.getClusterType() == ClusterType.SERVICE_DISCOVER_CLUSTER) { + hasDiscoverCluster = true; + } + serverServiceMap.put(serverServiceInfo.getServiceKey(), serverServiceInfo); + } + //加载cacheHandler + ServiceLoader handlers = ServiceLoader.load(CacheHandler.class); + for (CacheHandler handler : handlers) { + cacheHandlers.put(handler.getTargetEventType(), handler); + } + //构建协议connector + String protocol = ctx.getConfig().getGlobal().getServerConnector().getProtocol(); + connector = (ServerConnector) ctx.getPlugins().getPlugin(PluginTypes.SERVER_CONNECTOR.getBaseType(), protocol); + //构建基础属性 + String persistDir = ctx.getConfig().getConsumer().getLocalCache().getPersistDir(); + int maxReadRetry = ctx.getConfig().getConsumer().getLocalCache().getPersistMaxReadRetry(); + int maxWriteRetry = ctx.getConfig().getConsumer().getLocalCache().getPersistMaxWriteRetry(); + long retryIntervalMs = ctx.getConfig().getConsumer().getLocalCache().getPersistRetryInterval(); + this.serviceRefreshIntervalMs = ctx.getConfig().getConsumer().getLocalCache().getServiceRefreshInterval(); + boolean configPersistEnable = ctx.getConfig().getConsumer().getLocalCache().isPersistEnable(); + persistEnable = configPersistEnable && StringUtils.isNotBlank(persistDir); + //启动本地缓存 + if (persistEnable) { + messagePersistHandler = new MessagePersistHandler(persistDir, maxWriteRetry, maxReadRetry, retryIntervalMs); + try { + messagePersistHandler.init(); + } catch (IOException e) { + throw new PolarisException(ErrorCode.PLUGIN_ERROR, + String.format("plugin %s init failed", getName()), e); + } + loadFileCache(persistDir); + } + NamedThreadFactory namedThreadFactory = new NamedThreadFactory(getName()); + serviceExpireTimeMs = ctx.getConfig().getConsumer().getLocalCache().getServiceExpireTime(); + persistExecutor = Executors.newSingleThreadExecutor(namedThreadFactory); + expireExecutor = Executors.newSingleThreadScheduledExecutor(namedThreadFactory); + if (hasDiscoverCluster) { + serverServicesDiscoverExecutor = new ThreadPoolExecutor(0, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), + namedThreadFactory); + } + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + expireExecutor.scheduleAtFixedRate(new ExpireTask(), 0, serviceExpireTimeMs, TimeUnit.MILLISECONDS); + if (null != serverServicesDiscoverExecutor) { + serverServicesDiscoverExecutor.execute(new WarmupDiscoverServiceTask(extensions)); + } + statPlugins = extensions.getPlugins().getPlugins(PluginTypes.STAT_REPORTER.getBaseType()); + } + + /** + * 持久化消息 + * + * @param svcEventKey 资源KEY + * @param message 原始消息 + */ + public void saveMessageToFile(ServiceEventKey svcEventKey, Message message) { + if (!persistEnable) { + return; + } + if (!persistExecutor.isShutdown()) { + persistExecutor.execute(new SavePersistTask(svcEventKey, message)); + } + } + + /** + * 删除文件缓存 + * + * @param svcEventKey 资源KEY + */ + private void deleteFileMessage(ServiceEventKey svcEventKey) { + if (!persistEnable) { + return; + } + if (!persistExecutor.isShutdown()) { + persistExecutor.execute(new DeletePersistTask(svcEventKey)); + } + } + + private void loadFileCache(String persistPath) { + LOG.info("start to load local cache files from {}", persistPath); + Map loadCachedServices = messagePersistHandler.loadPersistedServices( + ResponseProto.DiscoverResponse.getDefaultInstance()); + for (Map.Entry entry : loadCachedServices.entrySet()) { + ServiceEventKey svcEventKey = entry.getKey(); + Message message = entry.getValue(); + if (null == message) { + LOG.warn("load local cache, response is null, service event:{}", svcEventKey); + continue; + } + CacheHandler cacheHandler = cacheHandlers.get(svcEventKey.getEventType()); + if (null == cacheHandler) { + LOG.warn("[LocalRegistry]resource type {} not registered, ignore the file", svcEventKey.getEventType()); + continue; + } + CacheObject cacheObject = new CacheObject( + cacheHandler, svcEventKey, this, message); + resourceMap.put(svcEventKey, cacheObject); + } + LOG.info("loaded {} services from local cache", loadCachedServices.size()); + } + + /** + * 清理资源缓存 + * + * @param serviceEventKey 资源KEY + */ + public void removeCache(ServiceEventKey serviceEventKey) { + LOG.info("[LocalRegistry] remove cache for resource {}", serviceEventKey); + try { + getConnector().deRegisterServiceHandler(serviceEventKey); + } catch (PolarisException e) { + LOG.error("[LocalRegistry] fail to deRegisterServiceHandler", e); + } + resourceMap.remove(serviceEventKey); + if (serviceEventKey.getEventType() == EventType.INSTANCE) { + services.remove(serviceEventKey.getServiceKey()); + } + deleteFileMessage(serviceEventKey); + } + + @Override + protected void doDestroy() { + ThreadPoolUtils.waitAndStopThreadPools( + new ExecutorService[]{serverServicesDiscoverExecutor, persistExecutor, expireExecutor,}); + } + + /** + * 持久化任务结构 + */ + private class SavePersistTask implements Runnable { + + final ServiceEventKey svcEventKey; + final Message message; + + SavePersistTask(ServiceEventKey svcEventKey, Message message) { + this.svcEventKey = svcEventKey; + this.message = message; + } + + @Override + public void run() { + InMemoryRegistry.this.messagePersistHandler.saveService(svcEventKey, message); + } + } + + /** + * 删除文件任务结构 + */ + private class DeletePersistTask implements Runnable { + + final ServiceEventKey svcEventKey; + + DeletePersistTask(ServiceEventKey svcEventKey) { + this.svcEventKey = svcEventKey; + } + + @Override + public void run() { + InMemoryRegistry.this.messagePersistHandler.deleteService(svcEventKey); + } + } + + /** + * 缓存淘汰操作 + */ + private class ExpireTask implements Runnable { + + @Override + public void run() { + for (Map.Entry entry : resourceMap.entrySet()) { + //如果当前时间减去最新访问时间没有超过expireTime,那么不用淘汰,继续检查下一个服务 + CacheObject cacheObject = entry.getValue(); + long lastAccessTime = cacheObject.getLastAccessTimeMs(); + if (lastAccessTime == 0) { + continue; + } + long nowMs = System.currentTimeMillis(); + long diffTimeMs = nowMs - lastAccessTime; + if (diffTimeMs < 0) { + //时间发生倒退,则直接更新最近访问时间 + cacheObject.setLastAccessTimeMs(nowMs); + continue; + } + if (diffTimeMs < InMemoryRegistry.this.serviceExpireTimeMs) { + continue; + } + //执行淘汰 + removeCache(entry.getKey()); + } + } + } + + private class WarmupDiscoverServiceTask implements Runnable { + + private final Extensions extensions; + + public WarmupDiscoverServiceTask(Extensions extensions) { + this.extensions = extensions; + } + + private void retryTask() { + Utils.sleepUninterrupted(defaultDiscoverServiceRetryIntervalMs); + ExecutorService serverServicesDiscoverExecutor = InMemoryRegistry.this.serverServicesDiscoverExecutor; + if (null != serverServicesDiscoverExecutor && !serverServicesDiscoverExecutor.isShutdown()) { + serverServicesDiscoverExecutor.execute(new WarmupDiscoverServiceTask(extensions)); + } + + } + + @Override + public void run() { + ServiceKey discoverSvcKey = null; + Map serverServiceMap = InMemoryRegistry.this.serverServiceMap; + for (Entry entry : serverServiceMap.entrySet()) { + if (entry.getValue().getClusterType() == ClusterType.SERVICE_DISCOVER_CLUSTER) { + discoverSvcKey = entry.getKey(); + break; + } + } + if (null == discoverSvcKey) { + LOG.warn("[LocalRegistry] discover service not config"); + return; + } + ServiceEventKey svcEventKey = new ServiceEventKey(discoverSvcKey, EventType.INSTANCE); + DefaultServiceEventKeysProvider provider = new DefaultServiceEventKeysProvider(); + provider.setSvcEventKey(svcEventKey); + DefaultFlowControlParam defaultFlowControlParam = new DefaultFlowControlParam(); + APIConfig apiConfig = extensions.getConfiguration().getGlobal().getAPI(); + defaultFlowControlParam.setTimeoutMs(apiConfig.getTimeout()); + defaultFlowControlParam.setMaxRetry(apiConfig.getMaxRetryTimes()); + defaultFlowControlParam.setRetryIntervalMs(apiConfig.getRetryInterval()); + ResourcesResponse resourcesResponse; + try { + resourcesResponse = BaseFlow + .syncGetResources(extensions, false, provider, defaultFlowControlParam); + } catch (PolarisException e) { + if (e.getCode() == ErrorCode.INVALID_STATE) { + return; + } + LOG.error("[LocalRegistry] fail to fetch server service {}", svcEventKey, e); + retryTask(); + return; + } + ServiceInstances serviceInstances = resourcesResponse.getServiceInstances(svcEventKey); + RegistryCacheValue cacheValue = (RegistryCacheValue) serviceInstances; + if (!cacheValue.isInitialized()) { + retryTask(); + } + } + } + + /** + * 设置系统服务就绪状态 + * + * @param serviceEventKey 资源标识 + */ + public void setServerServiceReady(ServiceEventKey serviceEventKey) { + if (!serverServiceMap.containsKey(serviceEventKey.getServiceKey())) { + return; + } + connector.updateServers(serviceEventKey); + } + +} diff --git a/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/MessagePersistHandler.java b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/MessagePersistHandler.java new file mode 100644 index 000000000..e1677afb4 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/java/com/tencent/polaris/plugins/local/cache/memory/MessagePersistHandler.java @@ -0,0 +1,323 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.local.cache.memory; + +import static java.nio.file.StandardCopyOption.ATOMIC_MOVE; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.util.Utils; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.yaml.snakeyaml.Yaml; + +/** + * 消息持久化处理器,用于持久化PB对象 + * + * @author andrewshan + * @date 2019/9/5 + */ +public class MessagePersistHandler { + + private static final Logger LOG = LoggerFactory.getLogger(MessagePersistHandler.class); + + private static final String CACHE_SUFFIX = ".yaml"; + + private static final Pattern REGEX_PATTERN_SERVICE = Pattern.compile("^svc#.+#.+#.+\\.yaml$"); + + private static final String PATTERN_SERVICE = "svc#%s#%s#%s.yaml"; + + private final File persistDirFile; + + private final String persistDirPath; + + private final int maxWriteRetry; + + private final int maxReadRetry; + + private final long retryInterval; + + private final JsonFormat.Printer printer = JsonFormat.printer(); + + private final JsonFormat.Parser parser = JsonFormat.parser(); + + public MessagePersistHandler( + String persistDirPath, int maxWriteRetry, int maxReadRetry, long retryInterval) { + this.maxReadRetry = maxReadRetry; + this.maxWriteRetry = maxWriteRetry; + this.retryInterval = retryInterval; + this.persistDirPath = Utils.translatePath(persistDirPath); + persistDirFile = new File(this.persistDirPath); + } + + public void init() throws IOException { + try { + if (!persistDirFile.exists() && !persistDirFile.mkdirs()) { + throw new IOException(String.format("fail to create dir %s", persistDirPath)); + } + //检查文件夹是否具备写权限 + if (!Files.isWritable(FileSystems.getDefault().getPath(persistDirPath))) { + throw new IOException(String.format("fail to check permission for dir %s", persistDirPath)); + } + } catch (Throwable e) { + throw new IOException(String.format("fail to check permission for dir %s", persistDirPath), e); + } + } + + /** + * 删除服务缓存数据 + * + * @param svcEventKey 服务标识 + */ + public void deleteService(ServiceEventKey svcEventKey) { + String fileName = serviceKeyToFileName(svcEventKey); + String persistFilePath = persistDirPath + File.separator + fileName; + try { + Files.deleteIfExists(FileSystems.getDefault().getPath(persistFilePath)); + } catch (IOException e) { + LOG.error("fail to delete cache file {}", persistFilePath); + } + String lockFileName = fileName + ".lock"; + String persistFileLockPath = persistDirPath + File.separator + lockFileName; + try { + Files.deleteIfExists(FileSystems.getDefault().getPath(persistFileLockPath)); + } catch (IOException e) { + LOG.error("fail to delete cache lock file {}", persistFileLockPath); + } + } + + /** + * 持久化单个服务实例数据 + * + * @param svcEventKey 服务标识 + * @param message 数据内容 + */ + public void saveService(ServiceEventKey svcEventKey, Message message) { + int retryTimes = 0; + LOG.info("start to save service {}", svcEventKey); + while (retryTimes <= maxWriteRetry) { + retryTimes++; + Path path = doSaveService(svcEventKey, message); + if (null == path) { + continue; + } + LOG.info("end to save service {} to {}", svcEventKey, path); + return; + } + LOG.error("fail to persist service {} after retry {}", svcEventKey, retryTimes); + } + + private static String serviceKeyToFileName(ServiceEventKey svcEventKey) { + try { + String encodedNamespace = URLEncoder.encode(svcEventKey.getServiceKey().getNamespace(), "UTF-8"); + String encodedService = URLEncoder.encode(svcEventKey.getServiceKey().getService(), "UTF-8"); + String eventType = URLEncoder.encode(svcEventKey.getEventType().toString().toLowerCase(), "UTF-8"); + return String.format(PATTERN_SERVICE, encodedNamespace, encodedService, eventType); + } catch (UnsupportedEncodingException e) { + throw new AssertionError("UTF-8 is unknown"); + // or 'throw new AssertionError("Impossible things are happening today. " + + // "Consider buying a lottery ticket!!");' + } + } + + private static ServiceEventKey fileNameToServiceKey(String fileName) { + fileName = fileName.substring(0, fileName.length() - CACHE_SUFFIX.length()); + String[] pieces = fileName.split("#"); + try { + String namespace = URLDecoder.decode(pieces[1], "UTF-8"); + String service = URLDecoder.decode(pieces[2], "UTF-8"); + String eventTypeStr = URLDecoder.decode(pieces[3], "UTF-8"); + ServiceEventKey.EventType eventType = ServiceEventKey.EventType.valueOf(eventTypeStr.toUpperCase()); + return new ServiceEventKey(new ServiceKey(namespace, service), eventType); + } catch (UnsupportedEncodingException e) { + throw new AssertionError("UTF-8 is unknown"); + // or 'throw new AssertionError("Impossible things are happening today. " + + // "Consider buying a lottery ticket!!");' + } + } + + private void writeTmpFile(File persistTmpFile, File persistLockFile, Message message) throws IOException { + try (RandomAccessFile raf = new RandomAccessFile(persistLockFile, "rw"); + FileChannel channel = raf.getChannel()) { + FileLock lock = channel.tryLock(); + if (lock == null) { + throw new IOException( + "fail to lock file " + persistTmpFile.getAbsolutePath() + ", ignore and retry later"); + } + //执行保存 + try { + doWriteTmpFile(persistTmpFile, message); + } finally { + lock.release(); + } + } + } + + private void doWriteTmpFile(File persistTmpFile, Message message) throws IOException { + if (!persistTmpFile.exists()) { + if (!persistTmpFile.createNewFile()) { + LOG.warn("tmp file {} already exists", persistTmpFile.getAbsolutePath()); + } + } + try (FileOutputStream outputFile = new FileOutputStream(persistTmpFile)) { + String jsonStr = printer.print(message); + JsonNode jsonNodeTree = new ObjectMapper().readTree(jsonStr); + String jsonAsYaml = new YAMLMapper().writeValueAsString(jsonNodeTree); + outputFile.write(jsonAsYaml.getBytes(StandardCharsets.UTF_8)); + outputFile.flush(); + } + } + + private Path doSaveService(ServiceEventKey svcEventKey, Message message) { + String fileName = serviceKeyToFileName(svcEventKey); + String tmpFileName = fileName + ".tmp"; + String lockFileName = fileName + ".lock"; + String persistFilePathStr = persistDirPath + File.separator + fileName; + Path persistPath = FileSystems.getDefault().getPath(persistFilePathStr); + File persistTmpFile = new File(persistDirPath + File.separator + tmpFileName); + File persistLockFile = new File(persistDirPath + File.separator + lockFileName); + try { + if (!persistLockFile.exists()) { + if (!persistLockFile.createNewFile()) { + LOG.warn("lock file {} already exists", persistLockFile.getAbsolutePath()); + } + } + writeTmpFile(persistTmpFile, persistLockFile, message); + Files.move(FileSystems.getDefault().getPath(persistTmpFile.getAbsolutePath()), + persistPath, REPLACE_EXISTING, ATOMIC_MOVE); + } catch (IOException e) { + LOG.error("fail to write file {}", persistTmpFile, e); + return null; + } + return persistPath.toAbsolutePath(); + } + + /** + * 遍历缓存目录并加载之前缓存的服务信息 + * + * @param message 消息对象 + * @return 服务标识-消息对象的集合 + */ + public Map loadPersistedServices(Message message) { + Path curDir = Paths.get(persistDirPath); + Map result = new HashMap<>(); + try { + Files.walkFileTree(curDir, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path filePath, BasicFileAttributes attrs) { + Path fileNamePath = filePath.getFileName(); + if (null == fileNamePath) { + return FileVisitResult.CONTINUE; + } + String fileName = fileNamePath.toString(); + if (!REGEX_PATTERN_SERVICE.matcher(fileName).matches()) { + return FileVisitResult.CONTINUE; + } + ServiceEventKey svcEventKey = fileNameToServiceKey(fileName); + int retryTimes = 0; + Message readMessage = null; + while (retryTimes <= maxReadRetry) { + retryTimes++; + Message.Builder builder = message.newBuilderForType(); + readMessage = loadMessage(filePath.toFile(), builder); + if (null == readMessage) { + Utils.sleepUninterrupted(retryInterval); + continue; + } + break; + } + if (null == readMessage) { + LOG.debug("fail to read service from {} after retry {} times", fileName, retryTimes); + return FileVisitResult.CONTINUE; + } + result.put(svcEventKey, readMessage); + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + LOG.error("fail to visit cache directory {}", persistDirPath); + } + return result; + } + + private Message loadMessage(File persistFile, Message.Builder builder) { + if (null == persistFile || !persistFile.exists()) { + return null; + } + InputStream inputStream = null; + InputStreamReader reader = null; + Yaml yaml = new Yaml(); + try { + inputStream = new FileInputStream(persistFile); + reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + Map jsonMap = yaml.load(reader); + ObjectMapper jsonWriter = new ObjectMapper(); + String jsonStr = jsonWriter.writeValueAsString(jsonMap); + parser.merge(jsonStr, builder); + return builder.build(); + } catch (IOException e) { + LOG.debug("fail to read file {}", persistFile.getAbsoluteFile(), e); + return null; + } finally { + if (null != reader) { + try { + reader.close(); + } catch (IOException e) { + LOG.warn("fail to close reader for {}", persistFile.getAbsoluteFile(), e); + } + } + if (null != inputStream) { + try { + inputStream.close(); + } catch (IOException e) { + LOG.warn("fail to close stream for {}", persistFile.getAbsoluteFile(), e); + } + } + } + } + +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.LocalRegistry b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.LocalRegistry new file mode 100644 index 000000000..498cdb235 --- /dev/null +++ b/polaris-plugins/polaris-plugins-client/resource-cache/resource-cache-memory/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.registry.LocalRegistry @@ -0,0 +1 @@ +com.tencent.polaris.plugins.local.cache.memory.InMemoryRegistry \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/pom.xml b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/pom.xml new file mode 100644 index 000000000..588613c6c --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/pom.xml @@ -0,0 +1,15 @@ + + + + loadbalancer + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + loadbalancer-random + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/java/com/tencent/polaris/plugins/loadbalancer/random/WeightedRandomBalance.java b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/java/com/tencent/polaris/plugins/loadbalancer/random/WeightedRandomBalance.java new file mode 100644 index 000000000..01277c32b --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/java/com/tencent/polaris/plugins/loadbalancer/random/WeightedRandomBalance.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.loadbalancer.random; + +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.Criteria; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.control.Destroyable; +import java.util.List; +import java.util.Random; + +/** + * 权重随机复杂均衡 + * + * @author andrewshan + * @date 2019/8/24 + */ +public class WeightedRandomBalance extends Destroyable implements LoadBalancer { + + private final Random random = new Random(); + + @Override + public Instance chooseInstance(Criteria criteria, ServiceInstances svcInstances) throws PolarisException { + int totalWeight = svcInstances.getTotalWeight(); + if (totalWeight <= 0) { + totalWeight = sumTotalWeight(svcInstances); + } + if (totalWeight == 0) { + throw new PolarisException(ErrorCode.INSTANCE_NOT_FOUND, + String.format("all instances weight 0 for %s:%s", svcInstances.getNamespace(), + svcInstances.getService())); + } + //进行权重区间分配 + List instances = svcInstances.getInstances(); + int randomValue = Math.abs(random.nextInt() % totalWeight); + int start = 0; + int end = 0; + for (Instance instance : instances) { + end = end + instance.getWeight(); + if (randomValue >= start && randomValue < end) { + return instance; + } + start = end; + } + //全都分配不到,则随机获取一个 + return instances.get(totalWeight % instances.size()); + } + + private int sumTotalWeight(ServiceInstances svcInstances) { + List instances = svcInstances.getInstances(); + int totalWeight = 0; + if (CollectionUtils.isNotEmpty(instances)) { + for (Instance instance : instances) { + totalWeight += instance.getWeight(); + } + } + return totalWeight; + } + + @Override + public String getName() { + return LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM; + } + + @Override + public PluginType getType() { + return PluginTypes.LOAD_BALANCER.getBaseType(); + } + + @Override + public void init(InitContext ctx) { + + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + + } + +} diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer new file mode 100644 index 000000000..468d8d866 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-random/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer @@ -0,0 +1 @@ +com.tencent.polaris.plugins.loadbalancer.random.WeightedRandomBalance \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/pom.xml b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/pom.xml new file mode 100644 index 000000000..224406a74 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/pom.xml @@ -0,0 +1,16 @@ + + + + loadbalancer + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + loadbalancer-ringhash + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/ConsistentHashLoadBalance.java b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/ConsistentHashLoadBalance.java new file mode 100644 index 000000000..bc060f3d1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/ConsistentHashLoadBalance.java @@ -0,0 +1,138 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.loadbalancer.ringhash; + +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.IdAwarePlugin; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.Criteria; +import com.tencent.polaris.api.utils.CollectionUtils; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Function; + +public class ConsistentHashLoadBalance extends Destroyable implements LoadBalancer, IdAwarePlugin { + + /** + * 虚拟环倍数 + */ + private static final int VIRTUAL_NODE_SIZE = 5; + private static final String VIRTUAL_NODE_SUFFIX = "&&"; + + private int id; + + /** + * 计算字符串hash的策略 + */ + private HashStrategy hashStrategy; + + private FlowCache flowCache; + + @Override + public Instance chooseInstance(Criteria criteria, ServiceInstances instances) throws PolarisException { + if (instances == null || CollectionUtils.isEmpty(instances.getInstances())) { + return null; + } + TreeMap ring = flowCache + .loadPluginCacheObject(getId(), instances, new Function>() { + @Override + public TreeMap apply(Object obj) { + return buildConsistentHashRing((ServiceInstances) obj); + } + }); + int invocationHashCode = hashStrategy.getHashCode(criteria.getHashKey()); + return lookup(ring, invocationHashCode); + } + + @Override + public String getName() { + return LoadBalanceConfig.LOAD_BALANCE_RING_HASH; + } + + @Override + public PluginType getType() { + return PluginTypes.LOAD_BALANCER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + hashStrategy = new MurmurHash(); + } + + @Override + public void postContextInit(Extensions ctx) throws PolarisException { + this.flowCache = ctx.getFlowCache(); + } + + /** + * 从环中获取一个实例 + * + * @param ring 环 + * @param invocationHashCode hashcode + * @return 实例 + */ + private Instance lookup(TreeMap ring, int invocationHashCode) { + // 向右找到第一个 key + Map.Entry locateEntry = ring.ceilingEntry(invocationHashCode); + if (locateEntry == null) { + // 超过尾部则取第一个 key + locateEntry = ring.firstEntry(); + } + return locateEntry.getValue(); + } + + /** + * 构建一致性hash环 + * + * @param serviceInstances 服务实例 + * @return 一致性hash环 + */ + private TreeMap buildConsistentHashRing(ServiceInstances serviceInstances) { + List instances = serviceInstances.getInstances(); + TreeMap virtualNodeRing = new TreeMap<>(); + for (Instance instance : instances) { + for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) { + // 新增虚拟节点 + virtualNodeRing.put(hashStrategy + .getHashCode(instance.getHost() + ":" + instance.getPort() + VIRTUAL_NODE_SUFFIX + i), + instance); + } + } + return virtualNodeRing; + } + + @Override + public int getId() { + return this.id; + } + + @Override + public void setId(int id) { + this.id = id; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/HashStrategy.java b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/HashStrategy.java new file mode 100644 index 000000000..a7a2dd2f1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/HashStrategy.java @@ -0,0 +1,26 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.loadbalancer.ringhash; + +/** + * 获取hash的策略 + */ +public interface HashStrategy { + + int getHashCode(String origin); +} diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/MurmurHash.java b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/MurmurHash.java new file mode 100644 index 000000000..744ae77bd --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/java/com/tencent/polaris/plugins/loadbalancer/ringhash/MurmurHash.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.loadbalancer.ringhash; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Murmur hash策略 + */ +public class MurmurHash implements HashStrategy { + + @Override + public int getHashCode(String origin) { + ByteBuffer buf = ByteBuffer.wrap(origin.getBytes()); + int seed = 0x1234ABCD; + + buf.order(ByteOrder.LITTLE_ENDIAN); + + long m = 0xc6a4a7935bd1e995L; + int r = 47; + + long h = seed ^ (buf.remaining() * m); + + long k; + while (buf.remaining() >= 8) { + k = buf.getLong(); + + k *= m; + k ^= k >>> r; + k *= m; + + h ^= k; + h *= m; + } + + if (buf.remaining() > 0) { + ByteBuffer finish = ByteBuffer.allocate(8).order( + ByteOrder.LITTLE_ENDIAN); + // for big-endian version, do this first: + // finish.position(8-buf.remaining()); + finish.put(buf).rewind(); + h ^= finish.getLong(); + h *= m; + } + h ^= h >>> r; + h *= m; + h ^= h >>> r; + + ByteOrder byteOrder = buf.order(); + buf.order(byteOrder); + return (int) (h & 0xffffffffL); + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer new file mode 100644 index 000000000..033b1501f --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/loadbalancer-ringhash/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.loadbalance.LoadBalancer @@ -0,0 +1 @@ +com.tencent.polaris.plugins.loadbalancer.ringhash.ConsistentHashLoadBalance \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/loadbalancer/pom.xml b/polaris-plugins/polaris-plugins-discovery/loadbalancer/pom.xml new file mode 100644 index 000000000..d9c794adf --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/loadbalancer/pom.xml @@ -0,0 +1,20 @@ + + + + polaris-plugins-discovery + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + loadbalancer + pom + + loadbalancer-random + loadbalancer-ringhash + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/pom.xml b/polaris-plugins/polaris-plugins-discovery/pom.xml new file mode 100644 index 000000000..14e17fbbb --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/pom.xml @@ -0,0 +1,38 @@ + + + + polaris-plugins + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins-discovery + pom + + + router + loadbalancer + + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/pom.xml new file mode 100644 index 000000000..b8c4bd296 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-plugins-discovery + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router + pom + + + router-set + router-canary + router-rule + router-nearby + router-isolated + router-healthy + router-metadata + router-common + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-canary/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-canary/pom.xml new file mode 100644 index 000000000..f17d9f532 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-canary/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-canary + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/java/com/tencent/polaris/plugin/router/canary/CanaryRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/java/com/tencent/polaris/plugin/router/canary/CanaryRouter.java new file mode 100644 index 000000000..ab8ecbdf3 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/java/com/tencent/polaris/plugin/router/canary/CanaryRouter.java @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugin.router.canary; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.plugin.route.RouterConstants; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.List; +import java.util.stream.Collectors; + +public class CanaryRouter extends AbstractServiceRouter { + + private static final String canaryMetadataEnable = "internal-canary"; + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) + throws PolarisException { + String canaryValue = routeInfo.getCanary(); + List instanceList; + if (!StringUtils.isBlank(canaryValue)) { + instanceList = instances.getInstances().stream() + .filter(instance -> MapUtils.isNotEmpty(instance.getMetadata()) + && StringUtils.equals(canaryValue, instance.getMetadata().get(RouterConstants.CANARY_KEY))) + .collect(Collectors.toList()); + } else { + instanceList = instances.getInstances().stream() + .filter(instance -> MapUtils.isEmpty(instance.getMetadata()) + || !StringUtils.equals(canaryValue, instance.getMetadata().get(RouterConstants.CANARY_KEY))) + .collect(Collectors.toList()); + } + if (CollectionUtils.isEmpty(instanceList)) { + return new RouteResult(instances.getInstances(), RouteResult.State.Next); + } + return new RouteResult(instanceList, RouteResult.State.Next); + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public String getName() { + return "canaryRouter"; + } + + @Override + public Aspect getAspect() { + return Aspect.MIDDLE; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + if (!dstSvcInfo.getMetadata().containsKey(canaryMetadataEnable)) { + return false; + } + return Boolean.parseBoolean(dstSvcInfo.getMetadata().get(canaryMetadataEnable)); + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..fe18e1b84 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-canary/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugin.router.canary.CanaryRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-common/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-common/pom.xml new file mode 100644 index 000000000..9d771d4bb --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-common/pom.xml @@ -0,0 +1,15 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-common + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-common/src/main/java/com/tencent/polaris/plugins/router/common/AbstractServiceRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-common/src/main/java/com/tencent/polaris/plugins/router/common/AbstractServiceRouter.java new file mode 100644 index 000000000..cb2a30ab5 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-common/src/main/java/com/tencent/polaris/plugins/router/common/AbstractServiceRouter.java @@ -0,0 +1,109 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.common; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.IdAwarePlugin; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.control.Destroyable; + +public abstract class AbstractServiceRouter extends Destroyable implements ServiceRouter, IdAwarePlugin { + + private int id; + + private void validateParams(RouteInfo routeInfo, ServiceInstances instances) + throws PolarisException { + + // 无实例, 返回参数错误 + if (instances == null || CollectionUtils.isEmpty(instances.getInstances())) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, + "GetFilteredInstances param invalid, empty instances"); + } + + // 规则实例为nil, 返回参数错误 + if (routeInfo == null) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, + "GetFilteredInstances param invalid, routeInfo can't be nil"); + } + + // 被调服务必须存在 + if (routeInfo.getDestService() == null) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, + "GetFilteredInstances param invalid, destService must exist"); + } + + // 被调规则如果存在, 主流程必须保证已初始化 + if (routeInfo.getDestRouteRule() != null && !routeInfo.getDestRouteRule().isInitialized()) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, + "GetFilteredInstances param invalid, dest routes exist but not initialized"); + } + + // 主调规则如果存在, 主流程必须保证已初始化 + if (routeInfo.getSourceRouteRule() != null && !routeInfo.getSourceRouteRule() + .isInitialized()) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, + "GetFilteredInstances param invalid, source routes exist but not initialized"); + } + + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + @Override + public RouteResult getFilteredInstances(RouteInfo routeInfo, ServiceInstances instances) + throws PolarisException { + validateParams(routeInfo, instances); + return router(routeInfo, instances); + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + Boolean enableValue = routeInfo.routerIsEnabled(getName()); + return null == enableValue || enableValue; + } + + public abstract RouteResult router(RouteInfo routeInfo, ServiceInstances instances) + throws PolarisException; + + @Override + public int getId() { + return id; + } + + @Override + public void setId(int id) { + this.id = id; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-healthy/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-healthy/pom.xml new file mode 100644 index 000000000..1c67ec544 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-healthy/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-healthy + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/java/com/tencent/polaris/plugins/router/healthy/RecoverRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/java/com/tencent/polaris/plugins/router/healthy/RecoverRouter.java new file mode 100644 index 000000000..6350b1078 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/java/com/tencent/polaris/plugins/router/healthy/RecoverRouter.java @@ -0,0 +1,77 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.healthy; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 全死全活路由 + */ +public class RecoverRouter extends AbstractServiceRouter { + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) + throws PolarisException { + //过滤不健康的节点,只有心跳而且没有被熔断的才是健康的 + List healthyInstance = instances.getInstances().stream().filter( + instance -> Utils.isHealthyInstance(instance, routeInfo.getStatusDimensions())) + .collect(Collectors.toList()); + int healthyInstanceCount = healthyInstance.size(); + if (healthyInstanceCount == 0) { + return new RouteResult(instances.getInstances(), RouteResult.State.Next); + } + return new RouteResult(healthyInstance, RouteResult.State.Next); + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + } + + @Override + public String getName() { + return ServiceRouterConfig.DEFAULT_ROUTER_RECOVER; + } + + @Override + public Aspect getAspect() { + return Aspect.AFTER; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + return true; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..129db7323 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-healthy/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.healthy.RecoverRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-isolated/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-isolated/pom.xml new file mode 100644 index 000000000..0cb0f5b8d --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-isolated/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-isolated + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/java/com/tencent/polaris/plugins/router/isolated/IsolatedRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/java/com/tencent/polaris/plugins/router/isolated/IsolatedRouter.java new file mode 100644 index 000000000..c5dcc85f8 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/java/com/tencent/polaris/plugins/router/isolated/IsolatedRouter.java @@ -0,0 +1,68 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.isolated; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.ArrayList; +import java.util.List; + +/** + * 剔除隔离的,以及不健康的节点 + */ +public class IsolatedRouter extends AbstractServiceRouter { + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances svcInstances) throws PolarisException { + List instances = new ArrayList<>(); + for (Instance instance : svcInstances.getInstances()) { + if (instance.getWeight() == 0 || instance.isIsolated()) { + continue; + } + instances.add(instance); + } + return new RouteResult(instances, RouteResult.State.Next); + } + + @Override + public Aspect getAspect() { + return Aspect.BEFORE; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + return true; + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public String getName() { + return ServiceRouterConfig.DEFAULT_ROUTER_ISOLATED; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..2224eaaf5 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-isolated/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.isolated.IsolatedRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-metadata/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/pom.xml new file mode 100644 index 000000000..84955294b --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-metadata + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/FailOverType.java b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/FailOverType.java new file mode 100644 index 000000000..e9e30df4e --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/FailOverType.java @@ -0,0 +1,22 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.metadata; + +public enum FailOverType { + none, all, others +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java new file mode 100644 index 000000000..2dbc03ba7 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouter.java @@ -0,0 +1,178 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.metadata; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.config.plugin.PluginConfigProvider; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.rpc.MetadataFailoverType; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 正常场景:选出的实例子集不为空,那么优先返回健康子集,如果全部不健康则进行全死全活返回不健康子集。 + * + * 异常场景:需要根据GetOneInstanceRequest的请求策略进行降级决策 + * + * 不降级(默认):返回未找到实例错误 + * 返回所有节点:优先返回服务下的健康子集,如果全部不健康则全死全活返回不健康子集 + * 返回实例元数据不包含请求metadata的key的节点:优先返回筛选出的健康子集,如果全部不健康则返回不健康子集 + * 例如:ip1 set=1 ; ip2 set=2 ; ip3 ; 请求时 set=0 返回的 ip3(这个时候只判断key) + * 降级使用指定metadata进行实例筛选。(未实现) + * + * @author starkwen + * @date 2021/2/24 下午3:23 + */ +public class MetadataRouter extends AbstractServiceRouter implements PluginConfigProvider { + + private static final String KEY_METADATA_FAILOVER_TYPE = "internal-metadata-failover-type"; + + private static final Map valueToFailoverType = new HashMap<>(); + + private static final Map inputToFailoverType = new HashMap<>(); + + static { + valueToFailoverType.put("none", FailOverType.none); + valueToFailoverType.put("all", FailOverType.all); + valueToFailoverType.put("others", FailOverType.others); + + inputToFailoverType.put(MetadataFailoverType.METADATAFAILOVERNONE, FailOverType.none); + inputToFailoverType.put(MetadataFailoverType.METADATAFAILOVERALL, FailOverType.all); + inputToFailoverType.put(MetadataFailoverType.METADATAFAILOVERNOTKEY, FailOverType.others); + } + + private MetadataRouterConfig config; + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) throws PolarisException { + FailOverType failOverType = config.getMetadataFailOverType(); + Map svcMetadata = instances.getMetadata(); + if (MapUtils.isNotEmpty(svcMetadata)) { + if (svcMetadata.containsKey(KEY_METADATA_FAILOVER_TYPE)) { + String value = svcMetadata.get(KEY_METADATA_FAILOVER_TYPE); + if (valueToFailoverType.containsKey(value)) { + failOverType = valueToFailoverType.get(value); + } + } + } + MetadataFailoverType metadataFailoverType = routeInfo.getMetadataFailoverType(); + if (null != metadataFailoverType) { + failOverType = inputToFailoverType.get(metadataFailoverType); + } + boolean availableInsFlag; + Map reqMetadata = routeInfo.getDestService().getMetadata(); + List instanceList = new ArrayList<>(); + for (Instance ins : instances.getInstances()) { + availableInsFlag = true; + // 要满足请求中的metadata K-V全部存在于实例的metadata中 + for (Map.Entry entry : reqMetadata.entrySet()) { + if (ins.getMetadata().containsKey(entry.getKey()) + && ins.getMetadata().get(entry.getKey()).equals(entry.getValue())) { + continue; + } + availableInsFlag = false; + break; + } + if (availableInsFlag) { + instanceList.add(ins); + } + } + if (!CollectionUtils.isEmpty(instanceList)) { + return new RouteResult(instanceList, RouteResult.State.Next); + } + switch (failOverType) { + case all: + return new RouteResult(instances.getInstances(), RouteResult.State.Next); + case others: + return new RouteResult(addNotContainKeyIns(instances, reqMetadata), RouteResult.State.Next); + default: + // 默认不降级 + throw new PolarisException(ErrorCode.METADATA_MISMATCH, + String.format("can not find any instance by service %s", routeInfo.getDestService())); + } + } + + private List addNotContainKeyIns(ServiceInstances instances, Map reqMetadata) { + List instanceList = new ArrayList<>(); + for (Instance ins : instances.getInstances()) { + boolean containKey = true; + for (Map.Entry entry : reqMetadata.entrySet()) { + if (ins.getMetadata().containsKey(entry.getKey())) { + continue; + } + containKey = false; + } + // 如果实例的metadata不包含传入的metadata,或者传入的metadata为空 + if (!containKey) { + instanceList.add(ins); + } + } + return instanceList; + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + this.config = ctx.getConfig().getConsumer().getServiceRouter() + .getPluginConfig(getName(), MetadataRouterConfig.class); + } + + @Override + public String getName() { + return ServiceRouterConfig.DEFAULT_ROUTER_METADATA; + } + + @Override + public Class getPluginConfigClazz() { + return MetadataRouterConfig.class; + } + + public MetadataRouterConfig getConfig() { + return config; + } + + @Override + public Aspect getAspect() { + return Aspect.MIDDLE; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + Map metadata = routeInfo.getDestService().getMetadata(); + return !MapUtils.isEmpty(metadata); + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouterConfig.java b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouterConfig.java new file mode 100644 index 000000000..6234f6259 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/java/com/tencent/polaris/plugins/router/metadata/MetadataRouterConfig.java @@ -0,0 +1,41 @@ +package com.tencent.polaris.plugins.router.metadata; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 元数据路由的配置 + * + * @author starkwen + * @date 2021/2/24 下午3:26 + */ +public class MetadataRouterConfig implements Verifier { + + /** + * 可选, metadata失败降级策略 + */ + @JsonProperty + private FailOverType metadataFailOverType; + + @Override + public void verify() { + ConfigUtils.validateNull(metadataFailOverType, "metadataFailOverType"); + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + MetadataRouterConfig metadataRouterConfig = (MetadataRouterConfig) defaultObject; + setMetadataFailOverType(metadataRouterConfig.getMetadataFailOverType()); + } + } + + public FailOverType getMetadataFailOverType() { + return metadataFailOverType; + } + + public void setMetadataFailOverType(FailOverType metadataFailoverType) { + this.metadataFailOverType = metadataFailoverType; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider new file mode 100644 index 000000000..0bf0c3cb9 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.metadata.MetadataRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..0bf0c3cb9 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-metadata/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.metadata.MetadataRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-nearby/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/pom.xml new file mode 100644 index 000000000..97a59baac --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-nearby + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java new file mode 100644 index 000000000..c718fa068 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouter.java @@ -0,0 +1,374 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.nearby; + +import static com.tencent.polaris.client.util.Utils.isHealthyInstance; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.config.plugin.PluginConfigProvider; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.common.ValueContext; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.route.LocationLevel; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.pojo.StatusDimension; +import com.tencent.polaris.api.pojo.StatusDimension.Level; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import com.tencent.polaris.client.util.NamedThreadFactory; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 就近接入路由 + * + * @author vickliu + * @date 2019/11/10 + */ +public class NearbyRouter extends AbstractServiceRouter implements PluginConfigProvider { + + private static final Logger LOG = LoggerFactory.getLogger(NearbyRouter.class); + + private static final LocationLevel defaultMinLevel = LocationLevel.zone; + + private ValueContext valueContext; + + private ScheduledExecutorService reportClientExecutor; + + /** + * # 默认就近区域:默认城市 matchLevel: zone # 最大就近区域,默认为空(全匹配) maxMatchLevel: all # + * 假如开启了严格就近,插件的初始化会等待地域信息获取成功才返回,假如获取失败(server获取失败或者IP地域信息缺失),则会初始化失败,而且必须按照 strictNearby: false # + * 是否启用按服务不健康实例比例进行降级 enableDegradeByUnhealthyPercent: true,假如不启用,则不会降级# + * 需要进行降级的实例比例,不健康实例达到百分之多少才进行降级。值(0, 100]。 # 默认100,即全部不健康才进行切换。 + */ + private NearbyRouterConfig config; + + /** + * 降级的剩余健康比例 + */ + private double healthyPercentToDegrade; + + /** + * 等待地域信息就绪的超时时间 + */ + long locationReadyTimeout; + /** + * 主调的地域信息 + */ + private final AtomicReference> locationInfo = new AtomicReference<>(); + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances serviceInstances) + throws PolarisException { + //先获取最低可用就近级别 + LocationLevel minAvailableLevel = config.getMatchLevel(); + if (null == minAvailableLevel) { + minAvailableLevel = defaultMinLevel; + } + LocationLevel minLevel = minAvailableLevel; + if (null != routeInfo.getNextRouterInfo()) { + if (null != routeInfo.getNextRouterInfo().getLocationLevel()) { + minLevel = routeInfo.getNextRouterInfo().getLocationLevel(); + } + if (null != routeInfo.getNextRouterInfo().getMinAvailableLevel()) { + minAvailableLevel = routeInfo.getNextRouterInfo().getMinAvailableLevel(); + } + } + LocationLevel maxLevel = config.getMaxMatchLevel(); + if (null == maxLevel) { + maxLevel = LocationLevel.all; + } + + Map clientLocationInfo = locationInfo.get(); + if (minLevel.ordinal() >= maxLevel.ordinal()) { + List instances = selectInstances(serviceInstances, minAvailableLevel, clientLocationInfo); + if (CollectionUtils.isEmpty(instances)) { + throw new PolarisException(ErrorCode.LOCATION_MISMATCH, + String.format("can not find any instance by level %s", minLevel.name())); + } + //已经循环了一圈 + return new RouteResult(selectInstances( + serviceInstances, minAvailableLevel, clientLocationInfo), RouteResult.State.Next); + } + CheckResult checkResult = new CheckResult(); + for (int i = minLevel.ordinal(); i < maxLevel.ordinal(); i++) { + LocationLevel curLevel = LocationLevel.values()[i]; + checkResult = hasHealthyInstances(serviceInstances, routeInfo.getStatusDimensions(), curLevel, + clientLocationInfo); + checkResult.curLevel = curLevel; + if (!CollectionUtils.isEmpty(checkResult.instances)) { + break; + } else { + minAvailableLevel = curLevel; + } + } + if (CollectionUtils.isEmpty(checkResult.instances)) { + throw new PolarisException(ErrorCode.LOCATION_MISMATCH, + String.format("can not find any instance by level %s", checkResult.curLevel.name())); + } + if (!config.isEnableDegradeByUnhealthyPercent() || checkResult.curLevel == LocationLevel.all) { + return new RouteResult(checkResult.instances, RouteResult.State.Next); + } + int healthyInstanceCount = checkResult.healthyInstanceCount; + double actualHealthyPercent = (double) healthyInstanceCount / (double) serviceInstances.getInstances().size(); + if (actualHealthyPercent <= healthyPercentToDegrade) { + LOG.debug("[shouldDegrade] enableDegradeByUnhealthyPercent = {},unhealthyPercentToDegrade={}," + + "healthyPercent={},isStrict={},matchLevel={}", + config.isEnableDegradeByUnhealthyPercent(), config.getUnhealthyPercentToDegrade(), + actualHealthyPercent, + config.isStrictNearby(), checkResult.curLevel); + RouteResult result = new RouteResult(checkResult.instances, RouteResult.State.Retry); + result.getNextRouterInfo().setLocationLevel(nextLevel(checkResult.curLevel)); + result.getNextRouterInfo().setMinAvailableLevel(minAvailableLevel); + return result; + } + return new RouteResult(checkResult.instances, RouteResult.State.Next); + } + + private LocationLevel nextLevel(LocationLevel current) { + if (current == LocationLevel.all) { + return current; + } + return LocationLevel.values()[current.ordinal() + 1]; + } + + private static class CheckResult { + + LocationLevel curLevel; + int healthyInstanceCount; + List instances = new ArrayList<>(); + } + + private CheckResult hasHealthyInstances(ServiceInstances svcInstances, Map dimensions, + LocationLevel targetLevel, Map clientInfo) { + String clientZone = ""; + String clientRegion = ""; + String clientCampus = ""; + if (null != clientInfo) { + clientZone = clientInfo.get(LocationLevel.zone); + clientRegion = clientInfo.get(LocationLevel.region); + clientCampus = clientInfo.get(LocationLevel.campus); + } + CheckResult checkResult = new CheckResult(); + for (Instance instance : svcInstances.getInstances()) { + switch (targetLevel) { + case zone: + if (clientZone.equals("") || clientZone.equals(instance.getZone())) { + checkResult.instances.add(instance); + if (isHealthyInstance(instance, dimensions)) { + checkResult.healthyInstanceCount++; + } + } + break; + case campus: + if (clientCampus.equals("") || clientCampus.equals(instance.getCampus())) { + checkResult.instances.add(instance); + if (isHealthyInstance(instance, dimensions)) { + checkResult.healthyInstanceCount++; + } + } + break; + case region: + if (clientRegion.equals("") || clientRegion.equals(instance.getRegion())) { + checkResult.instances.add(instance); + if (isHealthyInstance(instance, dimensions)) { + checkResult.healthyInstanceCount++; + } + } + break; + default: + checkResult.instances.add(instance); + if (isHealthyInstance(instance, dimensions)) { + checkResult.healthyInstanceCount++; + } + break; + } + } + return checkResult; + } + + + private List selectInstances( + ServiceInstances svcInstances, LocationLevel targetLevel, Map clientInfo) { + List instances = new ArrayList<>(); + String clientZone = ""; + String clientRegion = ""; + String clientCampus = ""; + if (null != clientInfo) { + clientZone = clientInfo.get(LocationLevel.zone); + clientRegion = clientInfo.get(LocationLevel.region); + clientCampus = clientInfo.get(LocationLevel.campus); + } + for (Instance instance : svcInstances.getInstances()) { + switch (targetLevel) { + case zone: + if (clientZone.equals("") || clientZone.equals(instance.getZone())) { + instances.add(instance); + } + break; + case campus: + if (clientCampus.equals("") || clientCampus.equals(instance.getCampus())) { + instances.add(instance); + } + break; + case region: + if (clientRegion.equals("") || clientRegion.equals(instance.getRegion())) { + instances.add(instance); + } + break; + default: + instances.add(instance); + break; + } + } + return instances; + } + + @Override + public String getName() { + return ServiceRouterConfig.DEFAULT_ROUTER_NEARBY; + } + + @Override + public Class getPluginConfigClazz() { + return NearbyRouterConfig.class; + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + + @Override + public void init(InitContext ctx) throws PolarisException { + valueContext = ctx.getValueContext(); + NearbyRouterConfig config = ctx.getConfig().getConsumer().getServiceRouter() + .getPluginConfig(getName(), NearbyRouterConfig.class); + if (config == null) { + throw new PolarisException(ErrorCode.INVALID_CONFIG, + String.format("plugin %s config is missing", getName())); + } + this.config = config; + LOG.debug("[init] config={}", this.config); + locationReadyTimeout = (ctx.getConfig().getGlobal().getAPI().getReportInterval() + ctx.getConfig().getGlobal() + .getServerConnector().getConnectTimeout()) * (ctx.getConfig().getGlobal().getAPI().getMaxRetryTimes() + + 1); + healthyPercentToDegrade = 1 - (double) config.getUnhealthyPercentToDegrade() / (double) 100; + if (this.config.isEnableReportLocalAddress()) { + reportClientExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory(getName())); + } + } + + + /** + * 在整个AppContext初始化完毕后调用 + * + * @param extensions 插件对象池 + * @throws PolarisException 异常 + */ + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + //加载本地配置文件的地址 + //TODO: + if (null != reportClientExecutor) { + //执行并定时进行客户端上报 + reportClientExecutor.scheduleAtFixedRate( + new ReportClientTask(extensions, valueContext), 0, 60, TimeUnit.SECONDS); + LOG.info("reportClientExecutor has been started"); + //强制就近模式下,需要等待地域信息初始化成功 + ensureLocationReady(); + } + } + + /** + * 校验是否严格校验地域信息 + */ + public void ensureLocationReady() throws PolarisException { + if (!this.config.isStrictNearby()) { + return; + } + try { + this.valueContext.waitForLocationReady(this.locationReadyTimeout); + refreshLocationInfo(); + } catch (InterruptedException e) { + throw new PolarisException(ErrorCode.LOCATION_MISMATCH, + "caller location not ready,and strict nearby is true.", e); + + } + } + + private void refreshLocationInfo() { + Map clientLocationInfo = new HashMap<>(); + for (LocationLevel key : LocationLevel.values()) { + if (valueContext.getValue(key.name()) != null) { + clientLocationInfo.put(key, valueContext.getValue(key.name())); + } + } + locationInfo.set(clientLocationInfo); + LOG.debug("[refreshLocationInfo] locationInfo={}", clientLocationInfo); + } + + private static final String nearbyMetadataEnable = "internal-enable-nearby"; + + @Override + public Aspect getAspect() { + return Aspect.MIDDLE; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + if (!super.enable(routeInfo, dstSvcInfo)) { + return false; + } + Map clientLocationInfo = locationInfo.get(); + if (MapUtils.isEmpty(clientLocationInfo)) { + return false; + } + if (!dstSvcInfo.getMetadata().containsKey(nearbyMetadataEnable)) { + return false; + } + return Boolean.parseBoolean(dstSvcInfo.getMetadata().get(nearbyMetadataEnable)); + } + + @Override + protected void doDestroy() { + LOG.info("reportClientExecutor has been stopped"); + ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{reportClientExecutor}); + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java new file mode 100644 index 000000000..d477b8de8 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/NearbyRouterConfig.java @@ -0,0 +1,151 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package com.tencent.polaris.plugins.router.nearby; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.api.plugin.route.LocationLevel; +import com.tencent.polaris.factory.util.ConfigUtils; + +/** + * 就近路由配置结构 + * + * @author andrewshan + * @date 2019/8/26 + */ +public class NearbyRouterConfig implements Verifier { + + @JsonProperty + private Boolean enableReportLocalAddress; + + @JsonProperty + private LocationLevel matchLevel; + + @JsonProperty + private LocationLevel maxMatchLevel; + + @JsonProperty + private Boolean strictNearby; + + @JsonProperty + private Boolean enableDegradeByUnhealthyPercent; + + @JsonProperty + private Integer unhealthyPercentToDegrade; + + public LocationLevel getMatchLevel() { + return matchLevel; + } + + public void setMatchLevel(LocationLevel matchLevel) { + this.matchLevel = matchLevel; + } + + public LocationLevel getMaxMatchLevel() { + return maxMatchLevel; + } + + public void setMaxMatchLevel(LocationLevel maxMatchLevel) { + this.maxMatchLevel = maxMatchLevel; + } + + public Boolean isStrictNearby() { + return strictNearby; + } + + public void setStrictNearby(Boolean strictNearby) { + this.strictNearby = strictNearby; + } + + public Boolean isEnableDegradeByUnhealthyPercent() { + return enableDegradeByUnhealthyPercent; + } + + public void setEnableDegradeByUnhealthyPercent(Boolean enableDegradeByUnhealthyPercent) { + this.enableDegradeByUnhealthyPercent = enableDegradeByUnhealthyPercent; + } + + public Integer getUnhealthyPercentToDegrade() { + return unhealthyPercentToDegrade; + } + + public void setUnhealthyPercentToDegrade(Integer unhealthyPercentToDegrade) { + this.unhealthyPercentToDegrade = unhealthyPercentToDegrade; + } + + public Boolean isEnableReportLocalAddress() { + return enableReportLocalAddress; + } + + public void setEnableReportLocalAddress(Boolean enableReportLocalAddress) { + this.enableReportLocalAddress = enableReportLocalAddress; + } + + @Override + public void verify() { + ConfigUtils.validateNull(enableReportLocalAddress, "enableReportLocalAddress"); + ConfigUtils.validateNull(matchLevel, "matchLevel"); + ConfigUtils.validateNull(maxMatchLevel, "matchLevel"); + if (matchLevel.ordinal() > maxMatchLevel.ordinal()) { + throw new IllegalArgumentException("matchLevel should smaller than maxMatchLevel"); + } + ConfigUtils.validateNull(strictNearby, "strictNearby"); + ConfigUtils.validateNull(enableDegradeByUnhealthyPercent, "enableDegradeByUnhealthyPercent"); + ConfigUtils.validatePositive(unhealthyPercentToDegrade, "unhealthyPercentToDegrade"); + if (unhealthyPercentToDegrade > 100) { + throw new IllegalArgumentException("unhealthyPercentToDegrade should less than or equals 100"); + } + } + + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + NearbyRouterConfig nearbyRouterConfig = (NearbyRouterConfig) defaultObject; + if (null == enableReportLocalAddress) { + setEnableReportLocalAddress(nearbyRouterConfig.isEnableReportLocalAddress()); + } + if (null == matchLevel) { + setMatchLevel(nearbyRouterConfig.getMatchLevel()); + } + if (null == maxMatchLevel) { + setMaxMatchLevel(nearbyRouterConfig.getMaxMatchLevel()); + } + if (null == strictNearby) { + setStrictNearby(nearbyRouterConfig.isStrictNearby()); + } + if (null == enableDegradeByUnhealthyPercent) { + setEnableDegradeByUnhealthyPercent(nearbyRouterConfig.isEnableDegradeByUnhealthyPercent()); + } + if (null == unhealthyPercentToDegrade) { + setUnhealthyPercentToDegrade(nearbyRouterConfig.getUnhealthyPercentToDegrade()); + } + } + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "NearbyRouterConfig{" + + "enableReportLocalAddress=" + enableReportLocalAddress + + ", matchLevel='" + matchLevel + '\'' + + ", maxMatchLevel='" + maxMatchLevel + '\'' + + ", strictNearby=" + strictNearby + + ", enableDegradeByUnhealthyPercent=" + enableDegradeByUnhealthyPercent + + ", unhealthyPercentToDegrade=" + unhealthyPercentToDegrade + + '}'; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/ReportClientTask.java b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/ReportClientTask.java new file mode 100644 index 000000000..9f028f164 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/java/com/tencent/polaris/plugins/router/nearby/ReportClientTask.java @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.nearby; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.ValueContext; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.route.LocationLevel; +import com.tencent.polaris.api.plugin.server.ReportClientRequest; +import com.tencent.polaris.api.plugin.server.ReportClientResponse; +import com.tencent.polaris.version.Version; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReportClientTask implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(ReportClientTask.class); + + private final Extensions extensions; + + private final String version; + private final String clientHost; + + private final ValueContext shareContext; + + public ReportClientTask(Extensions extensions, ValueContext shareContext) { + this.extensions = extensions; + this.version = Version.VERSION; + this.clientHost = shareContext.getHost(); + this.shareContext = shareContext; + } + + @Override + public void run() { + ReportClientResponse rsp = doReport(clientHost, version); + if (null == rsp) { + return; + } + + LOG.debug("current client Region:{}, Zone:{}, Campus:{}", rsp.getRegion(), rsp.getZone(), rsp.getCampus()); + + shareContext.setValue(LocationLevel.region.name(), rsp.getRegion()); + shareContext.setValue(LocationLevel.zone.name(), rsp.getZone()); + shareContext.setValue(LocationLevel.campus.name(), rsp.getCampus()); + shareContext.notifyAllForLocationReady(); + } + + private ReportClientResponse doReport(String clientHost, String version) { + ReportClientRequest req = new ReportClientRequest(); + req.setClientHost(clientHost); + req.setVersion(version); + ReportClientResponse rsp = null; + try { + rsp = extensions.getServerConnector().reportClient(req); + } catch (PolarisException e) { + LOG.warn("fail to report client info(clientHost={}, version={}), cause is {}", clientHost, version, + e.getMessage()); + } + return rsp; + } + + +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider new file mode 100644 index 000000000..06c851b7c --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.nearby.NearbyRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..06c851b7c --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-nearby/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.nearby.NearbyRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-rule/pom.xml new file mode 100644 index 000000000..7052532f4 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-rule + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/PrioritySubsets.java b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/PrioritySubsets.java new file mode 100644 index 000000000..a0ab8eef3 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/PrioritySubsets.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.rule; + +import java.util.List; + +// 同优先级的实例分组列表 +class PrioritySubsets { + + // 实例分组列表 + private List subsets; + // 实例分组的总权重 + private int totalWeight; + + public List getSubsets() { + return subsets; + } + + public void setSubsets(List subsets) { + this.subsets = subsets; + } + + public int getTotalWeight() { + return totalWeight; + } + + public void setTotalWeight(int totalWeight) { + this.totalWeight = totalWeight; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java new file mode 100644 index 000000000..d873734d6 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleBasedRouter.java @@ -0,0 +1,529 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.rule; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.RuleUtils; +import com.tencent.polaris.client.pb.ModelProto.MatchString; +import com.tencent.polaris.client.pb.RoutingProto; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 基于规则的服务路由能力 + * + * @author andrewshan + * @date 2019/8/28 + */ +public class RuleBasedRouter extends AbstractServiceRouter { + + private static final Logger LOG = LoggerFactory.getLogger(RuleBasedRouter.class); + + private Map globalVariablesConfig; + + /** + * 根据路由规则进行服务实例过滤, 并返回过滤后的实例列表 + * + * @param routeInfo + * @param ruleMatchType + * @return 路由规则列表 + * @throws PolarisException + */ + private List getRoutesFromRule(RouteInfo routeInfo, RuleMatchType ruleMatchType) + throws PolarisException { + // 跟据服务类型获取对应路由规则 + // 被调inbound + if (ruleMatchType == RuleMatchType.destRouteRuleMatch) { + if (null == routeInfo.getDestRouteRule() || null == routeInfo.getDestRouteRule().getRule()) { + return null; + } + Object routing = routeInfo.getDestRouteRule().getRule(); + if (!(routing instanceof RoutingProto.Routing)) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "getRuleFilteredInstances param invalid, " + + "inbound routing must be instance of RoutingProto.Routing"); + } + + return ((RoutingProto.Routing) routing).getInboundsList(); + } + + // 主调outbound + if (null == routeInfo.getSourceRouteRule() || null == routeInfo.getSourceRouteRule().getRule()) { + return null; + } + Object routing = routeInfo.getSourceRouteRule().getRule(); + if (!(routing instanceof RoutingProto.Routing)) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "getRuleFilteredInstances param invalid, " + + "outbound routing must be instance of RoutingProto.Routing"); + } + return ((RoutingProto.Routing) routing).getOutboundsList(); + } + + // 匹配source规则 + private boolean matchSource(List sources, ServiceMetadata sourceService, + RuleMatchType ruleMatchType, Map multiEnvRouterParamMap) { + if (CollectionUtils.isEmpty(sources)) { + return true; + } + + // source匹配成功标志 + boolean matched = true; + for (RoutingProto.Source source : sources) { + // 对于inbound规则, 需要匹配source服务 + if (ruleMatchType == RuleMatchType.destRouteRuleMatch) { + if (sourceService == null) { + // 如果没有source服务信息, 判断rule是否支持全匹配 + if (!RuleUtils.MATCH_ALL.equals(source.getNamespace().getValue()) || !RuleUtils.MATCH_ALL + .equals(source.getService().getValue())) { + matched = false; + continue; + } + } else { + // 如果有source服务信息, 需要匹配服务信息 + // 如果命名空间|服务不为"*"且不等于原服务, 则匹配失败 + if (!RuleUtils.MATCH_ALL.equals(source.getNamespace().getValue()) && !source.getNamespace() + .getValue().equals(sourceService.getNamespace())) { + matched = false; + continue; + } + + if (!RuleUtils.MATCH_ALL.equals(source.getService().getValue()) && !source.getService() + .getValue().equals(sourceService.getService())) { + matched = false; + continue; + } + } + } + + // 如果rule中metadata为空, 匹配成功, 结束 + if (MapUtils.isEmpty(source.getMetadataMap())) { + matched = true; + break; + } + + // 如果没有源服务信息, 本次匹配失败 + if (sourceService == null) { + matched = false; + continue; + } + + matched = matchMetadata( + source.getMetadataMap(), sourceService.getMetadata(), true, multiEnvRouterParamMap); + if (matched) { + break; + } + } + + return matched; + } + + + // 匹配metadata + private boolean matchMetadata(Map ruleMeta, Map destMeta, + boolean isMatchSource, Map multiEnvRouterParamMap) { + // 如果规则metadata为空, 返回成功 + if (MapUtils.isEmpty(ruleMeta)) { + return true; + } + + // 如果规则metadata不为空, 待匹配规则为空, 直接返回失败 + if (MapUtils.isEmpty(destMeta)) { + return false; + } + + // metadata是否全部匹配 + boolean allMetaMatched = true; + // dest中找到的metadata个数, 用于辅助判断是否能匹配成功 + int matchNum = 0; + + for (Map.Entry entry : ruleMeta.entrySet()) { + String ruleMetaKey = entry.getKey(); + MatchString ruleMetaValue = entry.getValue(); + if (RuleUtils.isMatchAllValue(ruleMetaValue)) { + matchNum++; + continue; + } + if (destMeta.containsKey(ruleMetaKey)) { + matchNum++; + if (!ruleMetaValue.hasValue() + && ruleMetaValue.getValueType() != MatchString.ValueType.PARAMETER) { + continue; + } + + String destMetaValue = destMeta.get(ruleMetaKey); + + allMetaMatched = isAllMetaMatched(isMatchSource, true, ruleMetaKey, ruleMetaValue, destMetaValue, + multiEnvRouterParamMap); + } + + if (!allMetaMatched) { + break; + } + } + + // 如果一个metadata未找到, 匹配失败 + if (matchNum == 0) { + allMetaMatched = false; + } + + return allMetaMatched; + } + + private boolean isAllMetaMatched(boolean isMatchSource, boolean allMetaMatched, String ruleMetaKey, + MatchString ruleMetaValue, String destMetaValue, Map multiEnvRouterParamMap) { + if (ruleMetaValue.getType() == MatchString.MatchStringType.REGEX) { + // 正则匹配 + allMetaMatched = matchValueByValueType(isMatchSource, true, ruleMetaKey, ruleMetaValue, destMetaValue, + multiEnvRouterParamMap); + } else { + // 精确匹配 + allMetaMatched = matchValueByValueType(isMatchSource, false, ruleMetaKey, ruleMetaValue, destMetaValue, + multiEnvRouterParamMap); + } + return allMetaMatched; + } + + private boolean matchValueByValueType(boolean isMatchSource, boolean isRegex, String ruleMetaKey, + MatchString ruleMetaValue, String destMetaValue, Map multiEnvRouterParamMap) { + boolean allMetaMatched = true; + + switch (ruleMetaValue.getValueType()) { + case PARAMETER: + // 通过参数传入 + if (isMatchSource) { + // 当匹配的是source,记录请求的 K V + multiEnvRouterParamMap.put(ruleMetaKey, destMetaValue); + } else { + // 当匹配的是dest, 判断value + if (!multiEnvRouterParamMap.containsKey(ruleMetaKey)) { + allMetaMatched = false; + } else { + String ruleValue = multiEnvRouterParamMap.get(ruleMetaKey); + // contains key + allMetaMatched = matchValue(isRegex, destMetaValue, ruleValue); + } + } + break; + case VARIABLE: + if (globalVariablesConfig.containsKey(ruleMetaKey)) { + // 1.先从配置获取 + String ruleValue = (String) globalVariablesConfig.get(ruleMetaKey); + allMetaMatched = matchValue(isRegex, destMetaValue, ruleValue); + } else { + // 2.从环境变量中获取 key从规则中获取 + String key = ruleMetaValue.getValue().getValue(); + if (!System.getenv().containsKey(key)) { + allMetaMatched = false; + } else { + String value = System.getenv(key); + allMetaMatched = matchValue(isRegex, destMetaValue, value); + } + if (!System.getenv().containsKey(key) || !System.getenv(key).equals(destMetaValue)) { + allMetaMatched = false; + } + } + break; + default: + allMetaMatched = matchValue(isRegex, destMetaValue, ruleMetaValue.getValue().getValue()); + } + + return allMetaMatched; + } + + private boolean matchValue(boolean isRegex, String destMetaValue, String ruleValue) { + boolean allMetaMatched = true; + + if (isRegex) { + boolean match = Utils.regMatch(ruleValue, destMetaValue); + if (!match) { + allMetaMatched = false; + } + } else { + if (!destMetaValue.equals(ruleValue)) { + allMetaMatched = false; + } + } + return allMetaMatched; + } + + + private List getRuleFilteredInstances(RouteInfo routeInfo, ServiceInstances instances, + RuleMatchType ruleMatchType) throws PolarisException { + // 获取路由规则 + List routes = getRoutesFromRule(routeInfo, ruleMatchType); + if (CollectionUtils.isEmpty(routes)) { + return Collections.emptyList(); + } + Map multiEnvRouterParamMap = new HashMap<>(); + for (RoutingProto.Route route : routes) { + if (route == null) { + continue; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("getRuleFilteredInstances, route:{}", route.toString()); + } + + // 匹配source规则 + boolean sourceMatched = matchSource(route.getSourcesList(), routeInfo.getSourceService(), ruleMatchType, + multiEnvRouterParamMap); + if (!sourceMatched) { + continue; + } + + // 如果source匹配成功, 继续匹配destination规则 + // 然后将结果写进map(key: 权重, value: 带权重的实例分组) + Map subsetsMap = new HashMap<>(); + int smallestPriority = -1; + for (RoutingProto.Destination dest : route.getDestinationsList()) { + if (dest == null) { + continue; + } + + // 对于outbound规则, 需要匹配DestService服务 + if (ruleMatchType == RuleMatchType.sourceRouteRuleMatch) { + if (!RuleUtils.MATCH_ALL.equals(dest.getNamespace().getValue()) && !dest.getNamespace().getValue() + .equals(routeInfo.getDestService().getNamespace())) { + continue; + } + + if (!RuleUtils.MATCH_ALL.equals(dest.getService().getValue()) && !dest.getService().getValue() + .equals(routeInfo.getDestService().getService())) { + continue; + } + } + + if (dest.getWeight().getValue() == 0) { + continue; + } + + boolean populated = populateSubsetsFromDest(instances, dest, subsetsMap, multiEnvRouterParamMap); + if (populated) { + int priority = dest.getPriority().getValue(); + if (smallestPriority < 0 || smallestPriority > priority) { + smallestPriority = priority; + } + } + } + + // 如果未匹配到分组, 继续匹配 + if (MapUtils.isEmpty(subsetsMap)) { + continue; + } + // 匹配到分组, 返回 + return selectInstances(routeInfo, subsetsMap.get(smallestPriority)); + } + + // 全部匹配完成, 未匹配到任何分组, 返回空 + return Collections.emptyList(); + } + + /** + * populateSubsetsFromDest 根据destination中的规则填充分组列表 + * + * @param instances 实例信息 + * @param dest 目标规则 + * @param subsetsMap 实例分组 + * @return 是否成功加入subset列表 + */ + private boolean populateSubsetsFromDest( + ServiceInstances instances, RoutingProto.Destination dest, Map subsetsMap, + Map multiEnvRouterParamMap) { + // 获取subset + List oriInstances = instances.getInstances(); + List filteredInstances = new ArrayList<>(); + for (Instance ins : oriInstances) { + if (!matchMetadata(dest.getMetadataMap(), ins.getMetadata(), false, multiEnvRouterParamMap)) { + continue; + } + filteredInstances.add(ins); + } + + // subset中无实例 + if (CollectionUtils.isEmpty(filteredInstances)) { + return false; + } + + // 根据优先级填充subset列表 + int priority = dest.getPriority().getValue(); + int weight = dest.getWeight().getValue(); + PrioritySubsets weightedSubsets = subsetsMap.get(priority); + if (weightedSubsets == null) { + PrioritySubsets prioritySubsets = new PrioritySubsets(); + + WeightedSubset weightedSubset = new WeightedSubset(); + weightedSubset.setInstances(filteredInstances); + weightedSubset.setWeight(weight); + + prioritySubsets.setSubsets(Arrays.asList(weightedSubset)); + prioritySubsets.setTotalWeight(weight); + + subsetsMap.put(priority, prioritySubsets); + + } else { + WeightedSubset weightedSubset = new WeightedSubset(); + weightedSubset.setInstances(filteredInstances); + weightedSubset.setWeight(weight); + + weightedSubsets.getSubsets().add(weightedSubset); + weightedSubsets.setTotalWeight(weightedSubsets.getTotalWeight() + weight); + } + return true; + } + + //selectInstances 从subset中选取实例 + private List selectInstances(RouteInfo routeInfo, PrioritySubsets weightedSubsets) { + if (weightedSubsets.getSubsets().size() == 1) { + return weightedSubsets.getSubsets().get(0).instances; + } + Random random = new Random(); + + // 根据区间算法选择subset + long weight = random.nextInt(weightedSubsets.getTotalWeight()); + //匹配的区间下标 + int matchedRange = -1; + for (WeightedSubset weightedSubset : weightedSubsets.getSubsets()) { + weight -= weightedSubset.getWeight(); + if (weight < 0) { + return weightedSubset.getInstances(); + } + } + return Collections.emptyList(); + } + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) { + // 根据匹配过程修改状态, 默认无路由策略状态 + RuleStatus ruleStatus; + // 优先匹配inbound规则, 成功则不需要继续匹配outbound规则 + List destFilteredInstances = new ArrayList<>(); + List sourceFilteredInstances = new ArrayList<>(); + if (routeInfo.getDestRouteRule() != null) { + destFilteredInstances = getRuleFilteredInstances(routeInfo, instances, + RuleMatchType.destRouteRuleMatch); + if (destFilteredInstances.isEmpty()) { + ruleStatus = RuleStatus.destRuleFail; + } else { + ruleStatus = RuleStatus.destRuleSucc; + } + } else { + // 然后匹配outbound规则 + sourceFilteredInstances = getRuleFilteredInstances(routeInfo, instances, + RuleMatchType.sourceRouteRuleMatch); + if (sourceFilteredInstances.isEmpty()) { + ruleStatus = RuleStatus.sourceRuleFail; + } else { + ruleStatus = RuleStatus.sourceRuleSucc; + } + } + switch (ruleStatus) { + case sourceRuleSucc: + return new RouteResult(sourceFilteredInstances, RouteResult.State.Next); + case destRuleSucc: + return new RouteResult(destFilteredInstances, RouteResult.State.Next); + default: + // 如果规则匹配失败, 返回错误 + LOG.error("route rule not match, rule status: {}", ruleStatus); + throw new PolarisException(ErrorCode.ROUTE_RULE_NOT_MATCH, "getFilteredInstances route rule not match"); + } + } + + + private List getHealthyInstances(List instances) { + List healthyInstances = new ArrayList<>(); + for (Instance instance : instances) { + if (instance.isHealthy()) { + healthyInstances.add(instance); + } + } + + return healthyInstances; + } + + private List getHealthyInstancesOrAllIfAllDead(List instances) { + List healthyInstances = getHealthyInstances(instances); + if (!healthyInstances.isEmpty()) { + //如果有健康实例,则只返回健康实例 + LOG.info("getHealthyInstancesOrAllIfAllDead found {} healthy instances, return heathy ones", + healthyInstances.size()); + return healthyInstances; + } + return instances; + } + + private List getInstancesOfEnv(ServiceInstances serviceInstances, String env) { + List instances = serviceInstances.getInstances(); + List retInstances = new ArrayList<>(); + for (Instance ins : instances) { + if (env.equals(ins.getMetadata().get("env"))) { + retInstances.add(ins); + } + } + + return retInstances; + } + + + @Override + public String getName() { + return ServiceRouterConfig.DEFAULT_ROUTER_RULE; + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + globalVariablesConfig = ctx.getConfig().getGlobal().getSystem().getVariables(); + } + + @Override + public ServiceRouter.Aspect getAspect() { + return ServiceRouter.Aspect.MIDDLE; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + List dstRoutes = getRoutesFromRule(routeInfo, RuleMatchType.destRouteRuleMatch); + List srcRoutes = getRoutesFromRule(routeInfo, RuleMatchType.sourceRouteRuleMatch); + return !(CollectionUtils.isEmpty(dstRoutes) && CollectionUtils.isEmpty(srcRoutes)); + } +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleMatchType.java b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleMatchType.java new file mode 100644 index 000000000..df8df083a --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleMatchType.java @@ -0,0 +1,19 @@ +package com.tencent.polaris.plugins.router.rule; + +/** + * 路由规则匹配类型 + * + * @author starkwen + * @date 2020/11/27 11:22 上午 + */ +enum RuleMatchType { + /** + * 主调服务规则匹配 + */ + sourceRouteRuleMatch, + + /** + * 被调服务匹配 + */ + destRouteRuleMatch, +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleStatus.java b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleStatus.java new file mode 100644 index 000000000..6a641d9bb --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/RuleStatus.java @@ -0,0 +1,30 @@ +package com.tencent.polaris.plugins.router.rule; + +/** + * 路由执行结果 + * + * @author starkwen + * @date 2020/11/27 11:22 上午 + */ +enum RuleStatus { + /** + * 无路由策略 + */ + noRule, + /** + * 被调服务路由策略匹配成功 + */ + destRuleSucc, + /** + * 被调服务路由策略匹配失败 + */ + destRuleFail, + /** + * 主调服务路由策略匹配成功 + */ + sourceRuleSucc, + /** + * 主调服务路由策略匹配失败 + */ + sourceRuleFail, +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/WeightedSubset.java b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/WeightedSubset.java new file mode 100644 index 000000000..6b81773ea --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/java/com/tencent/polaris/plugins/router/rule/WeightedSubset.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.router.rule; + +import com.tencent.polaris.api.pojo.Instance; +import java.util.List; + +/** + * 带权重的实例subset + */ +class WeightedSubset { + + // 实例subset + List instances; + // subset列表 + long weight; + + public List getInstances() { + return instances; + } + + public void setInstances(List instances) { + this.instances = instances; + } + + public long getWeight() { + return weight; + } + + public void setWeight(long weight) { + this.weight = weight; + } +} + diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..6dfceecf5 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-rule/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.router.rule.RuleBasedRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-set/pom.xml b/polaris-plugins/polaris-plugins-discovery/router/router-set/pom.xml new file mode 100644 index 000000000..e254117d1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-set/pom.xml @@ -0,0 +1,22 @@ + + + + router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + router-set + + + + com.tencent.nameservice + router-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/java/com/tencent/polaris/plugin/router/set/SetRouter.java b/polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/java/com/tencent/polaris/plugin/router/set/SetRouter.java new file mode 100644 index 000000000..c77fd073f --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/java/com/tencent/polaris/plugin/router/set/SetRouter.java @@ -0,0 +1,239 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugin.router.set; + +import static com.tencent.polaris.api.plugin.route.RouterConstants.SET_ENABLED; +import static com.tencent.polaris.api.plugin.route.RouterConstants.SET_ENABLE_KEY; +import static com.tencent.polaris.api.plugin.route.RouterConstants.SET_NAME_KEY; + +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.RouteResult; +import com.tencent.polaris.api.plugin.route.RouterConstants; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceMetadata; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.plugins.router.common.AbstractServiceRouter; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * set路由 + * + * @author Thrteenwang + * @date 20200613 + */ + +public class SetRouter extends AbstractServiceRouter { + + private static final Logger LOG = LoggerFactory.getLogger(SetRouter.class); + + @Override + public RouteResult router(RouteInfo routeInfo, ServiceInstances instances) + throws PolarisException { + //是否指定了被调的set + if (routeInfo.getDestService() != null && MapUtils + .isNotEmpty(routeInfo.getDestService().getMetadata())) { + Map metadata = routeInfo.getDestService().getMetadata(); + + // 如果指定了被调的set,则不走就近,不论结果如何直接返回 + String setEnabled = metadata.get(RouterConstants.SET_ENABLE_KEY); + if (SET_ENABLED.equals(setEnabled)) { + routeInfo.disableRouter(ServiceRouterConfig.DEFAULT_ROUTER_NEARBY); + String destSetName = metadata.get(RouterConstants.SET_NAME_KEY); + List filteredResult = instances.getInstances().stream().filter(instance -> + calleeSetEnabled(instance) && setIsAllSame(instance, destSetName)) + .collect(Collectors.toList()); + LOG.debug("[doSetRouter] destinationSet result:{},{}", destSetName, + filteredResult.size()); + return new RouteResult(filteredResult, RouteResult.State.Next); + } + } + + //是否指定的主调的set + if (routeInfo.getDestService() != null && MapUtils + .isNotEmpty(routeInfo.getDestService().getMetadata())) { + Map metadata = routeInfo.getDestService().getMetadata(); + String setEnabled = metadata.get(RouterConstants.SET_ENABLE_KEY); + + if (SET_ENABLED.equals(setEnabled)) { + String sourceSetName = metadata.get(RouterConstants.SET_NAME_KEY); + //2、被调是否启用set + boolean calleeSetEnabled = instances.getInstances().stream() + .anyMatch(instance -> calleeSetEnabled(instance) + && setNameIsSame(instance, sourceSetName)); + + LOG.debug("[doSetRouter] calleeSetEnabled={}", calleeSetEnabled); + //没有启用直接返回所有,走就近路由 + if (!calleeSetEnabled) { + return new RouteResult(instances.getInstances(), RouteResult.State.Next); + } + //只要主被调都启用了,就不走就近了 + routeInfo.disableRouter(ServiceRouterConfig.DEFAULT_ROUTER_NEARBY); + + boolean callIsLikeMatch = isLikeMatch(sourceSetName); + LOG.debug("[doSetRouter] callIsLikeMatch={}", callIsLikeMatch); + //如果是完整的三段匹配,先尝试三段匹配 + if (!callIsLikeMatch) { + //3、三段匹配 + List filteredResult = instances.getInstances().stream() + .filter(instance -> setIsAllSame(instance, sourceSetName) + ).collect(Collectors.toList()); + //如果匹配到了 + LOG.debug("[doSetRouter] allMatch result:{},{}", sourceSetName, + filteredResult.size()); + if (CollectionUtils.isNotEmpty(filteredResult)) { + return new RouteResult(filteredResult, RouteResult.State.Next); + } + //同地而且被调最后一段是* + filteredResult = instances.getInstances().stream() + .filter(instance -> setIsSameArea(instance, sourceSetName) + && groupIsLike( + instance) + ).collect(Collectors.toList()); + return new RouteResult(filteredResult, RouteResult.State.Next); + } + //4、两段匹配 + List filteredResult = instances.getInstances().stream() + .filter(instance -> setIsSameArea(instance, sourceSetName) + ).collect(Collectors.toList()); + LOG.debug("[doSetRouter] likeMatch result:{},{}", sourceSetName, + filteredResult.size()); + return new RouteResult(filteredResult, RouteResult.State.Next); + } + } + //直接走就近了 + return new RouteResult(instances.getInstances(), RouteResult.State.Next); + } + + + /** + * 主被调是否三段匹配 + */ + private boolean setIsAllSame(Instance instance, String setName) { + return instance.getMetadata() != null && setName + .equals(instance.getMetadata().get(RouterConstants.SET_NAME_KEY)); + } + + /** + * 是否是模糊匹配 + * + * @param instance 实例对象 + * @return boolean + */ + public boolean groupIsLike(Instance instance) { + String calleeSet = null; + if (instance.getMetadata() != null) { + calleeSet = instance.getMetadata().get(SET_NAME_KEY); + } + return isLikeMatch(calleeSet); + } + + /** + * 分组是否是带*的匹配 + */ + private boolean isLikeMatch(String callSet) { + if (callSet != null) { + String[] callSetTmp = callSet.split("\\."); + return callSetTmp.length == 3 && "*".equals(callSetTmp[2]); + } + return false; + } + + /** + * 被调是否启用了set + */ + private boolean calleeSetEnabled(Instance instance) { + return instance.getMetadata() != null && SET_ENABLED + .equals(instance.getMetadata().get(SET_ENABLE_KEY)); + } + + + /** + * 主调被调是否在同一个地区 + */ + private boolean setIsSameArea(Instance instance, String callSet) { + String calleeSet = null; + if (instance.getMetadata() != null) { + calleeSet = instance.getMetadata().get(SET_NAME_KEY); + } + + if (callSet == null || calleeSet == null) { + return false; + } + String[] callSetTmp = callSet.split("\\."); + String[] calleeSetTmp = calleeSet.split("\\."); + if (calleeSetTmp.length > 2 && callSetTmp.length > 2) { + return calleeSetTmp[0].equals(callSetTmp[0]) && calleeSetTmp[1].equals(callSetTmp[1]); + } + return false; + } + + /** + * 主被调set是否set名相同 + */ + private boolean setNameIsSame(Instance instance, String callerSet) { + String calleeSet = null; + if (instance.getMetadata() != null) { + calleeSet = instance.getMetadata().get(SET_NAME_KEY); + } + if (callerSet == null || calleeSet == null) { + return false; + } + String[] callSetTmp = callerSet.split("\\."); + String[] calleeSetTmp = calleeSet.split("\\."); + if (calleeSetTmp.length > 0 && callSetTmp.length > 0) { + return calleeSetTmp[0].equals(callSetTmp[0]); + } + return false; + } + + @Override + public String getName() { + return "setRouter"; + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_ROUTER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + + } + + @Override + public Aspect getAspect() { + return Aspect.MIDDLE; + } + + @Override + public boolean enable(RouteInfo routeInfo, ServiceMetadata dstSvcInfo) { + return true; + } +} diff --git a/polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter b/polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter new file mode 100644 index 000000000..a5e2b6b50 --- /dev/null +++ b/polaris-plugins/polaris-plugins-discovery/router/router-set/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.route.ServiceRouter @@ -0,0 +1 @@ +com.tencent.polaris.plugin.router.set.SetRouter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/pom.xml b/polaris-plugins/polaris-plugins-observability/pom.xml new file mode 100644 index 000000000..2c0a1addd --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/pom.xml @@ -0,0 +1,37 @@ + + + + polaris-plugins + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins-observability + pom + + stat-prometheus + stat-common + + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/pom.xml b/polaris-plugins/polaris-plugins-observability/stat-common/pom.xml new file mode 100644 index 000000000..37bacbf75 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-plugins-observability + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + stat-common + + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/api/ServiceCallStatCollector.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/api/ServiceCallStatCollector.java new file mode 100644 index 000000000..275784256 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/api/ServiceCallStatCollector.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.api; + +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.stat.StatInfo; +import com.tencent.polaris.api.plugin.stat.StatReporter; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.api.ServiceCallResultListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 用于将每次调用的结果转为StatInfo, + * 并交由statPlugins统计。 + */ +public class ServiceCallStatCollector implements ServiceCallResultListener { + private static final Logger LOG = LoggerFactory.getLogger(ServiceCallStatCollector.class); + + private Collection statPlugins; + + private final AtomicBoolean init = new AtomicBoolean(false); + + /** + * 初始化handler + * + * @param context SDK上下文 + */ + @Override + public void init(SDKContext context) { + if (!init.compareAndSet(false, true)) { + return; + } + + this.statPlugins = context.getPlugins().getPlugins(PluginTypes.STAT_REPORTER.getBaseType()); + } + + /** + * 收到服务上报数据后的回调处理 + * + * @param result 上报数据 + */ + @Override + public void onServiceCallResult(InstanceGauge result) { + if (null == statPlugins) { + return; + } + + try { + for (Plugin statPlugin : statPlugins) { + if (statPlugin instanceof StatReporter) { + ((StatReporter) statPlugin).reportStat(convert(result)); + } + } + } catch (Exception ex) { + LOG.info("service call report encountered exception, e: {}", ex.getMessage()); + } + } + + /** + * 停机释放资源 + */ + @Override + public void destroy() { + if (!init.compareAndSet(true, false)) { + return; + } + + if (null == statPlugins) { + return; + } + + for (Plugin statPlugin : statPlugins) { + statPlugin.destroy(); + } + statPlugins = null; + } + + private static StatInfo convert(InstanceGauge result) { + StatInfo statInfo = new StatInfo(); + statInfo.setRouterGauge(result); + return statInfo; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/AbstractSignatureStatInfoCollector.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/AbstractSignatureStatInfoCollector.java new file mode 100644 index 000000000..9d81b9571 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/AbstractSignatureStatInfoCollector.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import com.tencent.polaris.plugins.stat.common.util.SignatureUtil; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 签名类型的指标收集器,该收集器提供将指标转为签名的方法以及对应的容器。 + * + * @param 收集的指标类型 + * @param 收集指标后统计所采用的类型 + */ +public abstract class AbstractSignatureStatInfoCollector implements StatInfoCollector { + protected ConcurrentMap metricContainer; + + protected AbstractSignatureStatInfoCollector() { + this.metricContainer = new ConcurrentHashMap<>(); + } + + public static Long getSignature(String metricName, Map labels) { + labels.put(SystemMetricModel.SystemMetricName.METRIC_NAME_LABEL, metricName); + return SignatureUtil.labelsToSignature(labels); + } + + public ConcurrentMap getMetricContainer() { + return metricContainer; + } + + @Override + public Collection getCollectedValues() { + return metricContainer.values(); + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategy.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategy.java new file mode 100644 index 000000000..21a93829f --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategy.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +/** + * 维度相关的上报数据的聚合策略 + * + * @param 聚合数据源的数据类型 + */ +public interface MetricValueAggregationStrategy { + /** + * 返回策略的描述信息 + * + * @return 描述信息 + */ + String getStrategyDescription(); + + /** + * 返回策略名称,通常该名称用作metricName + * + * @return 策略名称 + */ + String getStrategyName(); + + /** + * 根据metric自身的value值和聚合数据源T的值来更新metric的value + * + * @param targetValue 待更新的value值 + * @param dataSource 聚合数据源数据 + */ + void updateMetricValue(StatMetric targetValue, T dataSource); + + /** + * 根据数据源的内容获取第一次创建metric的时候的初始值 + * + * @param dataSource 聚合数据源数据 + * @return metric需要的初始值 + */ + double initMetricValue(T dataSource); +} \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategyCollections.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategyCollections.java new file mode 100644 index 000000000..bc71765db --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/MetricValueAggregationStrategyCollections.java @@ -0,0 +1,334 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import com.tencent.polaris.api.plugin.stat.CircuitBreakGauge; +import com.tencent.polaris.api.plugin.stat.RateLimitGauge; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.RetStatus; + +import static com.tencent.polaris.api.pojo.CircuitBreakerStatus.Status.OPEN; +import static com.tencent.polaris.api.pojo.CircuitBreakerStatus.Status.HALF_OPEN; +import static com.tencent.polaris.api.pojo.CircuitBreakerStatus.Status.CLOSE; + +public class MetricValueAggregationStrategyCollections { + public static MetricValueAggregationStrategy[] SERVICE_CALL_STRATEGY; + public static MetricValueAggregationStrategy[] RATE_LIMIT_STRATEGY; + public static MetricValueAggregationStrategy[] CIRCUIT_BREAK_STRATEGY; + + static { + SERVICE_CALL_STRATEGY = new MetricValueAggregationStrategy[]{ + new UpstreamRequestTotalStrategy(), + new UpstreamRequestSuccessStrategy(), + new UpstreamRequestTimeoutStrategy(), + new UpstreamRequestMaxTimeoutStrategy(), + }; + + RATE_LIMIT_STRATEGY = new MetricValueAggregationStrategy[]{ + new RateLimitRequestTotalStrategy(), + new RateLimitRequestPassStrategy(), + new RateLimitRequestLimitStrategy(), + }; + + CIRCUIT_BREAK_STRATEGY = new MetricValueAggregationStrategy[]{ + new CircuitBreakerOpenStrategy(), + new CircuitBreakerHalfOpenStrategy(), + }; + } + + /** + * 服务调用总请求数 + */ + public static class UpstreamRequestTotalStrategy implements MetricValueAggregationStrategy { + + @Override + public String getStrategyDescription() { + return "total of request per period"; + } + + @Override + public String getStrategyName() { + return "upstream_rq_total"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, InstanceGauge dataSource) { + targetValue.incValue(); + } + + @Override + public double initMetricValue(InstanceGauge dataSource) { + return 1.0; + } + } + + /** + * 服务调用总成功数 + */ + public static class UpstreamRequestSuccessStrategy implements MetricValueAggregationStrategy { + @Override + public String getStrategyDescription() { + return "total of success request per period"; + } + + public String getStrategyName() { + return "upstream_rq_success"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, InstanceGauge dataSource) { + if (RetStatus.RetSuccess == dataSource.getRetStatus()) { + targetValue.incValue(); + } + } + + @Override + public double initMetricValue(InstanceGauge dataSource) { + return RetStatus.RetSuccess == dataSource.getRetStatus() ? 1 : 0; + } + } + + /** + * 服务调用总时延 + */ + public static class UpstreamRequestTimeoutStrategy implements MetricValueAggregationStrategy { + + @Override + public String getStrategyDescription() { + return "total of request delay per period"; + } + + @Override + public String getStrategyName() { + return "upstream_rq_timeout"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, InstanceGauge dataSource) { + if (null == dataSource.getDelay()) { + return; + } + + targetValue.addValue(dataSource.getDelay()); + } + + @Override + public double initMetricValue(InstanceGauge dataSource) { + if (null == dataSource.getDelay()) { + return 0.0; + } + + return dataSource.getDelay(); + } + } + + /** + * 服务调用最大时延 + */ + public static class UpstreamRequestMaxTimeoutStrategy implements MetricValueAggregationStrategy { + @Override + public String getStrategyDescription() { + return "maximum request delay per period"; + } + + @Override + public String getStrategyName() { + return "upstream_rq_max_timeout"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, InstanceGauge dataSource) { + if (null == dataSource.getDelay()) { + return; + } + + while (true) { + if (dataSource.getDelay() > targetValue.getValue()) { + if (targetValue.compareAndSet(targetValue.getValue(), dataSource.getDelay())) { + return; + } + } else { + return; + } + } + } + + @Override + public double initMetricValue(InstanceGauge dataSource) { + if (null == dataSource.getDelay()) { + return 0.0; + } + + return dataSource.getDelay(); + } + } + + /** + * 限流调用总请求数 + */ + public static class RateLimitRequestTotalStrategy implements MetricValueAggregationStrategy { + @Override + public String getStrategyDescription() { + return "total of rate limit per period"; + } + + @Override + public String getStrategyName() { + return "ratelimit_rq_total"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, RateLimitGauge dataSource) { + targetValue.incValue(); + } + + @Override + public double initMetricValue(RateLimitGauge dataSource) { + return 1.0; + } + } + + /** + * 限流调用总成功数 + */ + public static class RateLimitRequestPassStrategy implements MetricValueAggregationStrategy { + @Override + public String getStrategyDescription() { + return "total of passed request per period"; + } + + @Override + public String getStrategyName() { + return "ratelimit_rq_pass"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, RateLimitGauge dataSource) { + if (RateLimitGauge.Result.PASSED == dataSource.getResult()) { + targetValue.incValue(); + } + } + + @Override + public double initMetricValue(RateLimitGauge dataSource) { + return RateLimitGauge.Result.PASSED == dataSource.getResult() ? 1.0 : 0.0; + } + } + + /** + * 限流调用总限流数 + */ + public static class RateLimitRequestLimitStrategy implements MetricValueAggregationStrategy { + @Override + public String getStrategyDescription() { + return "total of limited request per period"; + } + + @Override + public String getStrategyName() { + return "ratelimit_rq_limit"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, RateLimitGauge dataSource) { + if (RateLimitGauge.Result.LIMITED == dataSource.getResult()) { + targetValue.incValue(); + } + } + + @Override + public double initMetricValue(RateLimitGauge dataSource) { + return RateLimitGauge.Result.LIMITED == dataSource.getResult() ? 1.0 : 0.0; + } + } + + /** + * 熔断总数 + */ + public static class CircuitBreakerOpenStrategy implements MetricValueAggregationStrategy { + + @Override + public String getStrategyDescription() { + return "total of opened circuit breaker"; + } + + @Override + public String getStrategyName() { + return "circuitbreaker_open"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, CircuitBreakGauge dataSource) { + if (null == dataSource.getCircuitBreakStatus()) { + return; + } + + if (OPEN == dataSource.getCircuitBreakStatus().getStatus()) { + targetValue.incValue(); + } else if (HALF_OPEN == dataSource.getCircuitBreakStatus().getStatus()) { + targetValue.addValue(-1); + } + } + + @Override + public double initMetricValue(CircuitBreakGauge dataSource) { + if (null == dataSource.getCircuitBreakStatus()) { + return 0.0; + } + + return dataSource.getCircuitBreakStatus().getStatus() == OPEN ? 1.0 : 0.0; + } + } + + /** + * 熔断半开数 + */ + public static class CircuitBreakerHalfOpenStrategy implements MetricValueAggregationStrategy { + @Override + public String getStrategyDescription() { + return "total of half-open circuit breaker"; + } + + @Override + public String getStrategyName() { + return "circuitbreaker_halfopen"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, CircuitBreakGauge dataSource) { + if (null == dataSource.getCircuitBreakStatus()) { + return; + } + + if (CLOSE == dataSource.getCircuitBreakStatus().getStatus()) { + targetValue.addValue(-1); + } else if (HALF_OPEN == dataSource.getCircuitBreakStatus().getStatus()) { + targetValue.incValue(); + } + } + + @Override + public double initMetricValue(CircuitBreakGauge dataSource) { + if (null == dataSource.getCircuitBreakStatus()) { + return 0; + } + + return dataSource.getCircuitBreakStatus().getStatus() == HALF_OPEN ? 1 : 0; + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollector.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollector.java new file mode 100644 index 000000000..e1abb9168 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollector.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import java.util.Collection; +import java.util.Map; + +/** + * 采用某种策略收集指标T,并统计为V + * + * @param 收集的指标类型 + * @param 收集指标后统计所采用的类型 + */ +public interface StatInfoCollector { + /** + * 使用Strategies策略集收集info的信息 + * + * @param info 收集的指标类型的值 + * @param metricLabels 收集的指标的标签集 + * @param strategies 收集指标采用的策略集 + */ + void collectStatInfo(T info, Map metricLabels, MetricValueAggregationStrategy[] strategies); + + /** + * 返回统计的结果 + * + * @return 统计的结果值 + */ + Collection getCollectedValues(); +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollectorContainer.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollectorContainer.java new file mode 100644 index 000000000..fdc734a51 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoCollectorContainer.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import com.tencent.polaris.api.plugin.stat.CircuitBreakGauge; +import com.tencent.polaris.api.plugin.stat.RateLimitGauge; +import com.tencent.polaris.api.pojo.InstanceGauge; + +import java.util.Arrays; +import java.util.List; + +public class StatInfoCollectorContainer { + private final StatInfoCollector insCollector; + private final StatInfoCollector rateLimitCollector; + private final StatInfoCollector circuitBreakerCollector; + + public StatInfoCollectorContainer() { + this.insCollector = new StatInfoRevisionCollector(); + this.rateLimitCollector = new StatInfoRevisionCollector(); + this.circuitBreakerCollector = new StatInfoGaugeCollector(); + } + + public StatInfoCollector getInsCollector() { + return insCollector; + } + + public StatInfoCollector getRateLimitCollector() { + return rateLimitCollector; + } + + public StatInfoCollector getCircuitBreakerCollector() { + return circuitBreakerCollector; + } + + public List> getCollectors() { + return Arrays.asList(insCollector, rateLimitCollector, circuitBreakerCollector); + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollector.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollector.java new file mode 100644 index 000000000..3aac53ccf --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollector.java @@ -0,0 +1,63 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import java.util.HashMap; +import java.util.Map; + +/** + * 普通的指标收集器,如果发现没有就添加,如果发现有就更新 + * + * @param 收集的指标类型 + */ +public class StatInfoGaugeCollector extends AbstractSignatureStatInfoCollector { + private final Object mutex = new Object(); + + public StatInfoGaugeCollector() { + super(); + } + + @Override + public void collectStatInfo(T info, + Map metricLabels, + MetricValueAggregationStrategy[] strategies) { + if (null != strategies) { + String metricName; + for (MetricValueAggregationStrategy strategy : strategies) { + metricName = strategy.getStrategyName(); + Map labels = new HashMap<>(metricLabels); + Long signature = getSignature(metricName, labels); + + StatMetric metric = metricContainer.get(signature); + if (null == metric) { + synchronized (mutex) { + metric = metricContainer.get(signature); + if (null == metric) { + StatMetric statMetric = new StatMetric(metricName, labels, signature); + statMetric.setValue(strategy.initMetricValue(info)); + metricContainer.put(signature, statMetric); + continue; + } + } + } + + strategy.updateMetricValue(metric, info); + } + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoHandler.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoHandler.java new file mode 100644 index 000000000..6af47d693 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoHandler.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import com.tencent.polaris.api.plugin.stat.StatInfo; + +/** + * 对StatInfo进行处理类 + */ +public interface StatInfoHandler { + + /** + * 处理StatInfo类型值 + * + * @param statInfo 待处理值 + */ + void handle(StatInfo statInfo); + + /** + * 停止处理StatInfo,通常用于释放资源 + */ + void stopHandle(); +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollector.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollector.java new file mode 100644 index 000000000..ec7774667 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollector.java @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 带有版本信息的指标收集器,当更新版本后,再次收集指标的时候,指标重新计数。 + * + * @param 收集的指标类型 + */ +public class StatInfoRevisionCollector extends AbstractSignatureStatInfoCollector { + private final Object mutex = new Object(); + private final AtomicLong currentRevision; + + public StatInfoRevisionCollector() { + super(); + this.currentRevision = new AtomicLong(); + } + + public long incRevision() { + return currentRevision.incrementAndGet(); + } + + public long getCurrentRevision() { + return currentRevision.get(); + } + + @Override + public void collectStatInfo(T info, + Map metricLabels, + MetricValueAggregationStrategy[] strategies) { + if (null != strategies) { + String metricName; + for (MetricValueAggregationStrategy strategy : strategies) { + metricName = strategy.getStrategyName(); + Map labels = new HashMap<>(metricLabels); + Long signature = getSignature(metricName, labels); + + StatRevisionMetric metrics = metricContainer.get(signature); + if (null == metrics) { + synchronized (mutex) { + metrics = metricContainer.get(signature); + if (null == metrics) { + // 如果该指标不存在,则初始化该指标。 + StatRevisionMetric stateMetric = new StatRevisionMetric(metricName, + labels, + signature, + currentRevision.get()); + stateMetric.setValue(strategy.initMetricValue(info)); + metricContainer.put(signature, stateMetric); + continue; + } + } + } + + if (currentRevision.get() != metrics.getRevision()) { + synchronized (mutex) { + if (currentRevision.get() != metrics.getRevision()) { + // 如果revision不一致,说明该指标记录过期, + // 则reversion设置为当前的reversion,且value重置为初始值。 + metrics.setValue(strategy.initMetricValue(info)); + metrics.setRevision(currentRevision.get()); + continue; + } + } + } + + strategy.updateMetricValue(metrics, info); + } + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatMetric.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatMetric.java new file mode 100644 index 000000000..dc37a8369 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatMetric.java @@ -0,0 +1,87 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import com.google.common.util.concurrent.AtomicDouble; +import com.tencent.polaris.plugins.stat.common.util.SignatureUtil; + +import java.util.Map; + +public class StatMetric { + private final String metricName; + private final Map labels; + private final Long signature; + private final AtomicDouble value; + + public StatMetric(String metricName, Map labels) { + this(metricName, labels, SignatureUtil.labelsToSignature(labels)); + } + + public StatMetric(String metricName, Map labels, Long signature) { + this.metricName = metricName; + this.labels = labels; + this.signature = signature; + this.value = new AtomicDouble(); + } + + public String getMetricName() { + return metricName; + } + + public Map getLabels() { + return labels; + } + + public double getValue() { + return value.doubleValue(); + } + + public void setValue(double value) { + this.value.set(value); + } + + public double addValue(double value) { + return this.value.addAndGet(value); + } + + public double incValue() { + return addValue(1.0); + } + + public boolean compareAndSet(double expect, double update) { + return value.compareAndSet(expect, update); + } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + + if (that == null || getClass() != that.getClass()) { + return false; + } + + return this.signature.equals(((StatMetric) that).signature); + } + + @Override + public int hashCode() { + return signature.intValue(); + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatRevisionMetric.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatRevisionMetric.java new file mode 100644 index 000000000..3a7509767 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/StatRevisionMetric.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +public class StatRevisionMetric extends StatMetric { + private final AtomicLong revision; + + public StatRevisionMetric(String metricName, Map labels, Long signature, long revision) { + super(metricName, labels, signature); + this.revision = new AtomicLong(revision); + } + + public long getRevision() { + return revision.get(); + } + + public void setRevision(long revision) { + this.revision.set(revision); + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/SystemMetricModel.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/SystemMetricModel.java new file mode 100644 index 000000000..a3c363e48 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/model/SystemMetricModel.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.model; + +public class SystemMetricModel { + public static class SystemMetricLabelOrder { + public static final String[] INSTANCE_GAUGE_LABEL_ORDER = new String[]{ + SystemMetricName.CALLEE_NAMESPACE, + SystemMetricName.CALLEE_SERVICE, + SystemMetricName.CALLEE_SUBSET, + SystemMetricName.CALLEE_INSTANCE, + SystemMetricName.CALLEE_RET_CODE, + SystemMetricName.CALLER_LABELS, + SystemMetricName.CALLER_NAMESPACE, + SystemMetricName.CALLER_SERVICE, + SystemMetricName.CALLER_IP, + SystemMetricName.METRIC_NAME_LABEL, + }; + public static final String[] RATELIMIT_GAUGE_LABEL_ORDER = new String[]{ + SystemMetricName.CALLEE_NAMESPACE, + SystemMetricName.CALLEE_SERVICE, + SystemMetricName.CALLEE_METHOD, + SystemMetricName.CALLER_LABELS, + SystemMetricName.METRIC_NAME_LABEL, + }; + public static final String[] CIRCUIT_BREAKER_LABEL_ORDER = new String[]{ + SystemMetricName.CALLEE_NAMESPACE, + SystemMetricName.CALLEE_SERVICE, + SystemMetricName.CALLEE_METHOD, + SystemMetricName.CALLEE_SUBSET, + SystemMetricName.CALLEE_INSTANCE, + SystemMetricName.CALLER_NAMESPACE, + SystemMetricName.CALLER_SERVICE, + SystemMetricName.CALLER_IP, + SystemMetricName.METRIC_NAME_LABEL, + }; + } + + public static class SystemMetricName { + public static final String CALLEE_NAMESPACE = "callee_namespace"; + public static final String CALLEE_SERVICE = "callee_service"; + public static final String CALLEE_SUBSET = "callee_subset"; + public static final String CALLEE_INSTANCE = "callee_instance"; + public static final String CALLEE_RET_CODE = "callee_result_code"; + public static final String CALLEE_METHOD = "callee_method"; + public static final String CALLER_NAMESPACE = "caller_namespace"; + public static final String CALLER_SERVICE = "caller_service"; + public static final String CALLER_IP = "caller_ip"; + public static final String CALLER_LABELS = "caller_labels"; + public static final String METRIC_NAME_LABEL = "metric_name"; + } + + public static class SystemMetricValue { + public static final String NULL_VALUE = "__NULL__"; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/FnvHash.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/FnvHash.java new file mode 100644 index 000000000..2fab33908 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/FnvHash.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.util; + +import java.nio.charset.StandardCharsets; + +public class FnvHash { + static final long offset64 = 0xcbf29ce484222325L; //14695981039346656037 + static final long prime64 = 1099511628211L; + + static long hashNew() { + return offset64; + } + + static long hashAdd(long h, String s) { + byte[] bArray = s.getBytes(StandardCharsets.UTF_8); + for (byte b : bArray) { + h = hashAddByte(h, b); + } + return h; + } + + static long hashAddByte(long h, byte b) { + h ^= b; + h *= prime64; + return h; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtil.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtil.java new file mode 100644 index 000000000..f5fccfcd9 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtil.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.common.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class SignatureUtil { + public static final byte SEPARATOR_BYTE = -1; //255 + + static long emptyLabelSignature = FnvHash.hashNew(); + + public static long labelsToSignature(Map labels) { + if (labels == null || labels.size() == 0) { + return emptyLabelSignature; + } + + List labelNames = new ArrayList<>(labels.keySet()); + Collections.sort(labelNames); + + long sum = FnvHash.hashNew(); + for (String labelName : labelNames) { + sum = FnvHash.hashAdd(sum, labelName); + sum = FnvHash.hashAddByte(sum, SEPARATOR_BYTE); + sum = FnvHash.hashAdd(sum, labels.get(labelName)); + sum = FnvHash.hashAddByte(sum, SEPARATOR_BYTE); + } + return sum; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener new file mode 100644 index 000000000..ed717e5be --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/main/resources/META-INF/services/com.tencent.polaris.client.api.ServiceCallResultListener @@ -0,0 +1 @@ +com.tencent.polaris.plugins.stat.common.api.ServiceCallStatCollector \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/TestUtil.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/TestUtil.java new file mode 100644 index 000000000..a32be52ce --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/TestUtil.java @@ -0,0 +1,32 @@ +package com.tencent.polaris.plugins.stat.common; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +public class TestUtil { + public static String getRandomString(int minSize, int maxSize) { + String dictionary = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + int dicLength = dictionary.length(); + Random random = new Random(); + StringBuilder sb = new StringBuilder(maxSize); + int strLength = random.nextInt(maxSize - minSize) + minSize; + for (int i = 0; i < strLength; i++) { + int numIndex = random.nextInt(dicLength); + sb.append(dictionary.charAt(numIndex)); + } + return sb.toString(); + } + + public static Map getRandomLabels() { + Map testLabels = new HashMap(); + Random random = new Random(); + String key, value; + for (int j = 0; j < (random.nextInt(10) + 1); j++) { + key = getRandomString(3, 10); + value = getRandomString(3, 10); + testLabels.put(key, value); + } + return testLabels; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollectorTest.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollectorTest.java new file mode 100644 index 000000000..2a2747448 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoGaugeCollectorTest.java @@ -0,0 +1,118 @@ +package com.tencent.polaris.plugins.stat.common.model; + +import com.google.common.util.concurrent.AtomicDouble; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.tencent.polaris.plugins.stat.common.TestUtil.getRandomLabels; + +public class StatInfoGaugeCollectorTest { + + @Test + public void testCollectStatInfo() throws InterruptedException { + Random random = new Random(); + StatInfoGaugeCollector collector = new StatInfoGaugeCollector(); + Map labels = getRandomLabels(); + double threshold = 0.5; + MetricValueAggregationStrategy maxIncStrategy = new MaxThresholdIncStrategy(threshold); + MetricValueAggregationStrategy minIncStrategy = new MinThresholdIncStrategy(threshold); + MetricValueAggregationStrategy[] strategies= new MetricValueAggregationStrategy[]{ + maxIncStrategy, minIncStrategy + }; + + // 并发收集数据 + int count = random.nextInt(10) + 10; + AtomicInteger maxIncExpected = new AtomicInteger(); + AtomicInteger minIncExpected = new AtomicInteger(); + CountDownLatch latch = new CountDownLatch(count); + for (int i = 0; i < count; i++) { + new Thread(() -> { + double n = random.nextDouble(); + if (n > threshold) { + maxIncExpected.incrementAndGet(); + } else { + minIncExpected.incrementAndGet(); + } + collector.collectStatInfo(new AtomicDouble(n), labels, strategies); + latch.countDown(); + }).start(); + } + latch.await(); + + // 验证 + StatMetric maxIncMetric = collector.getMetricContainer().get( + AbstractSignatureStatInfoCollector.getSignature(maxIncStrategy.getStrategyName(), labels)); + StatMetric minIncMetric = collector.getMetricContainer().get( + AbstractSignatureStatInfoCollector.getSignature(minIncStrategy.getStrategyName(), labels)); + Assert.assertEquals(maxIncExpected.get(), maxIncMetric.getValue(), 0); + Assert.assertEquals(minIncExpected.get(), minIncMetric.getValue(), 0); + Assert.assertEquals(minIncMetric.getValue() + maxIncMetric.getValue(), count, 0); + } + + private static class MaxThresholdIncStrategy implements MetricValueAggregationStrategy { + + private final double threshold; + + private MaxThresholdIncStrategy(double threshold) { + this.threshold = threshold; + } + + @Override + public String getStrategyDescription() { + return "max threshold inc strategy"; + } + + @Override + public String getStrategyName() { + return "max_strategy"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, AtomicDouble dataSource) { + if (dataSource.get() > threshold) { + targetValue.incValue(); + } + } + + @Override + public double initMetricValue(AtomicDouble dataSource) { + return dataSource.get() > threshold ? 1.0 : 0.0; + } + } + + private static class MinThresholdIncStrategy implements MetricValueAggregationStrategy { + + private final double threshold; + + private MinThresholdIncStrategy(double threshold) { + this.threshold = threshold; + } + + @Override + public String getStrategyDescription() { + return "min threshold inc strategy"; + } + + @Override + public String getStrategyName() { + return "min_strategy"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, AtomicDouble dataSource) { + if (dataSource.get() <= threshold) { + targetValue.incValue(); + } + } + + @Override + public double initMetricValue(AtomicDouble dataSource) { + return dataSource.get() <= threshold ? 1.0 : 0.0; + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollectorTest.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollectorTest.java new file mode 100644 index 000000000..af007ea78 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatInfoRevisionCollectorTest.java @@ -0,0 +1,79 @@ +package com.tencent.polaris.plugins.stat.common.model; + +import com.google.common.util.concurrent.AtomicDouble; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static com.tencent.polaris.plugins.stat.common.TestUtil.getRandomLabels; + +public class StatInfoRevisionCollectorTest { + private StatInfoRevisionCollector collector; + private Map labels; + private MetricValueAggregationStrategy strategy; + private MetricValueAggregationStrategy[] strategies; + + @Before + public void setUp() { + collector = new StatInfoRevisionCollector(); + labels = getRandomLabels(); + strategy = new TestIncStrategy(); + strategies = new MetricValueAggregationStrategy[]{strategy}; + } + + @Test + public void testCollectStatInfo() throws InterruptedException { + // 并发收集数据 + int count = 10; + addTestValues(count); + StatRevisionMetric metric = collector.getMetricContainer().get( + AbstractSignatureStatInfoCollector.getSignature( + strategy.getStrategyName(), + labels)); + Assert.assertEquals(metric.getValue(), count, 0); + + // 更新周期,并再次收集数据 + collector.incRevision(); + count = 20; + addTestValues(count); + Assert.assertEquals(metric.getValue(), count, 0); + Assert.assertEquals(metric.getRevision(), collector.getCurrentRevision()); + } + + private void addTestValues(int count) throws InterruptedException { + CountDownLatch latch = new CountDownLatch(count); + for (int i = 0; i < count; i++) { + new Thread(() -> { + collector.collectStatInfo(new AtomicDouble(1.0), labels, strategies); + latch.countDown(); + }).start(); + } + latch.await(); + } + + private static class TestIncStrategy implements MetricValueAggregationStrategy { + + @Override + public String getStrategyDescription() { + return "test inc strategy"; + } + + @Override + public String getStrategyName() { + return "test_inc_strategy"; + } + + @Override + public void updateMetricValue(StatMetric targetValue, AtomicDouble dataSource) { + targetValue.addValue(dataSource.doubleValue()); + } + + @Override + public double initMetricValue(AtomicDouble dataSource) { + return dataSource.get(); + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatMetricTest.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatMetricTest.java new file mode 100644 index 000000000..55669777f --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/model/StatMetricTest.java @@ -0,0 +1,46 @@ +package com.tencent.polaris.plugins.stat.common.model; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static com.tencent.polaris.plugins.stat.common.TestUtil.getRandomLabels; +import static com.tencent.polaris.plugins.stat.common.TestUtil.getRandomString; + +public class StatMetricTest { + @Test + public void testIncValue() { + String metricName = getRandomString(3, 10); + Map testLabels = getRandomLabels(); + StatMetric statMetric = new StatMetric(metricName, testLabels); + + int count = 10; + CountDownLatch latch = new CountDownLatch(count); + for (int i = 0; i < count; i++) { + new Thread(() -> { + try { + statMetric.incValue(); + } finally { + latch.countDown(); + } + }).start(); + } + + try { + latch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + Assert.assertEquals(statMetric.getValue(), count, 0); + } + + @Test + public void testEquals() { + String metricName = getRandomString(3, 10); + Map testLabels = getRandomLabels(); + Assert.assertEquals(new StatMetric(metricName, testLabels), new StatMetric(metricName, testLabels)); + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtilTest.java b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtilTest.java new file mode 100644 index 000000000..e8804f965 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-common/src/test/java/com/tencent/polaris/plugins/stat/common/util/SignatureUtilTest.java @@ -0,0 +1,27 @@ +package com.tencent.polaris.plugins.stat.common.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +import static com.tencent.polaris.plugins.stat.common.TestUtil.getRandomString; + +public class SignatureUtilTest { + @Test + public void testLabelsToSignature1() { + Set signatures = new HashSet<>(); + for (int i = 0; i < 10000000; i++) { + Random random = new Random(); + String key, value; + Map testLabels = new HashMap(); + for (int j = 0; j < (random.nextInt(10) + 1); j++) { + key = getRandomString(3, 10); + value = getRandomString(3, 10); + testLabels.put(key, value); + } + + Assert.assertTrue(signatures.add(SignatureUtil.labelsToSignature(testLabels))); + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/pom.xml b/polaris-plugins/polaris-plugins-observability/stat-prometheus/pom.xml new file mode 100644 index 000000000..5ca8a19b6 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-plugins-observability + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + stat-prometheus + + + com.tencent.nameservice + stat-common + ${project.version} + + + + io.prometheus + simpleclient_pushgateway + ${prometheus.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandler.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandler.java new file mode 100644 index 000000000..9ad1dc14f --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandler.java @@ -0,0 +1,391 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.prometheus.handler; + +import com.tencent.polaris.api.plugin.stat.CircuitBreakGauge; +import com.tencent.polaris.api.plugin.stat.RateLimitGauge; +import com.tencent.polaris.api.plugin.stat.StatInfo; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.plugins.stat.common.model.MetricValueAggregationStrategy; +import com.tencent.polaris.plugins.stat.common.model.StatInfoHandler; +import com.tencent.polaris.plugins.stat.common.model.StatMetric; +import com.tencent.polaris.plugins.stat.common.model.StatInfoCollector; +import com.tencent.polaris.plugins.stat.common.model.StatInfoRevisionCollector; +import com.tencent.polaris.plugins.stat.common.model.StatInfoCollectorContainer; +import com.tencent.polaris.plugins.stat.common.model.MetricValueAggregationStrategyCollections; +import com.tencent.polaris.plugins.stat.common.model.SystemMetricModel; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.Gauge; +import io.prometheus.client.exporter.PushGateway; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import static com.tencent.polaris.plugins.stat.common.model.SystemMetricModel.SystemMetricLabelOrder; +import static com.tencent.polaris.plugins.stat.common.model.SystemMetricModel.SystemMetricName; +import static com.tencent.polaris.plugins.stat.common.model.SystemMetricModel.SystemMetricValue.NULL_VALUE; + +/** + * 通过向Prometheus PushGateWay推送StatInfo消息来处理StatInfo。 + */ +public class PrometheusPushHandler implements StatInfoHandler { + private static final Logger LOG = LoggerFactory.getLogger(PrometheusPushHandler.class); + + public static final int PUSH_INTERVALS = 30; + public static final String PUSH_DEFAULT_ADDRESS = "127.0.0.1:9091"; + public static final String PUSH_DEFAULT_JOB_NAME = "defaultJobName"; + + // schedule + private final AtomicBoolean firstHandle = new AtomicBoolean(false); + private ScheduledExecutorService scheduledPushTask; + // storage + private StatInfoCollectorContainer container; + // push + private final String callerIp; + private final String jobName; + private final long pushIntervalS; + private final String pushAddress; + private final CollectorRegistry promRegistry; + private final Map sampleMapping; + private final PushAddressProvider addressProvider; + private PushGateway pushGateway; + + /** + * 构造函数 + * + * @param callerIp 调用者Ip + * @param jobName 向PushGateWay推送使用任务名称 + * @param pushIntervalS 向PushGateWay推送的时间间隔 + * @param provider push的地址提供者 + */ + public PrometheusPushHandler(String callerIp, String jobName, Long pushIntervalS, PushAddressProvider provider) { + this.callerIp = callerIp; + this.container = new StatInfoCollectorContainer(); + this.sampleMapping = new HashMap<>(); + this.promRegistry = new CollectorRegistry(true); + this.addressProvider = provider; + if (null != jobName) { + this.jobName = jobName; + } else { + this.jobName = PUSH_DEFAULT_JOB_NAME; + } + if (null != provider && null != provider.getAddress()) { + this.pushAddress = provider.getAddress(); + } else { + this.pushAddress = PUSH_DEFAULT_ADDRESS; + } + if (null != pushIntervalS) { + this.pushIntervalS = pushIntervalS; + } else { + this.pushIntervalS = PUSH_INTERVALS; + } + this.pushGateway = new PushGateway(this.pushAddress); + this.scheduledPushTask = Executors.newSingleThreadScheduledExecutor(); + + initSampleMapping(MetricValueAggregationStrategyCollections.SERVICE_CALL_STRATEGY, + SystemMetricLabelOrder.INSTANCE_GAUGE_LABEL_ORDER); + initSampleMapping(MetricValueAggregationStrategyCollections.RATE_LIMIT_STRATEGY, + SystemMetricLabelOrder.RATELIMIT_GAUGE_LABEL_ORDER); + initSampleMapping(MetricValueAggregationStrategyCollections.CIRCUIT_BREAK_STRATEGY, + SystemMetricLabelOrder.CIRCUIT_BREAKER_LABEL_ORDER); + } + + private void initSampleMapping(MetricValueAggregationStrategy[] strategies, String[] order) { + for (MetricValueAggregationStrategy strategy : strategies) { + Gauge strategyGauge = new Gauge.Builder() + .name(strategy.getStrategyName()) + .help(strategy.getStrategyDescription()) + .labelNames(order) + .create() + .register(promRegistry); + sampleMapping.put(strategy.getStrategyName(), strategyGauge); + } + } + + @Override + public void handle(StatInfo statInfo) { + if (firstHandle.compareAndSet(false, true)) { + startSchedulePushTask(); + } + + if (null == statInfo) { + return; + } + + if (null != statInfo.getRouterGauge()) { + handleRouterGauge(statInfo.getRouterGauge()); + } + + if (null != statInfo.getCircuitBreakGauge()) { + handleCircuitBreakGauge(statInfo.getCircuitBreakGauge()); + } + + if (null != statInfo.getRateLimitGauge()) { + handleRateLimitGauge(statInfo.getRateLimitGauge()); + } + } + + public void handleRouterGauge(InstanceGauge instanceGauge) { + if (null != container && null != container.getInsCollector()) { + container.getInsCollector().collectStatInfo(instanceGauge, + convertInsGaugeToLabels(instanceGauge), + MetricValueAggregationStrategyCollections.SERVICE_CALL_STRATEGY); + } + } + + public void handleRateLimitGauge(RateLimitGauge rateLimitGauge) { + if (null != container && null != container.getRateLimitCollector()) { + container.getRateLimitCollector().collectStatInfo(rateLimitGauge, + convertRateLimitGaugeToLabels(rateLimitGauge), + MetricValueAggregationStrategyCollections.RATE_LIMIT_STRATEGY); + } + } + + public void handleCircuitBreakGauge(CircuitBreakGauge circuitBreakGauge) { + if (null != container && null != container.getCircuitBreakerCollector()) { + container.getCircuitBreakerCollector().collectStatInfo(circuitBreakGauge, + convertCircuitBreakToLabels(circuitBreakGauge), + MetricValueAggregationStrategyCollections.CIRCUIT_BREAK_STRATEGY); + } + } + + @Override + public void stopHandle() { + if (container != null) { + container = null; + } + + if (scheduledPushTask != null) { + scheduledPushTask.shutdown(); + scheduledPushTask = null; + } + } + + private void startSchedulePushTask() { + if (null != container && null != scheduledPushTask && null != pushGateway && null != sampleMapping) { + this.scheduledPushTask.scheduleWithFixedDelay(this::doPush, + pushIntervalS, + pushIntervalS, + TimeUnit.SECONDS); + } + } + + private void doPush() { + try { + putDataFromContainerInOrder(container.getInsCollector().getCollectedValues(), + SystemMetricLabelOrder.INSTANCE_GAUGE_LABEL_ORDER); + putDataFromContainerInOrder(container.getRateLimitCollector().getCollectedValues(), + SystemMetricLabelOrder.RATELIMIT_GAUGE_LABEL_ORDER); + putDataFromContainerInOrder(container.getCircuitBreakerCollector().getCollectedValues(), + SystemMetricLabelOrder.CIRCUIT_BREAKER_LABEL_ORDER); + + try { + pushGateway.pushAdd(promRegistry, jobName); + LOG.info("push result to push-gateway {} success", pushAddress); + } catch (IOException exception) { + LOG.error("push result to push-gateway {} encountered exception, exception:{}", pushAddress, + exception.getMessage()); + + if (null != addressProvider) { + String newAddress = addressProvider.getAddress(); + if (newAddress != null && !pushAddress.equals(newAddress)) { + pushGateway = new PushGateway(newAddress); + } + } + return; + } + + for (StatInfoCollector s : container.getCollectors()) { + if (s instanceof StatInfoRevisionCollector) { + long currentRevision = ((StatInfoRevisionCollector) s).incRevision(); + LOG.debug("RevisionCollector inc current revision to {}", currentRevision); + } + } + } catch (Exception e) { + LOG.error("push result to push-gateway {} encountered exception, exception:{}", pushAddress, + e.getMessage()); + e.printStackTrace(); + } + } + + private void putDataFromContainerInOrder(Collection values, String[] order) { + if (null == values) { + return; + } + + for (StatMetric s : values) { + Gauge gauge = sampleMapping.get(s.getMetricName()); + if (null != gauge) { + Gauge.Child child = gauge.labels(getOrderedMetricLabelValues(s.getLabels(), order)); + if (null != child) { + child.set(s.getValue()); + } + } + } + } + + public static String[] getOrderedMetricLabelValues(Map labels, String[] orderedKey) { + String[] orderValue = new String[orderedKey.length]; + for (int i = 0; i < orderedKey.length; i++) { + orderValue[i] = labels.getOrDefault(orderedKey[i], NULL_VALUE); + } + return orderValue; + } + + protected Map convertInsGaugeToLabels(InstanceGauge insGauge) { + Map labels = new HashMap<>(); + for (String labelName : SystemMetricLabelOrder.INSTANCE_GAUGE_LABEL_ORDER) { + switch (labelName) { + case SystemMetricName.CALLEE_NAMESPACE: + addLabel(labelName, insGauge.getNamespace(), labels); + break; + case SystemMetricModel.SystemMetricName.CALLEE_SERVICE: + addLabel(labelName, insGauge.getService(), labels); + break; + case SystemMetricName.CALLEE_SUBSET: + addLabel(labelName, insGauge.getSubset(), labels); + break; + case SystemMetricModel.SystemMetricName.CALLEE_INSTANCE: + addLabel(labelName, buildAddress(insGauge.getHost(), insGauge.getPort()), labels); + break; + case SystemMetricName.CALLEE_RET_CODE: + String retCodeStr = + null == insGauge.getRetCode() ? null : insGauge.getRetCode().toString(); + addLabel(labelName, retCodeStr, labels); + break; + case SystemMetricName.CALLER_LABELS: + addLabel(labelName, insGauge.getLabels(), labels); + break; + case SystemMetricName.CALLER_NAMESPACE: + String namespace = + null == insGauge.getCallerService() ? null : insGauge.getCallerService().getNamespace(); + addLabel(labelName, namespace, labels); + break; + case SystemMetricName.CALLER_SERVICE: + String serviceName = + null == insGauge.getCallerService() ? null : insGauge.getCallerService().getService(); + addLabel(labelName, serviceName, labels); + break; + case SystemMetricName.CALLER_IP: + addLabel(labelName, callerIp, labels); + break; + default: + } + } + + return labels; + } + + protected Map convertRateLimitGaugeToLabels(RateLimitGauge rateLimitGauge) { + Map labels = new HashMap<>(); + for (String labelName : SystemMetricModel.SystemMetricLabelOrder.RATELIMIT_GAUGE_LABEL_ORDER) { + switch (labelName) { + case SystemMetricName.CALLEE_NAMESPACE: + addLabel(labelName, rateLimitGauge.getNamespace(), labels); + break; + case SystemMetricName.CALLEE_SERVICE: + addLabel(labelName, rateLimitGauge.getService(), labels); + break; + case SystemMetricName.CALLEE_METHOD: + addLabel(labelName, rateLimitGauge.getMethod(), labels); + break; + case SystemMetricName.CALLER_LABELS: + addLabel(labelName, rateLimitGauge.getLabels(), labels); + break; + default: + } + } + + return labels; + } + + protected Map convertCircuitBreakToLabels(CircuitBreakGauge gauge) { + Map labels = new HashMap<>(); + for (String labelName : SystemMetricModel.SystemMetricLabelOrder.CIRCUIT_BREAKER_LABEL_ORDER) { + switch (labelName) { + case SystemMetricName.CALLEE_NAMESPACE: + addLabel(labelName, gauge.getNamespace(), labels); + break; + case SystemMetricName.CALLEE_SERVICE: + addLabel(labelName, gauge.getService(), labels); + break; + case SystemMetricName.CALLEE_METHOD: + addLabel(labelName, gauge.getMethod(), labels); + break; + case SystemMetricName.CALLEE_SUBSET: + addLabel(labelName, gauge.getSubset(), labels); + break; + case SystemMetricName.CALLEE_INSTANCE: + addLabel(labelName, buildAddress(gauge.getHost(), gauge.getPort()), labels); + break; + case SystemMetricName.CALLER_NAMESPACE: + String namespace = + null == gauge.getCallerService() ? null : gauge.getCallerService().getNamespace(); + addLabel(labelName, namespace, labels); + break; + case SystemMetricName.CALLER_SERVICE: + String serviceName = + null == gauge.getCallerService() ? null : gauge.getCallerService().getService(); + addLabel(labelName, serviceName, labels); + break; + case SystemMetricName.CALLER_IP: + addLabel(labelName, callerIp, labels); + break; + default: + } + } + + return labels; + } + + private void addLabel(String key, String value, Map target) { + if (null == key) { + return; + } + + if (null == value) { + value = NULL_VALUE; + } + + target.put(key, value); + } + + private static String buildAddress(String host, int port) { + if (null == host) { + host = NULL_VALUE; + } + + return host + ":" + port; + } + + protected void setPushGateway(PushGateway pushGateway) { + this.pushGateway = pushGateway; + } + + protected CollectorRegistry getPromRegistry() { + return promRegistry; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfig.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfig.java new file mode 100644 index 000000000..db4fd479d --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfig.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.prometheus.handler; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tencent.polaris.api.config.verify.Verifier; +import com.tencent.polaris.factory.util.ConfigUtils; + +public class PrometheusPushHandlerConfig implements Verifier { + public static final String PROMETHEUS_PUSH_CONFIG_NAME = "pushgatewayConfig"; + + @JsonProperty + private String jobName; + + @JsonProperty + private String serviceName; + + @JsonProperty + private Long pushInterval; + + public PrometheusPushHandlerConfig() { + } + + /** + * 执行校验操作,参数校验失败会抛出IllegalArgumentException + */ + @Override + public void verify() { + ConfigUtils.validateInterval(pushInterval, "prometheus push-gateway interval"); + ConfigUtils.validateString(jobName, "prometheus push-gateway job name"); + ConfigUtils.validateString(serviceName, "prometheus push-gateway service name"); + } + + /** + * 设置默认值信息 + * + * @param defaultObject 默认值对象 + */ + @Override + public void setDefault(Object defaultObject) { + if (null != defaultObject) { + PrometheusPushHandlerConfig config = (PrometheusPushHandlerConfig) defaultObject; + if (null == jobName) { + setJobName(config.getJobName()); + } + if (null == serviceName) { + setServiceName(config.getServiceName()); + } + if (null == pushInterval) { + setPushInterval(config.getPushInterval()); + } + } + } + + public String getJobName() { + return jobName; + } + + public void setJobName(String jobName) { + this.jobName = jobName; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public Long getPushInterval() { + return pushInterval; + } + + public void setPushInterval(Long pushInterval) { + this.pushInterval = pushInterval; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfigProvider.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfigProvider.java new file mode 100644 index 000000000..a36c43710 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerConfigProvider.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.prometheus.handler; + +import com.tencent.polaris.api.config.plugin.PluginConfigProvider; +import com.tencent.polaris.api.config.verify.Verifier; + +public class PrometheusPushHandlerConfigProvider implements PluginConfigProvider { + /** + * 插件名 + * + * @return name + */ + @Override + public String getName() { + return PrometheusPushHandlerConfig.PROMETHEUS_PUSH_CONFIG_NAME; + } + + /** + * 获取插件配置类型 + * + * @return config clazz + */ + @Override + public Class getPluginConfigClazz() { + return PrometheusPushHandlerConfig.class; + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PushAddressProvider.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PushAddressProvider.java new file mode 100644 index 000000000..37c338462 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/PushAddressProvider.java @@ -0,0 +1,22 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.prometheus.handler; + +public interface PushAddressProvider { + String getAddress(); +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/ServiceDiscoveryProvider.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/ServiceDiscoveryProvider.java new file mode 100644 index 000000000..be4e51cd9 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/handler/ServiceDiscoveryProvider.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.prometheus.handler; + +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.client.flow.BaseFlow; + +public class ServiceDiscoveryProvider implements PushAddressProvider { + private final Extensions extensions; + private final ServerServiceInfo serverServiceInfo; + + public ServiceDiscoveryProvider(Extensions extensions, ServerServiceInfo serverServiceInfo) { + this.extensions = extensions; + this.serverServiceInfo = serverServiceInfo; + } + + @Override + public String getAddress() { + if (null == extensions || null == serverServiceInfo) { + return null; + } + + Instance instance = BaseFlow.commonGetOneInstance(extensions, + serverServiceInfo.getServiceKey(), + serverServiceInfo.getRouters(), + serverServiceInfo.getLbPolicy(), + extensions.getConfiguration().getGlobal().getServerConnector().getProtocol(), + extensions.getValueContext().getClientId()); + + if (null != instance) { + return instance.getHost() + ":" + instance.getPort(); + } else { + return null; + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/plugin/PrometheusReporter.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/plugin/PrometheusReporter.java new file mode 100644 index 000000000..485b5609c --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/java/com/tencent/polaris/plugins/stat/prometheus/plugin/PrometheusReporter.java @@ -0,0 +1,97 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.stat.prometheus.plugin; + +import com.tencent.polaris.api.config.global.ClusterType; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.compose.ServerServiceInfo; +import com.tencent.polaris.api.plugin.stat.StatInfo; +import com.tencent.polaris.api.plugin.stat.StatReporter; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.plugins.stat.prometheus.handler.PrometheusPushHandler; +import com.tencent.polaris.plugins.stat.prometheus.handler.PrometheusPushHandlerConfig; +import com.tencent.polaris.plugins.stat.common.model.StatInfoHandler; +import com.tencent.polaris.plugins.stat.prometheus.handler.PushAddressProvider; +import com.tencent.polaris.plugins.stat.prometheus.handler.ServiceDiscoveryProvider; + +import java.util.Collection; + +public class PrometheusReporter implements StatReporter { + private StatInfoHandler statInfoHandler; + private ServerServiceInfo monitorService; + + @Override + public void init(InitContext initContext) throws PolarisException { + Collection serverServices = initContext.getServerServices(); + if (CollectionUtils.isNotEmpty(serverServices)) { + for (ServerServiceInfo serverService : serverServices) { + if (serverService.getClusterType() == ClusterType.MONITOR_CLUSTER) { + this.monitorService = serverService; + return; + } + } + } + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + if (extensions.getConfiguration().getGlobal().getStatReporter().isEnable()) { + PrometheusPushHandlerConfig config = extensions.getConfiguration() + .getGlobal() + .getStatReporter() + .getPluginConfig(PrometheusPushHandlerConfig.PROMETHEUS_PUSH_CONFIG_NAME, + PrometheusPushHandlerConfig.class); + if (null != config) { + PushAddressProvider provider = new ServiceDiscoveryProvider(extensions, monitorService); + statInfoHandler = new PrometheusPushHandler(extensions.getValueContext().getHost(), + config.getJobName(), + config.getPushInterval(), + provider); + } + } + } + + @Override + public void reportStat(StatInfo statInfo) { + if (null != statInfoHandler && null != statInfo) { + statInfoHandler.handle(statInfo); + } + } + + @Override + public String getName() { + return this.getClass().getSimpleName(); + } + + @Override + public PluginType getType() { + return PluginTypes.STAT_REPORTER.getBaseType(); + } + + @Override + public void destroy() { + if (null != statInfoHandler) { + statInfoHandler.stopHandle(); + statInfoHandler = null; + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider new file mode 100644 index 000000000..344137ab7 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.config.plugin.PluginConfigProvider @@ -0,0 +1 @@ +com.tencent.polaris.plugins.stat.prometheus.handler.PrometheusPushHandlerConfigProvider \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.stat.StatReporter b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.stat.StatReporter new file mode 100644 index 000000000..f2e3ae65c --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.stat.StatReporter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.stat.prometheus.plugin.PrometheusReporter \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerTest.java b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerTest.java new file mode 100644 index 000000000..c718821e2 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/java/com/tencent/polaris/plugins/stat/prometheus/handler/PrometheusPushHandlerTest.java @@ -0,0 +1,445 @@ +package com.tencent.polaris.plugins.stat.prometheus.handler; + +import com.tencent.polaris.api.plugin.stat.*; +import com.tencent.polaris.api.pojo.CircuitBreakerStatus; +import com.tencent.polaris.api.pojo.InstanceGauge; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import com.tencent.polaris.plugins.stat.common.model.MetricValueAggregationStrategy; +import com.tencent.polaris.plugins.stat.common.model.MetricValueAggregationStrategyCollections; +import com.tencent.polaris.plugins.stat.common.model.StatRevisionMetric; +import com.tencent.polaris.plugins.stat.common.model.SystemMetricModel; +import com.tencent.polaris.plugins.stat.common.model.SystemMetricModel.SystemMetricLabelOrder; +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.PushGateway; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicLong; + +public class PrometheusPushHandlerTest { + private static final Logger LOG = LoggerFactory.getLogger(PrometheusPushHandlerTest.class); + + private final Random random = new Random(); + private PrometheusPushHandler handler; + private long pushInterval; + + @Before + public void setUp() { + String callerIp = "127.0.0.1"; + MockPushGateway pgw = new MockPushGateway(PrometheusPushHandler.PUSH_DEFAULT_ADDRESS); + pushInterval = 2L; + handler = new PrometheusPushHandler(callerIp, null, pushInterval, new MockAddressProvider()); + handler.setPushGateway(pgw); + } + + @Test + public void testPushNullStatInfo() throws InterruptedException { + StatInfo statInfo = new StatInfo(); + handler.handle(statInfo); + handler.handle(null); + + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + } + + @Test + public void testPushInstanceGaugeConcurrency() throws InterruptedException { + batchDone(() -> { + StatInfo statInfo = new StatInfo(); + statInfo.setRouterGauge(mockServiceCallResult()); + handler.handle(statInfo); + }, 10); + + // 等待上报的内容 + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + } + + @Test + public void testServiceCallTotalStrategy() throws InterruptedException { + StatInfo statInfo = new StatInfo(); + ServiceCallResult callResult = mockServiceCallResult(); + statInfo.setRouterGauge(callResult); + int count = 10; + batchDone(() -> handler.handle(statInfo), count); + + // mock pushing + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + Double result = getServiceCallTotalResult(callResult); + Assert.assertEquals(new Double(count), result); + } + + @Test + public void testServiceCallSuccessStrategy() throws InterruptedException { + int count = 10; + int expected = 3; + CountDownLatch latch = new CountDownLatch(2); + new Thread(() -> { + try { + batchDone(() -> { + StatInfo statInfo = new StatInfo(); + ServiceCallResult callResult = mockFixedLabelServiceCallResult(200, 1000); + callResult.setRetStatus(RetStatus.RetSuccess); + statInfo.setRouterGauge(callResult); + handler.handle(statInfo); + }, expected); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + latch.countDown(); + }).start(); + new Thread(() -> { + try { + batchDone(() -> { + StatInfo statInfo = new StatInfo(); + ServiceCallResult callResult = mockFixedLabelServiceCallResult(200, 1000); + callResult.setRetStatus(RetStatus.RetFail); + statInfo.setRouterGauge(callResult); + handler.handle(statInfo); + }, count - expected); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + latch.countDown(); + }).start(); + latch.await(); + + // mock pushing + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + ServiceCallResult example = mockFixedLabelServiceCallResult(200, 1000); + Double result = getServiceCallSuccessResult(example); + Assert.assertEquals(new Double(expected), result); + } + + @Test + public void testServiceCallSumAndMaxStrategy() throws InterruptedException { + List delayList = Collections.synchronizedList(new ArrayList<>()); + int count = 20; + batchDone(() -> { + int delay = random.nextInt(1000) + 100; + delayList.add(delay); + + StatInfo statInfo = new StatInfo(); + ServiceCallResult callResult = mockFixedLabelServiceCallResult(200, delay); + statInfo.setRouterGauge(callResult); + handler.handle(statInfo); + }, count); + + // mock pushing + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + int maxExpected = 0; + int sumExpected = 0; + for (Integer i : delayList) { + if (i > maxExpected) { + maxExpected = i; + } + sumExpected += i; + } + + ServiceCallResult example = mockFixedLabelServiceCallResult(200, 1000); + Double maxResult = getServiceCallMaxResult(example); + Double sumResult = getServiceCallSumResult(example); + Assert.assertEquals(new Double(maxExpected), maxResult); + Assert.assertEquals(new Double(sumExpected), sumResult); + } + + @Test + public void testRateLimitStrategy() throws InterruptedException { + int count = 10; + int passedNum = 3; + CountDownLatch latch = new CountDownLatch(2); + new Thread(() -> { + try { + batchDone(() -> { + StatInfo statInfo = new StatInfo(); + DefaultRateLimitResult rateLimitResult = mockFixedRateLimitResult(RateLimitGauge.Result.PASSED); + statInfo.setRateLimitGauge(rateLimitResult); + handler.handle(statInfo); + }, passedNum); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + latch.countDown(); + }).start(); + new Thread(() -> { + try { + batchDone(() -> { + StatInfo statInfo = new StatInfo(); + DefaultRateLimitResult rateLimitResult = mockFixedRateLimitResult(RateLimitGauge.Result.LIMITED); + statInfo.setRateLimitGauge(rateLimitResult); + handler.handle(statInfo); + }, count - passedNum); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + latch.countDown(); + }).start(); + latch.await(); + + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + DefaultRateLimitResult example = mockFixedRateLimitResult(RateLimitGauge.Result.LIMITED); + // assert result + Double totalResult = getRateLimitTotalResult(example); + Double passResult = getRateLimitPassedResult(example); + Double limitResult = getRateLimitLimitedResult(example); + Assert.assertEquals(new Double(count), totalResult); + Assert.assertEquals(new Double(passedNum), passResult); + Assert.assertEquals(new Double(count - passedNum), limitResult); + } + + @Test + public void testCircuitBreakerOpen() throws InterruptedException { + changeCircuitBreakerStatus(mockFixedCircuitResult(CircuitBreakerStatus.Status.OPEN)); + + // mock pushing + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + DefaultCircuitBreakResult example = mockFixedCircuitResult(CircuitBreakerStatus.Status.OPEN); + Assert.assertEquals(new Double(1), getOpenResult(example)); + Assert.assertEquals(new Double(0), getHalfOpenResult(example)); + } + + @Test + public void testCircuitBreakerOpenToHalf() throws InterruptedException { + changeCircuitBreakerStatus(mockFixedCircuitResult(CircuitBreakerStatus.Status.OPEN)); + changeCircuitBreakerStatus(mockFixedCircuitResult(CircuitBreakerStatus.Status.HALF_OPEN)); + + // mock pushing + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + DefaultCircuitBreakResult example = mockFixedCircuitResult(CircuitBreakerStatus.Status.OPEN); + Assert.assertEquals(new Double(0), getOpenResult(example)); + Assert.assertEquals(new Double(1), getHalfOpenResult(example)); + } + + @Test + public void testCircuitBreakerOpenToClose() throws InterruptedException { + changeCircuitBreakerStatus(mockFixedCircuitResult(CircuitBreakerStatus.Status.OPEN)); + changeCircuitBreakerStatus(mockFixedCircuitResult(CircuitBreakerStatus.Status.HALF_OPEN)); + changeCircuitBreakerStatus(mockFixedCircuitResult(CircuitBreakerStatus.Status.CLOSE)); + + // mock pushing + Thread.sleep(pushInterval * 1500); + handler.stopHandle(); + + DefaultCircuitBreakResult example = mockFixedCircuitResult(CircuitBreakerStatus.Status.OPEN); + Assert.assertEquals(new Double(0), getOpenResult(example)); + Assert.assertEquals(new Double(0), getHalfOpenResult(example)); + } + + private void batchDone(Runnable runnable, int count) throws InterruptedException { + CountDownLatch latch = new CountDownLatch(count); + for (int i = 0; i < count; i++) { + new Thread(() -> { + runnable.run(); + latch.countDown(); + }).start(); + } + + latch.await(); + } + + private Double getServiceCallTotalResult(ServiceCallResult example) { + return getServiceCallResult(example, + new MetricValueAggregationStrategyCollections.UpstreamRequestTotalStrategy()); + } + + private Double getServiceCallSuccessResult(ServiceCallResult example) { + return getServiceCallResult(example, + new MetricValueAggregationStrategyCollections.UpstreamRequestSuccessStrategy()); + } + + private Double getServiceCallSumResult(ServiceCallResult example) { + return getServiceCallResult(example, + new MetricValueAggregationStrategyCollections.UpstreamRequestTimeoutStrategy()); + } + + private Double getServiceCallMaxResult(ServiceCallResult example) { + return getServiceCallResult(example, + new MetricValueAggregationStrategyCollections.UpstreamRequestMaxTimeoutStrategy()); + } + + private Double getServiceCallResult(ServiceCallResult example, + MetricValueAggregationStrategy strategy) { + CollectorRegistry registry = handler.getPromRegistry(); + String[] labelKeys = SystemMetricLabelOrder.INSTANCE_GAUGE_LABEL_ORDER; + String[] labelValues = PrometheusPushHandler.getOrderedMetricLabelValues( + getServiceCallLabels(strategy, example, handler), labelKeys); + return registry.getSampleValue(strategy.getStrategyName(), labelKeys, labelValues); + } + + private Double getRateLimitTotalResult(DefaultRateLimitResult example) { + return getRateLimitResult(example, + new MetricValueAggregationStrategyCollections.RateLimitRequestTotalStrategy()); + } + + private Double getRateLimitPassedResult(DefaultRateLimitResult example) { + return getRateLimitResult(example, + new MetricValueAggregationStrategyCollections.RateLimitRequestPassStrategy()); + } + + private Double getRateLimitLimitedResult(DefaultRateLimitResult example) { + return getRateLimitResult(example, + new MetricValueAggregationStrategyCollections.RateLimitRequestLimitStrategy()); + } + + private Double getRateLimitResult(DefaultRateLimitResult example, + MetricValueAggregationStrategy strategy) { + CollectorRegistry registry = handler.getPromRegistry(); + String[] labelKeys = SystemMetricLabelOrder.RATELIMIT_GAUGE_LABEL_ORDER; + String[] labelValues = PrometheusPushHandler.getOrderedMetricLabelValues( + getRateLimitLabels(strategy, example, handler), labelKeys); + return registry.getSampleValue(strategy.getStrategyName(), labelKeys, labelValues); + } + + private void changeCircuitBreakerStatus(DefaultCircuitBreakResult toStatus) { + StatInfo statInfo = new StatInfo(); + statInfo.setCircuitBreakGauge(toStatus); + handler.handle(statInfo); + } + + private Double getOpenResult(DefaultCircuitBreakResult example) { + return getCircuitBreakerResult(example, + new MetricValueAggregationStrategyCollections.CircuitBreakerOpenStrategy()); + } + + private Double getHalfOpenResult(DefaultCircuitBreakResult example) { + return getCircuitBreakerResult(example, + new MetricValueAggregationStrategyCollections.CircuitBreakerHalfOpenStrategy()); + } + + private Double getCircuitBreakerResult(DefaultCircuitBreakResult example, + MetricValueAggregationStrategy strategy) { + CollectorRegistry registry = handler.getPromRegistry(); + String[] labelKeys = SystemMetricLabelOrder.CIRCUIT_BREAKER_LABEL_ORDER; + String[] labelValues = PrometheusPushHandler.getOrderedMetricLabelValues( + getCircuitBreakerLabels(strategy, example, handler), labelKeys); + return registry.getSampleValue(strategy.getStrategyName(), labelKeys, labelValues); + } + + private Map getServiceCallLabels(MetricValueAggregationStrategy strategy, + InstanceGauge gauge, + PrometheusPushHandler handler) { + Map labels = handler.convertInsGaugeToLabels(gauge); + labels.put(SystemMetricModel.SystemMetricName.METRIC_NAME_LABEL, strategy.getStrategyName()); + return labels; + } + + private Map getRateLimitLabels(MetricValueAggregationStrategy strategy, + RateLimitGauge gauge, + PrometheusPushHandler handler) { + Map labels = handler.convertRateLimitGaugeToLabels(gauge); + labels.put(SystemMetricModel.SystemMetricName.METRIC_NAME_LABEL, strategy.getStrategyName()); + return labels; + } + + private Map getCircuitBreakerLabels(MetricValueAggregationStrategy strategy, + CircuitBreakGauge gauge, + PrometheusPushHandler handler) { + Map labels = handler.convertCircuitBreakToLabels(gauge); + labels.put(SystemMetricModel.SystemMetricName.METRIC_NAME_LABEL, strategy.getStrategyName()); + return labels; + } + + private DefaultCircuitBreakResult mockFixedCircuitResult(CircuitBreakerStatus.Status status) { + DefaultCircuitBreakResult circuitBreakResult = new DefaultCircuitBreakResult(); + circuitBreakResult.setService("callService"); + circuitBreakResult.setNamespace("callNamespace"); + circuitBreakResult.setMethod("GET"); + circuitBreakResult.setSubset("callSubset"); + circuitBreakResult.setHost("callHost"); + circuitBreakResult.setPort(8080); + circuitBreakResult.setInstanceId("callInstanceId"); + circuitBreakResult.setCallerService(mockCallerService()); + CircuitBreakerStatus circuitBreakerStatus = new CircuitBreakerStatus( + "mockCB", status, System.currentTimeMillis(), 0); + circuitBreakResult.setCircuitBreakStatus(circuitBreakerStatus); + return circuitBreakResult; + } + + private DefaultRateLimitResult mockFixedRateLimitResult(RateLimitGauge.Result result) { + DefaultRateLimitResult rateLimitResult = new DefaultRateLimitResult(); + rateLimitResult.setMethod("GET"); + rateLimitResult.setLabels("a:b|c:d"); + rateLimitResult.setService("callService"); + rateLimitResult.setNamespace("callNamespace"); + rateLimitResult.setResult(result); + return rateLimitResult; + } + + private ServiceCallResult mockServiceCallResult() { + return mockFixedLabelServiceCallResult(random.nextInt(300) + 200, random.nextInt(10000)); + } + + private ServiceCallResult mockFixedLabelServiceCallResult(int retCode, int Delay) { + ServiceCallResult serviceCallResult = new ServiceCallResult(); + serviceCallResult.setService("callService"); + serviceCallResult.setNamespace("callNamespace"); + serviceCallResult.setHost("callHost"); + serviceCallResult.setPort(8080); + serviceCallResult.setRetStatus(RetStatus.RetSuccess); + serviceCallResult.setRetCode(retCode); + serviceCallResult.setDelay(Delay); + serviceCallResult.setMethod("GET"); + serviceCallResult.setCallerService(mockCallerService()); + return serviceCallResult; + } + + private ServiceInfo mockCallerService() { + ServiceInfo caller = new ServiceInfo(); + caller.setService("consumer"); + caller.setNamespace("Production"); + return caller; + } + + private static class MockPushGateway extends PushGateway { + public MockPushGateway(String address) { + super(address); + } + + public void pushAdd(CollectorRegistry registry, String job) { + LOG.info("mock push-gateway sending..."); + Enumeration enumeration = registry.metricFamilySamples(); + if (null != enumeration) { + while (enumeration.hasMoreElements()) { + Collector.MetricFamilySamples samples = enumeration.nextElement(); + for (Collector.MetricFamilySamples.Sample sample : samples.samples) { + LOG.info("mock pgw-{} exposed sample: {}", super.gatewayBaseURL, sample); + } + } + } + } + } + + private static class MockAddressProvider implements PushAddressProvider { + + @Override + public String getAddress() { + return PrometheusPushHandler.PUSH_DEFAULT_ADDRESS; + } + } +} diff --git a/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/resources/log4j2.xml b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/resources/log4j2.xml new file mode 100644 index 000000000..cd8ec59a1 --- /dev/null +++ b/polaris-plugins/polaris-plugins-observability/stat-prometheus/src/test/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-ratelimit/pom.xml b/polaris-plugins/polaris-plugins-ratelimit/pom.xml new file mode 100644 index 000000000..0fb3066da --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/pom.xml @@ -0,0 +1,36 @@ + + + + polaris-plugins + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins-ratelimit + pom + + ratelimiter + + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-config + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/pom.xml b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/pom.xml new file mode 100644 index 000000000..8ed236f94 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/pom.xml @@ -0,0 +1,20 @@ + + + + polaris-plugins-ratelimit + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + ratelimiter + + pom + + ratelimiter-common + ratelimiter-reject + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/pom.xml b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/pom.xml new file mode 100644 index 000000000..5a38f182e --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/pom.xml @@ -0,0 +1,16 @@ + + + + ratelimiter + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + ratelimiter-common + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/BucketShareInfo.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/BucketShareInfo.java new file mode 100644 index 000000000..34632f184 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/BucketShareInfo.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.common.bucket; + +/** + * 通过滑桶策略信息 + */ +public class BucketShareInfo { + + private final boolean shareEqual; + + private final boolean local; + + private final boolean passOnRemoteFail; + + private long minDurationMs; + + public BucketShareInfo(boolean shareEqual, boolean local, boolean passOnRemoteFail) { + this.shareEqual = shareEqual; + this.local = local; + this.passOnRemoteFail = passOnRemoteFail; + } + + public void setMinDurationMs(long minDurationMs) { + this.minDurationMs = minDurationMs; + } + + public boolean isShareEqual() { + return shareEqual; + } + + public boolean isLocal() { + return local; + } + + public boolean isPassOnRemoteFail() { + return passOnRemoteFail; + } + + public long getMinDurationMs() { + return minDurationMs; + } +} diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/UpdateIdentifier.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/UpdateIdentifier.java new file mode 100644 index 000000000..7304cb520 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/bucket/UpdateIdentifier.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.common.bucket; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * 令牌桶是否进行更新的凭证 + */ +public class UpdateIdentifier { + + private final AtomicLong stageStartMs = new AtomicLong(0); + + private final AtomicLong lastRemoteClientUpdateMs = new AtomicLong(0); + + private final AtomicLong lastRemoteUpdateMs = new AtomicLong(0); + + public AtomicLong getStageStartMs() { + return stageStartMs; + } + + public AtomicLong getLastRemoteClientUpdateMs() { + return lastRemoteClientUpdateMs; + } + + public AtomicLong getLastRemoteUpdateMs() { + return lastRemoteUpdateMs; + } +} diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/SlidingWindow.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/SlidingWindow.java new file mode 100644 index 000000000..9c0d47194 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/SlidingWindow.java @@ -0,0 +1,131 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.common.slide; + +public class SlidingWindow { + + private final long windowLengthMs; + + private final Object lock = new Object(); + + private final Window[] windowArray; + + private final int slideCount; + + public SlidingWindow(int slideCount, long intervalMs) { + this.slideCount = slideCount; + this.windowLengthMs = intervalMs / slideCount; + this.windowArray = new Window[slideCount]; + for (int i = 0; i < slideCount; i++) { + this.windowArray[i] = new Window(); + } + } + + private int calculateTimeIdx(long curTimeMs) { + long timeId = curTimeMs / windowLengthMs; + return (int) (timeId % slideCount); + } + + public static long calculateStartTimeMs(long curTimeMs, long intervalMs) { + return curTimeMs - curTimeMs % intervalMs; + } + + private long calculateWindowStartMs(long curTimeMs) { + return calculateStartTimeMs(curTimeMs, this.windowLengthMs); + } + + public Window currentWindow(long curTimeMs) { + int idx = calculateTimeIdx(curTimeMs); + long windowStartMs = calculateWindowStartMs(curTimeMs); + Window curWindow = windowArray[idx]; + long oldWindowStartMs = curWindow.getCurrentWindowStartMs(); + if (oldWindowStartMs == windowStartMs) { + return curWindow; + } + synchronized (lock) { + curWindow.reset(oldWindowStartMs, windowStartMs); + return curWindow; + } + } + + /** + * 增加通过数 + * + * @param curTimeMs 当前时间点 + * @param value 通过数 + */ + public void addAndGetCurrentPassed(long curTimeMs, long value) { + Window curWindow = currentWindow(curTimeMs); + curWindow.addAndGetPassed(value); + } + + /** + * 增加限流数 + * + * @param curTimeMs 当前时间点 + * @param value 限流数 + */ + public void addAndGetCurrentLimited(long curTimeMs, long value) { + Window curWindow = currentWindow(curTimeMs); + curWindow.addAndGetLimited(value); + } + + /** + * 获取上报数据,并重置当前统计数据 + * + * @param curTimeMs 当前时间点 + * @return 上报数据 + */ + public Result acquireCurrentValues(long curTimeMs) { + Window curWindow = currentWindow(curTimeMs); + return new Result(curWindow.swapPassed(), curWindow.swapLimited()); + } + + /** + * 直接获取通过数据,不重置 + * + * @param curTimeMs 当前时间点 + * @return 通过数据 + */ + public long touchCurrentPassed(long curTimeMs) { + Window curWindow = currentWindow(curTimeMs); + return curWindow.getPassedValue(); + } + + + public static class Result { + + private final long passed; + + private final long limited; + + public Result(long passed, long limited) { + this.passed = passed; + this.limited = limited; + } + + public long getPassed() { + return passed; + } + + public long getLimited() { + return limited; + } + } +} + diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/Window.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/Window.java new file mode 100644 index 000000000..dad2a34f7 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-common/src/main/java/com/tencent/polaris/plugins/ratelimiter/common/slide/Window.java @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.common.slide; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * 单个滑窗 + */ +public class Window { + + private final AtomicLong windowStartMs; + + private final AtomicLong passedValue; + + private final AtomicLong limitedValue; + + public Window(long windStartMs, long passed, long limited) { + this.windowStartMs = new AtomicLong(windStartMs); + this.passedValue = new AtomicLong(passed); + this.limitedValue = new AtomicLong(limited); + } + + public Window() { + this(0, 0, 0); + } + + + /** + * 重置滑窗 + * + * @param oldWindowStartMs 淘汰的时间起始点 + * @param curWindowStartMs 新的时间起始点 + * @return 旧的滑窗 + */ + public Window reset(long oldWindowStartMs, long curWindowStartMs) { + if (windowStartMs.compareAndSet(oldWindowStartMs, curWindowStartMs)) { + long passed = swapPassed(); + long limited = swapLimited(); + return new Window(oldWindowStartMs, passed, limited); + } + return null; + } + + /** + * 获取当前的滑窗起始时间 + * + * @return 滑窗起始时间 + */ + public long getCurrentWindowStartMs() { + return windowStartMs.get(); + } + + /** + * 原子增加通过数 + * + * @param value 通过数 + * @return 增加后数量 + */ + public long addAndGetPassed(long value) { + return passedValue.addAndGet(value); + } + + /** + * 原子增加被限流数 + * + * @param value 被限流数 + * @return 增加后数量 + */ + public long addAndGetLimited(long value) { + return limitedValue.addAndGet(value); + } + + /** + * 原子获取通过数 + * + * @return 通过数 + */ + public long swapPassed() { + return passedValue.getAndSet(0); + } + + /** + * 原子获取限流数 + * + * @return 限流数 + */ + public long swapLimited() { + return limitedValue.getAndSet(0); + } + + /** + * 获取通过数 + * + * @return 通过数 + */ + public long getPassedValue() { + return passedValue.get(); + } +} diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/pom.xml b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/pom.xml new file mode 100644 index 000000000..c6fcbff83 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/pom.xml @@ -0,0 +1,22 @@ + + + + ratelimiter + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + ratelimiter-reject + + + + com.tencent.nameservice + ratelimiter-common + ${project.version} + + + \ No newline at end of file diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RejectRateLimiter.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RejectRateLimiter.java new file mode 100644 index 000000000..240a12b84 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RejectRateLimiter.java @@ -0,0 +1,61 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.reject; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.PluginType; +import com.tencent.polaris.api.plugin.common.InitContext; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.ratelimiter.InitCriteria; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaBucket; +import com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter; + +public class RejectRateLimiter implements ServiceRateLimiter { + + private Extensions extensions; + + @Override + public QuotaBucket initQuota(InitCriteria criteria) { + return new RemoteAwareBucket(criteria, extensions.getFlowCache()); + } + + @Override + public String getName() { + return ServiceRateLimiter.LIMITER_REJECT; + } + + @Override + public PluginType getType() { + return PluginTypes.SERVICE_LIMITER.getBaseType(); + } + + @Override + public void init(InitContext ctx) throws PolarisException { + } + + @Override + public void postContextInit(Extensions extensions) throws PolarisException { + this.extensions = extensions; + } + + @Override + public void destroy() { + + } +} diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RemoteAwareBucket.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RemoteAwareBucket.java new file mode 100644 index 000000000..a0a04137f --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/RemoteAwareBucket.java @@ -0,0 +1,202 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.reject; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.ratelimiter.AmountInfo; +import com.tencent.polaris.api.plugin.ratelimiter.InitCriteria; +import com.tencent.polaris.api.plugin.ratelimiter.LocalQuotaInfo; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaBucket; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.api.plugin.ratelimiter.RemoteQuotaInfo; +import com.tencent.polaris.client.pb.RateLimitProto.Amount; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.client.pb.RateLimitProto.Rule.AmountMode; +import com.tencent.polaris.client.pb.RateLimitProto.Rule.FailoverType; +import com.tencent.polaris.client.pb.RateLimitProto.Rule.Type; +import com.tencent.polaris.plugins.ratelimiter.common.bucket.BucketShareInfo; +import com.tencent.polaris.plugins.ratelimiter.common.bucket.UpdateIdentifier; +import com.tencent.polaris.plugins.ratelimiter.common.slide.SlidingWindow; +import com.tencent.polaris.plugins.ratelimiter.common.slide.SlidingWindow.Result; +import com.tencent.polaris.plugins.ratelimiter.reject.TokenBucket.AllocateResult; +import com.tencent.polaris.plugins.ratelimiter.reject.TokenBucket.TokenBucketMode; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoteAwareBucket implements QuotaBucket { + + private static final Logger LOG = LoggerFactory.getLogger(RemoteAwareBucket.class); + + private final Map tokenBucketMap = new HashMap<>(); + + private final List tokenBuckets = new ArrayList<>(); + + private final FlowCache flowCache; + + /** + * 构造器 + * + * @param initCriteria 参数 + * @param flowCache 流程缓存 + */ + public RemoteAwareBucket(InitCriteria initCriteria, FlowCache flowCache) { + Rule rule = initCriteria.getRule(); + this.flowCache = flowCache; + boolean shareEqual = rule.getAmountMode() == AmountMode.SHARE_EQUALLY; + boolean local = rule.getType() == Type.LOCAL; + boolean passOnRemoteFail = rule.getFailover() == FailoverType.FAILOVER_PASS; + BucketShareInfo bucketShareInfo = new BucketShareInfo(shareEqual, local, passOnRemoteFail); + long minDurationMs = 0; + for (int i = 0; i < rule.getAmountsCount(); i++) { + Amount amount = rule.getAmountsList().get(i); + long validDurationMs = amount.getValidDuration().getSeconds() * 1000; + if (minDurationMs == 0 || minDurationMs > validDurationMs) { + minDurationMs = validDurationMs; + } + TokenBucket tokenBucket = new TokenBucket(initCriteria.getWindowKey(), validDurationMs, + amount.getMaxAmount().getValue(), + bucketShareInfo); + tokenBuckets.add(tokenBucket); + tokenBucketMap.put(validDurationMs, tokenBucket); + } + Collections.sort(tokenBuckets); + bucketShareInfo.setMinDurationMs(minDurationMs); + } + + @Override + public QuotaResult allocateQuota(long curTimeMs, int token) throws PolarisException { + int stopIdx = -1; + TokenBucketMode mode = TokenBucketMode.UNKNOWN; + UpdateIdentifier[] identifiers = new UpdateIdentifier[tokenBuckets.size()]; + AllocateResult[] results = new AllocateResult[tokenBuckets.size()]; + for (int i = 0; i < tokenBuckets.size(); i++) { + identifiers[i] = flowCache.borrowThreadCacheObject(UpdateIdentifier.class); + results[i] = flowCache.borrowThreadCacheObject(AllocateResult.class); + } + for (int i = 0; i < tokenBuckets.size(); i++) { + TokenBucket tokenBucket = tokenBuckets.get(i); + AllocateResult allocateResult = tokenBucket + .tryAllocateToken(mode, token, curTimeMs, identifiers[i], results[i]); + mode = allocateResult.getMode(); + if (allocateResult.getLeft() < 0) { + stopIdx = i; + break; + } + } + QuotaResult response; + boolean usedRemoteQuota = mode == TokenBucketMode.REMOTE; + if (stopIdx >= 0) { + //有一个扣除不成功,则进行限流 + TokenBucket tokenBucket = tokenBuckets.get(stopIdx); + if (usedRemoteQuota) { + //远程才记录滑窗, 滑窗用于上报 + tokenBucket.confirmLimited(token, curTimeMs); + } + //归还配额 + for (int i = 0; i < stopIdx; i++) { + TokenBucket bucket = tokenBuckets.get(i); + bucket.giveBackToken(identifiers[i], token, mode); + } + response = new QuotaResult(QuotaResult.Code.QuotaResultLimited, 0, ""); + } else { + //记录分配的配额 + for (TokenBucket tokenBucket : tokenBuckets) { + if (usedRemoteQuota) { + tokenBucket.confirmPassed(token, curTimeMs); + } + } + response = new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, ""); + } + //归还对象 + for (int i = 0; i < tokenBuckets.size(); i++) { + flowCache.giveBackThreadCacheObject(identifiers[i]); + flowCache.giveBackThreadCacheObject(results[i]); + } + return response; + } + + @Override + public void release() { + + } + + @Override + public void onRemoteUpdate(RemoteQuotaInfo remoteQuotaInfo) { + long localCurTimeMs = System.currentTimeMillis(); + long remoteCurTimeMs = remoteQuotaInfo.getCurTimeMs(); + long durationMs = remoteQuotaInfo.getDurationMs(); + long localCurStartMs = SlidingWindow.calculateStartTimeMs(localCurTimeMs, durationMs); + long remoteCurStartMs = SlidingWindow.calculateStartTimeMs(remoteCurTimeMs, durationMs); + TokenBucket tokenBucket = tokenBucketMap.get(durationMs); + if (null == tokenBucket) { + return; + } + if (remoteCurStartMs != localCurStartMs) { + long remoteQuotaLeft = remoteQuotaInfo.getRemoteQuotaLeft(); + if (remoteCurStartMs + durationMs == localCurStartMs) { + //仅仅相差一个周期,可以认为是周期间切换导致,这时候可以直接更新配额为全量配额 + //当前周期没有更新,则重置当前周期配额,避免出现时间周期开始时候的误限 + remoteQuotaInfo = new RemoteQuotaInfo(tokenBucket.getRuleTotal(), remoteQuotaInfo.getClientCount(), + localCurStartMs, durationMs); + LOG.warn("[RateLimit]reset remote quota, localTimeMilli {}(startMilli {}), " + + "remoteTimeMilli {}(startMilli {}), interval {}, remoteLeft is {}, reset to {}", + localCurTimeMs, localCurStartMs, remoteCurTimeMs, remoteCurStartMs, durationMs, + remoteQuotaLeft, remoteQuotaInfo.getRemoteQuotaLeft()); + } else { + tokenBucket.syncUpdateRemoteClientCount(remoteQuotaInfo); + //不在一个时间段内,丢弃 + LOG.warn("[RateLimit]Drop remote quota, localTimeMilli {}(startMilli {}), " + + "remoteTimeMilli {}(startMilli {}), interval {}, remoteLeft is {}", localCurTimeMs, + localCurStartMs, remoteCurTimeMs, remoteCurStartMs, durationMs, + remoteQuotaLeft); + } + } + tokenBucket.syncUpdateRemoteToken(remoteQuotaInfo); + } + + @Override + public Map fetchLocalUsage(long curTimeMs) { + Map localInfos = new HashMap<>(); + for (Map.Entry entry : tokenBucketMap.entrySet()) { + TokenBucket tokenBucket = entry.getValue(); + Result result = tokenBucket.getSlidingWindow().acquireCurrentValues(curTimeMs); + LocalQuotaInfo localQuotaInfo = new LocalQuotaInfo(result.getPassed(), result.getLimited()); + localInfos.put(tokenBucket.getValidDurationSecond(), localQuotaInfo); + } + return localInfos; + } + + @Override + public Map getAmountInfo() { + Map amountInfos = new HashMap<>(); + for (Map.Entry entry : tokenBucketMap.entrySet()) { + TokenBucket tokenBucket = entry.getValue(); + AmountInfo amountInfo = new AmountInfo(); + amountInfo.setMaxAmount(tokenBucket.getRuleTotal()); + amountInfos.put(tokenBucket.getValidDurationSecond(), amountInfo); + } + return amountInfos; + } + +} diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/TokenBucket.java b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/TokenBucket.java new file mode 100644 index 000000000..90569b97a --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/java/com/tencent/polaris/plugins/ratelimiter/reject/TokenBucket.java @@ -0,0 +1,423 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.plugins.ratelimiter.reject; + +import com.tencent.polaris.api.plugin.ratelimiter.RemoteQuotaInfo; +import com.tencent.polaris.api.utils.ClosableReadWriteLock; +import com.tencent.polaris.api.utils.ClosableReadWriteLock.LockWrapper; +import com.tencent.polaris.plugins.ratelimiter.common.bucket.BucketShareInfo; +import com.tencent.polaris.plugins.ratelimiter.common.bucket.UpdateIdentifier; +import com.tencent.polaris.plugins.ratelimiter.common.slide.SlidingWindow; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TokenBucket implements Comparable { + + private static final Logger LOG = LoggerFactory.getLogger(TokenBucket.class); + + @Override + public int compareTo(TokenBucket o) { + return (int) (this.validDurationMs - o.validDurationMs); + } + + //工作模式 + public enum TokenBucketMode { + UNKNOWN, + REMOTE, + REMOTE_TO_LOCAL, + LOCAL + } + + private final UpdateIdentifier tokenBucketTimeSet = new UpdateIdentifier(); + + //共享的规则数据 + private final BucketShareInfo shareInfo; + + //唯一的滑窗标识 + private final String windowKey; + + //限流区间 + private final long validDurationMs; + + //限流区间,单位s + private final int validDurationSecond; + + //规则中定义的限流配额 + private final AtomicLong ruleToken = new AtomicLong(0); + + //每周期分配的配额总量 + private final AtomicLong tokenLeft = new AtomicLong(0); + + //远程降级到本地的剩余配额数 + private final AtomicLong remoteToLocalTokenLeft = new AtomicLong(0); + + //实例数,通过远程更新 + private final AtomicInteger instanceCount = new AtomicInteger(0); + + private final ClosableReadWriteLock lock = new ClosableReadWriteLock(); + + private final SlidingWindow slidingWindow; + + public TokenBucket(String windowKey, long validDurationMs, int tokenAmount, BucketShareInfo shareInfo) { + this.windowKey = windowKey; + this.validDurationMs = validDurationMs; + this.validDurationSecond = (int) (validDurationMs / 1e3); + this.ruleToken.set(tokenAmount); + this.tokenLeft.set(tokenAmount); + this.slidingWindow = new SlidingWindow(1, validDurationMs); + this.shareInfo = shareInfo; + this.instanceCount.set(1); + } + + /** + * 获取限流总量 + * + * @return 总量 + */ + public long getRuleTotal() { + if (!shareInfo.isShareEqual() || shareInfo.isLocal()) { + return ruleToken.get(); + } + return ruleToken.get() * instanceCount.get(); + } + + public SlidingWindow getSlidingWindow() { + return slidingWindow; + } + + /** + * 归还配额 + * + * @param identifier 时间标识 + * @param token 配额 + * @param mode 限流模式 + */ + public void giveBackToken(UpdateIdentifier identifier, long token, TokenBucketMode mode) { + try (LockWrapper readLock = this.lock.readLock()) { + readLock.lock(); + //相同则归还,否则忽略 + switch (mode) { + case REMOTE: + if (tokenBucketTimeSet.getLastRemoteUpdateMs().get() == identifier.getLastRemoteUpdateMs().get()) { + tokenLeft.addAndGet(token); + } + break; + case LOCAL: + if (tokenBucketTimeSet.getStageStartMs().get() == identifier.getStageStartMs().get()) { + tokenLeft.addAndGet(token); + } + break; + case REMOTE_TO_LOCAL: + if (tokenBucketTimeSet.getStageStartMs().get() == identifier.getStageStartMs().get()) { + remoteToLocalTokenLeft.addAndGet(token); + } + break; + default: + break; + } + } + } + + private void updateRemoteClientCount(RemoteQuotaInfo remoteQuotaInfo) { + long lastRemoteClientUpdateMs = tokenBucketTimeSet.getLastRemoteClientUpdateMs().get(); + if (lastRemoteClientUpdateMs < remoteQuotaInfo.getCurTimeMs()) { + int lastClientCount; + int curClientCount; + if (remoteQuotaInfo.getClientCount() == 0) { + curClientCount = 1; + } else { + curClientCount = remoteQuotaInfo.getClientCount(); + } + lastClientCount = instanceCount.getAndSet(curClientCount); + if (lastClientCount != curClientCount) { + LOG.info("[RateLimit]clientCount change from {} to {}, windowKey {}", lastClientCount, + curClientCount, + windowKey); + } + tokenBucketTimeSet.getLastRemoteClientUpdateMs().set(remoteQuotaInfo.getCurTimeMs()); + + } + } + + /** + * 同步更新bucket连接的客户端数量 + * + * @param remoteQuotaInfo 远程结果 + */ + public void syncUpdateRemoteClientCount(RemoteQuotaInfo remoteQuotaInfo) { + try (LockWrapper writeLock = this.lock.writeLock()) { + writeLock.lock(); + updateRemoteClientCount(remoteQuotaInfo); + } + } + + /** + * 同步更新远程配额 + * + * @param remoteQuotaInfo 远程结果 + */ + public void syncUpdateRemoteToken(RemoteQuotaInfo remoteQuotaInfo) { + try (LockWrapper writeLock = this.lock.writeLock()) { + writeLock.lock(); + updateRemoteClientCount(remoteQuotaInfo); + long used = slidingWindow.touchCurrentPassed(remoteQuotaInfo.getCurTimeMs()); + //需要减去上报周期使用的配额数 + tokenLeft.set(remoteQuotaInfo.getRemoteQuotaLeft() - used); + tokenBucketTimeSet.getLastRemoteUpdateMs().set(remoteQuotaInfo.getCurTimeMs()); + } + } + + private boolean isRemoteNotExpired(long nowMs) { + return nowMs - tokenBucketTimeSet.getLastRemoteUpdateMs().get() <= shareInfo.getMinDurationMs(); + } + + private long calculateStageStart(long curTimeMs) { + return curTimeMs - curTimeMs % validDurationMs; + } + + private void initLocalStageOnLocalConfig(long nowMs) { + long nowStageMs = calculateStageStart(nowMs); + if (tokenBucketTimeSet.getStageStartMs().get() == nowStageMs) { + return; + } + try (LockWrapper writeLock = this.lock.writeLock()) { + writeLock.lock(); + if (isRemoteNotExpired(nowMs)) { + return; + } + if (tokenBucketTimeSet.getStageStartMs().get() == nowStageMs) { + return; + } + //开始降级到本地限流 + tokenLeft.set(ruleToken.get()); + tokenBucketTimeSet.getStageStartMs().set(nowStageMs); + } + } + + private AllocateResult tryAllocateLocal(int token, long nowMs, UpdateIdentifier identifier, AllocateResult result) { + initLocalStageOnLocalConfig(nowMs); + try (LockWrapper readLock = this.lock.readLock()) { + readLock.lock(); + identifier.getStageStartMs().set(tokenBucketTimeSet.getStageStartMs().get()); + identifier.getLastRemoteUpdateMs().set(tokenBucketTimeSet.getLastRemoteUpdateMs().get()); + result.setSuccess(true); + result.setMode(TokenBucketMode.LOCAL); + result.setLeft(tokenLeft.addAndGet(-token)); + return result; + } + } + + private long directAllocateRemoteToken(int token) { + return tokenLeft.addAndGet(-token); + } + + //尝试只读方式分配远程配额 + private AllocateResult allocateRemoteReadOnly(int token, long nowMs, UpdateIdentifier identifier, + AllocateResult result) { + try (LockWrapper readLock = this.lock.readLock()) { + readLock.lock(); + if (isRemoteNotExpired(nowMs)) { + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE); + result.setLeft(directAllocateRemoteToken(token)); + return result; + } + //远程配置过期,配置了直接放通的场景 + if (shareInfo.isPassOnRemoteFail()) { + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE_TO_LOCAL); + result.setLeft(0); + return result; + } + long stageStartMs = tokenBucketTimeSet.getStageStartMs().get(); + if (stageStartMs == calculateStageStart(nowMs)) { + identifier.getStageStartMs().set(stageStartMs); + identifier.getLastRemoteUpdateMs().set(tokenBucketTimeSet.getLastRemoteUpdateMs().get()); + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE_TO_LOCAL); + result.setLeft(remoteToLocalTokenLeft.addAndGet(-token)); + return result; + } + result.setSuccess(false); + result.setMode(TokenBucketMode.REMOTE_TO_LOCAL); + result.setLeft(0); + return result; + } + } + + //创建远程降级的token池 + private long createRemoteToLocalTokens(long nowMs, int token, UpdateIdentifier identifier, long stageStartMs) { + long nowStageMs = calculateStageStart(nowMs); + if (stageStartMs == nowStageMs) { + identifier.getStageStartMs().set(stageStartMs); + return remoteToLocalTokenLeft.addAndGet(-token); + } + long tokenPerInst = (long) Math.ceil(getRuleTotal() / (double) instanceCount.get()); + if (tokenPerInst == 0) { + tokenPerInst = 1; + } + remoteToLocalTokenLeft.set(tokenPerInst); + tokenBucketTimeSet.getStageStartMs().set(nowStageMs); + identifier.getStageStartMs().set(nowMs); + return remoteToLocalTokenLeft.addAndGet(-token); + } + + //以本地退化远程模式来进行分配 + private long allocateRemoteToLocal(int token, long nowMs, UpdateIdentifier identifier) { + //远程配额过期,配置了直接放通 + if (shareInfo.isPassOnRemoteFail()) { + return 0; + } + long stageStartMs = tokenBucketTimeSet.getStageStartMs().get(); + try (LockWrapper readLock = this.lock.readLock()) { + readLock.lock(); + if (stageStartMs == calculateStageStart(nowMs)) { + identifier.getStageStartMs().set(stageStartMs); + identifier.getLastRemoteUpdateMs().set(tokenBucketTimeSet.getLastRemoteUpdateMs().get()); + return remoteToLocalTokenLeft.addAndGet(-token); + } + } + try (LockWrapper writeLock = this.lock.writeLock()) { + writeLock.lock(); + return createRemoteToLocalTokens(nowMs, token, identifier, stageStartMs); + } + } + + //远端分配完整流程 + private AllocateResult tryAllocateRemote(int token, long nowMs, UpdateIdentifier identifier, + AllocateResult result) { + AllocateResult allocateResult = allocateRemoteReadOnly(token, nowMs, identifier, result); + if (allocateResult.isSuccess()) { + return allocateResult; + } + try (LockWrapper writeLock = this.lock.writeLock()) { + writeLock.lock(); + long stageStartMs = tokenBucketTimeSet.getStageStartMs().get(); + identifier.getLastRemoteUpdateMs().set(tokenBucketTimeSet.getLastRemoteUpdateMs().get()); + if (isRemoteNotExpired(nowMs)) { + identifier.getStageStartMs().set(stageStartMs); + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE); + result.setLeft(tokenLeft.addAndGet(-token)); + return result; + } + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE_TO_LOCAL); + result.setLeft(createRemoteToLocalTokens(nowMs, token, identifier, stageStartMs)); + return result; + } + } + + /** + * 尝试分配配额 + * + * @param token 所需配额 + * @param nowMs 当前时间点 + * @param identifier 标识 + * @param mode 配额模式 + * @param result 出参,分配结果 + * @return 分配结果 + */ + public AllocateResult tryAllocateToken(TokenBucketMode mode, int token, long nowMs, UpdateIdentifier identifier, + AllocateResult result) { + switch (mode) { + case LOCAL: + return tryAllocateLocal(token, nowMs, identifier, result); + case REMOTE: + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE); + result.setLeft(directAllocateRemoteToken(token)); + return result; + case REMOTE_TO_LOCAL: + result.setSuccess(true); + result.setMode(TokenBucketMode.REMOTE_TO_LOCAL); + result.setLeft(allocateRemoteToLocal(token, nowMs, identifier)); + return result; + default: + break; + } + //自适应计算 + if (shareInfo.isLocal()) { + return tryAllocateLocal(token, nowMs, identifier, result); + } + return tryAllocateRemote(token, nowMs, identifier, result); + } + + public String getWindowKey() { + return windowKey; + } + + /** + * 记录真实分配的配额 + * + * @param passed 已分配配额 + * @param nowMs 时间点 + */ + public void confirmPassed(long passed, long nowMs) { + slidingWindow.addAndGetCurrentPassed(nowMs, passed); + } + + /** + * 记录真实限流记录 + * + * @param limited 已限流记录 + * @param nowMs 时间点 + */ + public void confirmLimited(long limited, long nowMs) { + slidingWindow.addAndGetCurrentLimited(nowMs, limited); + } + + public static class AllocateResult { + + private boolean success; + + private TokenBucketMode mode; + + private long left; + + public void setSuccess(boolean success) { + this.success = success; + } + + public void setMode(TokenBucketMode mode) { + this.mode = mode; + } + + public void setLeft(long left) { + this.left = left; + } + + public boolean isSuccess() { + return success; + } + + public TokenBucketMode getMode() { + return mode; + } + + public long getLeft() { + return left; + } + } + + public int getValidDurationSecond() { + return validDurationSecond; + } +} diff --git a/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter new file mode 100644 index 000000000..ff0483ac8 --- /dev/null +++ b/polaris-plugins/polaris-plugins-ratelimit/ratelimiter/ratelimiter-reject/src/main/resources/META-INF/services/com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter @@ -0,0 +1 @@ +com.tencent.polaris.plugins.ratelimiter.reject.RejectRateLimiter \ No newline at end of file diff --git a/polaris-plugins/pom.xml b/polaris-plugins/pom.xml new file mode 100644 index 000000000..f90d8c159 --- /dev/null +++ b/polaris-plugins/pom.xml @@ -0,0 +1,23 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-plugins + pom + + polaris-plugin-api + polaris-plugins-client + polaris-plugins-discovery + polaris-plugins-circuitbreaker + polaris-plugins-ratelimit + polaris-plugins-observability + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-api/pom.xml b/polaris-ratelimit/polaris-ratelimit-api/pom.xml new file mode 100644 index 000000000..503df1a0c --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-api/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-ratelimit + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-ratelimit-api + + + + com.tencent.nameservice + polaris-plugin-api + ${project.version} + + + com.tencent.nameservice + polaris-protobuf + ${project.version} + + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/core/LimitAPI.java b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/core/LimitAPI.java new file mode 100644 index 000000000..1b5a61fd1 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/core/LimitAPI.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.api.core; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import java.io.Closeable; + +/** + * 限流API的主入口 + */ +public interface LimitAPI extends AutoCloseable, Closeable { + + /** + * 获取检查限流配额通过情况 + * + * @param request 限流请求(服务及标签信息) + * @return 配额通过情况 + * @throws PolarisException 异常信息 + */ + QuotaResponse getQuota(QuotaRequest request) throws PolarisException; + + /** + * 清理并释放资源 + */ + void destroy(); + + @Override + default void close() { + destroy(); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaRequest.java b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaRequest.java new file mode 100644 index 000000000..f8ad740e7 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaRequest.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making CL5 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.api.rpc; + +import com.tencent.polaris.api.rpc.RequestBaseEntity; +import java.util.Map; + +//配额查询请求 +public class QuotaRequest extends RequestBaseEntity { + + private String namespace; + + private String service; + + private String method; + + private Map labels; + + private int count; + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public Map getLabels() { + return labels; + } + + public void setLabels(Map labels) { + this.labels = labels; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResponse.java b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResponse.java new file mode 100644 index 000000000..b5bb5af83 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResponse.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making CL5 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.api.rpc; + +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; + +public class QuotaResponse { + + private final QuotaResult quotaResult; + + public QuotaResponse(QuotaResult quotaResult) { + this.quotaResult = quotaResult; + } + + public QuotaResultCode getCode() { + return QuotaResultCode.values()[quotaResult.getCode().ordinal()]; + } + + public long getWaitMs() { + return quotaResult.getWaitMs(); + } + + public String getInfo() { + return quotaResult.getInfo(); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResultCode.java b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResultCode.java new file mode 100644 index 000000000..93612e32a --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-api/src/main/java/com/tencent/polaris/ratelimit/api/rpc/QuotaResultCode.java @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making CL5 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.api.rpc; + +/** + * 限流返回码 + */ +public enum QuotaResultCode { + /** + * OK,代表请求可以通过 + */ + QuotaResultOk, + /** + * Limited,代表本次请求被限流 + */ + QuotaResultLimited +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/pom.xml b/polaris-ratelimit/polaris-ratelimit-client/pom.xml new file mode 100644 index 000000000..2bd136e21 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-ratelimit + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-ratelimit-client + + + + com.tencent.nameservice + polaris-ratelimit-api + ${project.version} + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/api/DefaultLimitAPI.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/api/DefaultLimitAPI.java new file mode 100644 index 000000000..01cd9fbce --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/api/DefaultLimitAPI.java @@ -0,0 +1,127 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.api; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.stat.DefaultRateLimitResult; +import com.tencent.polaris.api.plugin.stat.RateLimitGauge; +import com.tencent.polaris.api.plugin.stat.StatInfo; +import com.tencent.polaris.api.plugin.stat.StatReporter; +import com.tencent.polaris.client.api.BaseEngine; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import com.tencent.polaris.ratelimit.client.flow.QuotaFlow; +import com.tencent.polaris.ratelimit.client.pojo.CommonQuotaRequest; +import com.tencent.polaris.ratelimit.client.utils.LimitValidator; +import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Collections; + +import static com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode.QuotaResultOk; + +/** + * 默认的限流API实现 + */ +public class DefaultLimitAPI extends BaseEngine implements LimitAPI { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultLimitAPI.class); + + private final QuotaFlow quotaFlow = new QuotaFlow(); + + private Collection statPlugins; + + public DefaultLimitAPI(SDKContext sdkContext) { + super(sdkContext); + } + + @Override + protected void subInit() { + quotaFlow.init(sdkContext.getExtensions()); + sdkContext.registerDestroyHook(new Destroyable() { + @Override + protected void doDestroy() { + quotaFlow.destroy(); + } + }); + statPlugins = sdkContext.getPlugins().getPlugins(PluginTypes.STAT_REPORTER.getBaseType()); + } + + @Override + public QuotaResponse getQuota(QuotaRequest request) throws PolarisException { + checkAvailable("LimitAPI"); + LimitValidator.validateQuotaRequest(request); + CommonQuotaRequest commonQuotaRequest = new CommonQuotaRequest(request, sdkContext.getConfig()); + QuotaResponse response = quotaFlow.getQuota(commonQuotaRequest); + reportRateLimit(request, response); + return response; + } + + private void reportRateLimit(QuotaRequest req, QuotaResponse rsp) { + if (null != statPlugins) { + try { + DefaultRateLimitResult rateLimitGauge = new DefaultRateLimitResult(); + rateLimitGauge.setLabels(formatLabelsToStr(req.getLabels())); + rateLimitGauge.setMethod(req.getMethod()); + rateLimitGauge.setNamespace(req.getNamespace()); + rateLimitGauge.setService(req.getService()); + rateLimitGauge.setResult( + rsp.getCode() == QuotaResultOk ? RateLimitGauge.Result.PASSED : RateLimitGauge.Result.LIMITED); + StatInfo statInfo = new StatInfo(); + statInfo.setRateLimitGauge(rateLimitGauge); + + for (Plugin statPlugin : statPlugins) { + if (statPlugin instanceof StatReporter) { + ((StatReporter) statPlugin).reportStat(statInfo); + } + } + } catch (Exception ex) { + LOG.info("rate limit report encountered exception, e: {}", ex.getMessage()); + } + } + } + + private static String formatLabelsToStr(Map labels) { + if (null == labels) { + return null; + } + + if (labels.isEmpty()) { + return ""; + } + + List tmpList = new ArrayList<>(); + String labelEntry; + for (Map.Entry entry : labels.entrySet()) { + labelEntry = entry.getKey() + RateLimitConstants.DEFAULT_KV_SEPARATOR + labels.get(entry.getKey()); + tmpList.add(labelEntry); + } + Collections.sort(tmpList); + return String.join(RateLimitConstants.DEFAULT_ENTRY_SEPARATOR, tmpList); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/AsyncRateLimitConnector.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/AsyncRateLimitConnector.java new file mode 100644 index 000000000..9ebb0b10f --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/AsyncRateLimitConnector.java @@ -0,0 +1,123 @@ +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.flow.BaseFlow; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 连接器,单线程调用,不考虑并发 + */ +public class AsyncRateLimitConnector { + + private static final Logger LOG = LoggerFactory.getLogger(AsyncRateLimitConnector.class); + + /** + * 节点到客户端连接 + */ + private final Map hostToStream = new HashMap<>(); + + /** + * uniqueKey到客户端连接 + */ + private final Map uniqueKeyToStream = new HashMap<>(); + + /** + * 配置信息 + */ + private final Configuration configuration; + + /** + * 与服务端的时间差 + */ + private final AtomicLong timeDiff = new AtomicLong(); + + /** + * 最后一次同步的时间戳 + */ + private final AtomicLong lastSyncTimeMilli = new AtomicLong(); + + private final List coreRouters = new ArrayList<>(); + + + public AsyncRateLimitConnector(Configuration configuration) { + this.configuration = configuration; + coreRouters.add(ServiceRouterConfig.DEFAULT_ROUTER_METADATA); + } + + /** + * 获取连接流对象 + * + * @param extensions 插件容器 + * @param remoteCluster 远程限流集群名 + * @param uniqueKey 唯一主键 + * @param serviceIdentifier 服务标识 + * @return 连接流对象 + */ + public StreamCounterSet getStreamCounterSet(Extensions extensions, ServiceKey remoteCluster, String uniqueKey, + ServiceIdentifier serviceIdentifier) { + HostIdentifier hostIdentifier = getServiceInstance(extensions, remoteCluster, uniqueKey); + if (hostIdentifier == null) { + LOG.error("[getStreamCounterSet] rate limit cluster service not found."); + return null; + } + StreamCounterSet streamCounterSet = uniqueKeyToStream.get(uniqueKey); + if (null != streamCounterSet) { + if (streamCounterSet.getIdentifier().equals(hostIdentifier)) { + return streamCounterSet; + } + //切换了节点,去掉初始化记录 + Map initRecord = streamCounterSet.getInitRecord(); + if (null != initRecord) { + initRecord.remove(serviceIdentifier); + } + //切换了节点,老的不再使用 + if (streamCounterSet.decreaseReference()) { + hostToStream.remove(hostIdentifier); + } + } + streamCounterSet = hostToStream.get(hostIdentifier); + if (null == streamCounterSet) { + streamCounterSet = new StreamCounterSet(this, hostIdentifier, configuration); + } + streamCounterSet.addReference(); + return streamCounterSet; + } + + /** + * 一致性hash保证被调正常的情况下,拿到的都是同一个节点 + * + * @param remoteCluster 远程集群信息 + * @return 节点标识 + */ + private HostIdentifier getServiceInstance(Extensions extensions, ServiceKey remoteCluster, String hashValue) { + Instance instance = BaseFlow + .commonGetOneInstance(extensions, remoteCluster, coreRouters, LoadBalanceConfig.LOAD_BALANCE_RING_HASH, + "grpc", hashValue); + if (instance == null) { + LOG.error("can not found any instance by serviceKye:{}", remoteCluster); + return null; + } + String host = instance.getHost(); + int port = instance.getPort(); + return new HostIdentifier(host, port); + } + + public AtomicLong getTimeDiff() { + return timeDiff; + } + + public AtomicLong getLastSyncTimeMilli() { + return lastSyncTimeMilli; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/DurationBaseCallback.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/DurationBaseCallback.java new file mode 100644 index 000000000..b1f33af2f --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/DurationBaseCallback.java @@ -0,0 +1,31 @@ +package com.tencent.polaris.ratelimit.client.flow; + +/** + * duration 对应的Window + */ +public class DurationBaseCallback { + + /** + * duration + */ + private final int duration; + + /** + * 限流窗口 + */ + private final RateLimitWindow rateLimitWindow; + + + public DurationBaseCallback(int duration, RateLimitWindow rateLimitWindow) { + this.duration = duration; + this.rateLimitWindow = rateLimitWindow; + } + + public int getDuration() { + return duration; + } + + public RateLimitWindow getRateLimitWindow() { + return rateLimitWindow; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/HostIdentifier.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/HostIdentifier.java new file mode 100644 index 000000000..ace5de28c --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/HostIdentifier.java @@ -0,0 +1,60 @@ +package com.tencent.polaris.ratelimit.client.flow; + +import java.util.Objects; + +/** + * 节点的唯一标识 + */ +public class HostIdentifier { + + /** + * host + */ + private final String host; + + /** + * port + */ + private final int port; + + public HostIdentifier(String host, int port) { + this.host = host; + this.port = port; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HostIdentifier that = (HostIdentifier) o; + return port == that.port && + Objects.equals(host, that.host); + } + + @Override + public int hashCode() { + return Objects.hash(host, port); + } + + @Override + @SuppressWarnings("checkstyle:all") + public String toString() { + return "HostIdentifier{" + + "host='" + host + '\'' + + ", port=" + port + + '}'; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/InitializeRecord.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/InitializeRecord.java new file mode 100644 index 000000000..5b24c3eb7 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/InitializeRecord.java @@ -0,0 +1,37 @@ +package com.tencent.polaris.ratelimit.client.flow; + +import com.google.common.collect.Maps; +import java.util.Map; + +/** + * 初始化的记录 + */ +public class InitializeRecord { + + /** + * 限流窗口 + */ + private final RateLimitWindow rateLimitWindow; + + /** + * duration 对应记录duration -> counterKey + */ + private final Map durationRecord = Maps.newConcurrentMap(); + + public InitializeRecord(RateLimitWindow rateLimitWindow) { + this.rateLimitWindow = rateLimitWindow; + } + + /** + * 获取duration对应关系 + * + * @return duration对应关系 + */ + public Map getDurationRecord() { + return durationRecord; + } + + public RateLimitWindow getRateLimitWindow() { + return rateLimitWindow; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java new file mode 100644 index 000000000..b11888e51 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/QuotaFlow.java @@ -0,0 +1,289 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.cache.FlowCache; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.api.plugin.registry.AbstractResourceEventListener; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.api.utils.RuleUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.ResourcesResponse; +import com.tencent.polaris.client.pb.ModelProto.MatchString; +import com.tencent.polaris.client.pb.ModelProto.MatchString.MatchStringType; +import com.tencent.polaris.client.pb.RateLimitProto.RateLimit; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import com.tencent.polaris.ratelimit.client.pojo.CommonQuotaRequest; +import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants; + +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class QuotaFlow extends Destroyable { + + private static final Logger LOG = LoggerFactory.getLogger(QuotaFlow.class); + + private RateLimitExtension rateLimitExtension; + /** + * 客户端的唯一标识 + */ + private String clientId; + + private final Map svcToWindowSet = new ConcurrentHashMap<>(); + + public void init(Extensions extensions) throws PolarisException { + clientId = extensions.getValueContext().getClientId(); + rateLimitExtension = new RateLimitExtension(extensions); + extensions.getLocalRegistry().registerResourceListener(new RateLimitRuleListener()); + rateLimitExtension.submitExpireJob(new Runnable() { + @Override + public void run() { + for (Map.Entry entry : svcToWindowSet.entrySet()) { + entry.getValue().cleanupContainers(); + } + } + }); + } + + protected void doDestroy() { + rateLimitExtension.destroy(); + } + + public QuotaResponse getQuota(CommonQuotaRequest request) throws PolarisException { + RateLimitWindow rateLimitWindow = lookupRateLimitWindow(request); + if (null == rateLimitWindow) { + //没有限流规则,直接放通 + return new QuotaResponse( + new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, RateLimitConstants.RULE_NOT_EXISTS)); + } + rateLimitWindow.init(); + return new QuotaResponse(rateLimitWindow.allocateQuota(request.getCount())); + } + + private RateLimitWindow lookupRateLimitWindow(CommonQuotaRequest request) throws PolarisException { + //1.获取限流规则 + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(rateLimitExtension.getExtensions(), false, request, request.getFlowControlParam()); + ServiceRule serviceRule = resourcesResponse.getServiceRule(request.getSvcEventKey()); + //2.进行规则匹配 + Rule rule = lookupRule(serviceRule, request.getLabels()); + if (null == rule) { + return null; + } + request.setTargetRule(rule); + //3.获取已有的限流窗口 + ServiceKey serviceKey = request.getSvcEventKey().getServiceKey(); + String labelsStr = formatLabelsToStr(request); + RateLimitWindowSet rateLimitWindowSet = getRateLimitWindowSet(serviceKey); + RateLimitWindow rateLimitWindow = rateLimitWindowSet.getRateLimitWindow(rule, labelsStr); + if (null != rateLimitWindow) { + return rateLimitWindow; + } + //3.创建限流窗口 + return rateLimitWindowSet.addRateLimitWindow(request, labelsStr); + } + + private RateLimitWindowSet getRateLimitWindowSet(ServiceKey serviceKey) { + RateLimitWindowSet rateLimitWindowSet = svcToWindowSet.get(serviceKey); + if (null != rateLimitWindowSet) { + return rateLimitWindowSet; + } + return svcToWindowSet.computeIfAbsent(serviceKey, new Function() { + @Override + public RateLimitWindowSet apply(ServiceKey serviceKey) { + return new RateLimitWindowSet(serviceKey, rateLimitExtension, clientId); + } + }); + } + + private static String formatLabelsToStr(CommonQuotaRequest request) { + Rule rule = request.getInitCriteria().getRule(); + Map labels = request.getLabels(); + if (rule.getLabelsCount() == 0 || MapUtils.isEmpty(labels)) { + return ""; + } + List tmpList = new ArrayList<>(); + boolean regexCombine = rule.getRegexCombine().getValue(); + Map labelsMap = rule.getLabelsMap(); + for (Map.Entry entry : labelsMap.entrySet()) { + MatchString matcher = entry.getValue(); + String labelEntry; + if (matcher.getType() == MatchStringType.REGEX && regexCombine) { + labelEntry = entry.getKey() + RateLimitConstants.DEFAULT_KV_SEPARATOR + matcher.getValue().getValue(); + } else { + labelEntry = entry.getKey() + RateLimitConstants.DEFAULT_KV_SEPARATOR + labels.get(entry.getKey()); + if (matcher.getType() == MatchStringType.REGEX) { + //正则表达式扩散 + request.setRegexSpread(true); + } + } + tmpList.add(labelEntry); + } + Collections.sort(tmpList); + return String.join(RateLimitConstants.DEFAULT_ENTRY_SEPARATOR, tmpList); + } + + private Rule lookupRule(ServiceRule serviceRule, Map labels) { + if (null == serviceRule.getRule()) { + return null; + } + RateLimit rateLimitProto = (RateLimit) serviceRule.getRule(); + List rulesList = rateLimitProto.getRulesList(); + if (CollectionUtils.isEmpty(rulesList)) { + return null; + } + for (Rule rule : rulesList) { + if (null != rule.getDisable() && rule.getDisable().getValue()) { + continue; + } + if (rule.getAmountsCount() == 0) { + //没有amount的规则就忽略 + continue; + } + if (rule.getLabelsCount() == 0) { + return rule; + } + boolean allMatchLabels = true; + Map labelsMap = rule.getLabelsMap(); + for (Map.Entry entry : labelsMap.entrySet()) { + if (!matchLabels(entry.getKey(), entry.getValue(), labels)) { + allMatchLabels = false; + break; + } + } + if (allMatchLabels) { + return rule; + } + } + return null; + } + + private boolean matchLabels(String ruleLabelKey, MatchString ruleLabelMatch, Map labels) { + //设置了MatchAllValue,相当于这个规则就无效了 + if (RuleUtils.isMatchAllValue(ruleLabelMatch)) { + return true; + } + if (MapUtils.isEmpty(labels)) { + return false; + } + //集成的路由规则不包含这个key,就不匹配 + if (!labels.containsKey(ruleLabelKey)) { + return false; + } + String labelValue = labels.get(ruleLabelKey); + FlowCache flowCache = rateLimitExtension.getExtensions().getFlowCache(); + MatchStringType matchType = ruleLabelMatch.getType(); + String matchValue = ruleLabelMatch.getValue().getValue(); + if (matchType == MatchStringType.REGEX) { + //正则表达式匹配 + Pattern pattern = flowCache.loadOrStoreCompiledRegex(matchValue); + return pattern.matcher(labelValue).find(); + } + return StringUtils.equals(labelValue, matchValue); + } + + private static Map parseRules(RegistryCacheValue oldValue) { + if (null == oldValue || !oldValue.isInitialized()) { + return null; + } + ServiceRule serviceRule = (ServiceRule) oldValue; + if (null == serviceRule.getRule()) { + return null; + } + Map ruleMap = new HashMap<>(); + RateLimit rateLimit = (RateLimit) serviceRule.getRule(); + for (Rule rule : rateLimit.getRulesList()) { + ruleMap.put(rule.getRevision().getValue(), rule); + } + return ruleMap; + } + + private void deleteRules(ServiceKey serviceKey, Set deletedRules) { + LOG.info("[RateLimit]start to delete rules {} for service {}", deletedRules, serviceKey); + RateLimitWindowSet rateLimitWindowSet = svcToWindowSet.get(serviceKey); + if (null == rateLimitWindowSet) { + return; + } + rateLimitWindowSet.deleteRules(deletedRules); + } + + private class RateLimitRuleListener extends AbstractResourceEventListener { + + + @Override + public void onResourceUpdated(ServiceEventKey svcEventKey, RegistryCacheValue oldValue, + RegistryCacheValue newValue) { + EventType eventType = svcEventKey.getEventType(); + if (eventType != EventType.RATE_LIMITING) { + return; + } + Map oldRules = parseRules(oldValue); + Map newRules = parseRules(newValue); + if (MapUtils.isEmpty(oldRules)) { + return; + } + Set deletedRules = new HashSet<>(); + for (Map.Entry entry : oldRules.entrySet()) { + if (MapUtils.isEmpty(newRules) || !newRules.containsKey(entry.getKey())) { + deletedRules.add(entry.getKey()); + } + } + if (CollectionUtils.isNotEmpty(deletedRules)) { + deleteRules(svcEventKey.getServiceKey(), deletedRules); + } + } + + @Override + public void onResourceDeleted(ServiceEventKey svcEventKey, RegistryCacheValue oldValue) { + EventType eventType = svcEventKey.getEventType(); + if (eventType != EventType.RATE_LIMITING) { + return; + } + Map oldRules = parseRules(oldValue); + if (MapUtils.isEmpty(oldRules)) { + return; + } + deleteRules(svcEventKey.getServiceKey(), oldRules.keySet()); + } + } + +} + + diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitExtension.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitExtension.java new file mode 100644 index 000000000..4db8817be --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitExtension.java @@ -0,0 +1,129 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.plugin.Plugin; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter; +import com.tencent.polaris.api.utils.ThreadPoolUtils; +import com.tencent.polaris.api.control.Destroyable; +import com.tencent.polaris.client.util.NamedThreadFactory; +import com.tencent.polaris.ratelimit.client.sync.RemoteSyncTask; +import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class RateLimitExtension extends Destroyable { + + private final Extensions extensions; + + private final Map rateLimiters = new HashMap<>(); + + private ServiceRateLimiter defaultRateLimiter; + + private final ScheduledExecutorService syncExecutor; + + private final ScheduledExecutorService windowExpireExecutor; + + private final Map> scheduledTasks = new ConcurrentHashMap<>(); + + /** + * 构造器 + * + * @param extensions extensions + */ + public RateLimitExtension(Extensions extensions) { + this.extensions = extensions; + Collection plugins = extensions.getPlugins().getPlugins(PluginTypes.SERVICE_LIMITER.getBaseType()); + for (Plugin plugin : plugins) { + if (plugin.getName().equals(ServiceRateLimiter.LIMITER_REJECT)) { + defaultRateLimiter = (ServiceRateLimiter) plugin; + } + rateLimiters.put(plugin.getName(), (ServiceRateLimiter) plugin); + } + ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(0, + new NamedThreadFactory("rateLimit-sync")); + executor.setMaximumPoolSize(1); + ScheduledThreadPoolExecutor expireExecutor = new ScheduledThreadPoolExecutor(0, + new NamedThreadFactory("rateLimit-expire")); + expireExecutor.setMaximumPoolSize(1); + syncExecutor = executor; + windowExpireExecutor = expireExecutor; + } + + public ServiceRateLimiter getDefaultRateLimiter() { + return defaultRateLimiter; + } + + public Extensions getExtensions() { + return extensions; + } + + public ServiceRateLimiter getRateLimiter(String name) { + return rateLimiters.get(name); + } + + /** + * 提交同步任务 + * + * @param task 任务 + */ + public void submitSyncTask(RemoteSyncTask task) { + ScheduledFuture scheduledFuture = syncExecutor + .scheduleWithFixedDelay(task, 0, getTaskDelayInterval(), TimeUnit.MILLISECONDS); + scheduledTasks.put(task.getWindow().getUniqueKey(), scheduledFuture); + } + + private static final int EXPIRE_INTERVAL_SECOND = 5; + + /** + * 提交过期检查任务 + * + * @param task 任务 + */ + public void submitExpireJob(Runnable task) { + windowExpireExecutor + .scheduleWithFixedDelay(task, EXPIRE_INTERVAL_SECOND, EXPIRE_INTERVAL_SECOND, TimeUnit.SECONDS); + } + + + + private static long getTaskDelayInterval() { + Random random = new Random(); + return RateLimitConstants.STARTUP_DELAY_MS + random.nextInt(RateLimitConstants.RANGE_DELAY_MS); + } + + public void stopSyncTask(String uniqueKey) { + ScheduledFuture future = scheduledTasks.remove(uniqueKey); + future.cancel(true); + } + + @Override + protected void doDestroy() { + ThreadPoolUtils.waitAndStopThreadPools(new ExecutorService[]{syncExecutor, windowExpireExecutor}); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindow.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindow.java new file mode 100644 index 000000000..6a7b05ded --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindow.java @@ -0,0 +1,273 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.plugin.ratelimiter.InitCriteria; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaBucket; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.api.plugin.ratelimiter.ServiceRateLimiter; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.flow.FlowControlParam; +import com.tencent.polaris.client.pb.RateLimitProto.Amount; +import com.tencent.polaris.client.pb.RateLimitProto.RateLimitCluster; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.client.pb.RateLimitProto.Rule.Type; +import com.tencent.polaris.ratelimit.client.pojo.CommonQuotaRequest; +import com.tencent.polaris.ratelimit.client.sync.RemoteSyncTask; +import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 限流窗口 + */ +public class RateLimitWindow { + + private static final Logger LOG = LoggerFactory.getLogger(RateLimitWindow.class); + + public enum WindowStatus { + /** + * 刚创建, 无需进行后台调度 + */ + CREATED, + /** + * 已获取调度权,准备开始调度 + */ + INITIALIZING, + /** + * 已经在远程初始化结束 + */ + INITIALIZED, + /** + * 已经删除 + */ + DELETED, + } + + private final RateLimitWindowSet windowSet; + + private final ServiceKey svcKey; + + private final String labels; + + private final String uniqueKey; + + private final int hashValue; + + private final FlowControlParam syncParam; + + private final Object initLock = new Object(); + + private final AtomicInteger status = new AtomicInteger(); + + private final AtomicLong lastAccessTimeMs = new AtomicLong(); + + // 执行正式分配的令牌桶 + private final QuotaBucket allocatingBucket; + + private final long expireDurationMs; + + //远程同步的集群名 + private final ServiceKey remoteCluster; + + //限流规则 + private final Rule rule; + + //限流模式 + private int configMode; + + /** + * 构造函数 + * + * @param windowSet 集合 + * @param quotaRequest 请求 + * @param labelsStr 标签 + */ + public RateLimitWindow(RateLimitWindowSet windowSet, CommonQuotaRequest quotaRequest, String labelsStr) { + status.set(WindowStatus.CREATED.ordinal()); + InitCriteria initCriteria = quotaRequest.getInitCriteria(); + Rule rule = initCriteria.getRule(); + this.rule = rule; + this.windowSet = windowSet; + this.svcKey = quotaRequest.getSvcEventKey().getServiceKey(); + this.labels = labelsStr; + this.uniqueKey = buildQuotaUniqueKey(rule.getRevision().getValue()); + initCriteria.setWindowKey(this.uniqueKey); + this.hashValue = uniqueKey.hashCode(); + this.expireDurationMs = getExpireDurationMs(rule); + this.syncParam = quotaRequest.getFlowControlParam(); + RateLimitCluster cluster = rule.getCluster(); + if (null != cluster && StringUtils.isNotBlank(cluster.getNamespace().getValue()) && StringUtils + .isNotBlank(cluster.getService().getValue())) { + remoteCluster = new ServiceKey(cluster.getNamespace().getValue(), cluster.getService().getValue()); + } else { + remoteCluster = null; + } + allocatingBucket = getQuotaBucket(initCriteria, windowSet.getRateLimitExtension()); + lastAccessTimeMs.set(System.currentTimeMillis()); + + buildRemoteConfigMode(); + } + + private void buildRemoteConfigMode() { + //解析限流集群配置 + if (Type.LOCAL.equals(rule.getType())) { + this.configMode = RateLimitConstants.CONFIG_QUOTA_LOCAL_MODE; + return; + } + if (StringUtils.isBlank(rule.getNamespace().getValue()) || StringUtils.isBlank(rule.getService().getValue())) { + this.configMode = RateLimitConstants.CONFIG_QUOTA_LOCAL_MODE; + return; + } + this.configMode = RateLimitConstants.CONFIG_QUOTA_GLOBAL_MODE; + } + + public String getUniqueKey() { + return uniqueKey; + } + + private static QuotaBucket getQuotaBucket(InitCriteria criteria, RateLimitExtension extension) { + String action = criteria.getRule().getAction().getValue(); + ServiceRateLimiter rateLimiter = null; + if (StringUtils.isNotBlank(action)) { + rateLimiter = extension.getRateLimiter(action); + } + if (null == rateLimiter) { + rateLimiter = extension.getDefaultRateLimiter(); + } + return rateLimiter.initQuota(criteria); + } + + private static long getExpireDurationMs(Rule rule) { + return getMaxSeconds(rule) + RateLimitConstants.EXPIRE_FACTOR_MS; + } + + private static long getMaxSeconds(Rule rule) { + long maxSeconds = 0; + for (Amount amount : rule.getAmountsList()) { + long seconds = amount.getValidDuration().getSeconds(); + if (maxSeconds == 0 || seconds > maxSeconds) { + maxSeconds = seconds; + } + } + return maxSeconds; + } + + private String buildQuotaUniqueKey(String ruleRevision) { + StringBuilder builder = new StringBuilder(); + builder.append(ruleRevision).append(RateLimitConstants.DEFAULT_NAMES_SEPARATOR); + builder.append(svcKey.getService()).append(RateLimitConstants.DEFAULT_NAMES_SEPARATOR); + builder.append(svcKey.getNamespace()); + if (StringUtils.isNotBlank(labels)) { + builder.append(RateLimitConstants.DEFAULT_NAMES_SEPARATOR); + builder.append(labels); + } + return builder.toString(); + } + + public void init() { + synchronized (initLock) { + if (!status.compareAndSet(WindowStatus.CREATED.ordinal(), WindowStatus.INITIALIZING.ordinal())) { + //确保初始化一次 + return; + } + if (configMode == RateLimitConstants.CONFIG_QUOTA_LOCAL_MODE) { + //本地限流,则直接可用 + status.set(WindowStatus.INITIALIZED.ordinal()); + return; + } + //加入轮询队列,走异步调度 + windowSet.getRateLimitExtension().submitSyncTask(new RemoteSyncTask(this)); + } + } + + public void unInit() { + synchronized (initLock) { + if (status.get() == WindowStatus.DELETED.ordinal()) { + return; + } + status.set(WindowStatus.DELETED.ordinal()); + //从轮询队列中剔除 + if (null == remoteCluster) { + return; + } + windowSet.getRateLimitExtension().stopSyncTask(uniqueKey); + } + } + + public QuotaResult allocateQuota(int count) { + long curTimeMs = System.currentTimeMillis(); + lastAccessTimeMs.set(curTimeMs); + return allocatingBucket.allocateQuota(curTimeMs, count); + } + + /** + * 窗口已经过期 + * + * @return boolean + */ + public boolean isExpired() { + long curTimeMs = System.currentTimeMillis(); + boolean expired = curTimeMs - lastAccessTimeMs.get() > expireDurationMs; + if (expired) { + LOG.info("[RateLimit]window has expired, expireDurationMs {}, uniqueKey {}", expireDurationMs, uniqueKey); + } + return expired; + } + + /** + * 获取当前窗口的状态 + * + * @return 窗口状态 + */ + public WindowStatus getStatus() { + return WindowStatus.class.getEnumConstants()[this.status.get()]; + } + + public void setStatus(int value) { + this.status.set(value); + } + + public RateLimitWindowSet getWindowSet() { + return windowSet; + } + + public QuotaBucket getAllocatingBucket() { + return allocatingBucket; + } + + public ServiceKey getRemoteCluster() { + return remoteCluster; + } + + public ServiceKey getSvcKey() { + return svcKey; + } + + public String getLabels() { + return labels; + } + + public Rule getRule() { + return rule; + } + +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindowSet.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindowSet.java new file mode 100644 index 000000000..b3b5dec06 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/RateLimitWindowSet.java @@ -0,0 +1,150 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.ratelimit.client.pojo.CommonQuotaRequest; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RateLimitWindowSet { + + private static final Logger LOG = LoggerFactory.getLogger(RateLimitWindowSet.class); + + /** + * 被限流服务的serviceKey + */ + private final ServiceKey serviceKey; + + private final RateLimitExtension rateLimitExtension; + + /** + * 不同规则版本对应不同的WindowContainer + * WindowContainer 里面放的是根据label做为key的Window集合 + */ + private final Map windowByRule = new ConcurrentHashMap<>(); + + /** + * 限流与集群通信的客户端 + */ + AsyncRateLimitConnector asyncRateLimitConnector; + + /** + * 客户端唯一标识 + */ + String clientId; + + /** + * syncFlow + * + * @param serviceKey 服务名 + * @param rateLimitExtension 扩展插件 + * @param clientId 客户端唯一标识 + */ + public RateLimitWindowSet(ServiceKey serviceKey, RateLimitExtension rateLimitExtension, String clientId) { + this.clientId = clientId; + this.serviceKey = serviceKey; + this.rateLimitExtension = rateLimitExtension; + this.asyncRateLimitConnector = new AsyncRateLimitConnector( + rateLimitExtension.getExtensions().getConfiguration()); + } + + public RateLimitWindow getRateLimitWindow(Rule rule, String labelsStr) { + WindowContainer windowContainer = windowByRule.get(rule.getRevision().getValue()); + if (null == windowContainer) { + return null; + } + return windowContainer.getLabelWindow(labelsStr); + } + + /** + * 增加限流窗口 + * + * @param request 请求 + * @param labelsStr 标签 + * @return window + */ + public RateLimitWindow addRateLimitWindow(CommonQuotaRequest request, String labelsStr) { + Rule targetRule = request.getInitCriteria().getRule(); + String revision = targetRule.getRevision().getValue(); + Function createRateLimitWindow = new Function() { + @Override + public RateLimitWindow apply(String label) { + return new RateLimitWindow(RateLimitWindowSet.this, request, label); + } + }; + WindowContainer container = windowByRule.computeIfAbsent(revision, new Function() { + @Override + public WindowContainer apply(String s) { + RateLimitWindow window = createRateLimitWindow.apply(labelsStr); + return new WindowContainer(serviceKey, labelsStr, window, request.isRegexSpread()); + } + }); + RateLimitWindow mainWindow = container.getLabelWindow(labelsStr); + if (null != mainWindow) { + return mainWindow; + } + return container.computeLabelWindow(labelsStr, createRateLimitWindow); + } + + public RateLimitExtension getRateLimitExtension() { + return rateLimitExtension; + } + + public void deleteRules(Set rules) { + for (String rule : rules) { + WindowContainer container = windowByRule.remove(rule); + if (null == container) { + continue; + } + LOG.info("[RateLimit]container {} for service {} has been stopped", rule, serviceKey); + container.stopSyncTasks(); + } + } + + public void cleanupContainers() { + int rulesExpired = 0; + for (Map.Entry entry : windowByRule.entrySet()) { + String revision = entry.getKey(); + boolean expired = entry.getValue().checkAndExpireWindows(); + if (expired) { + rulesExpired++; + WindowContainer container = windowByRule.remove(revision); + if (null != container) { + container.getMainWindow().unInit(); + } + } + } + if (rulesExpired > 0) { + LOG.info("[RateLimit]{} rules has been cleanup by expired, service {}", rulesExpired, serviceKey); + } + } + + public AsyncRateLimitConnector getAsyncRateLimitConnector() { + return asyncRateLimitConnector; + } + + public String getClientId() { + return clientId; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/ServiceIdentifier.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/ServiceIdentifier.java new file mode 100644 index 000000000..d226b984c --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/ServiceIdentifier.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.flow; + +import java.util.Objects; + +public class ServiceIdentifier { + + private final String service; + private final String namespace; + private final String labels; + + public ServiceIdentifier(String service, String namespace, String labels) { + this.service = service; + this.namespace = namespace; + this.labels = labels; + } + + @Override + @SuppressWarnings("checkstyle:all") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceIdentifier that = (ServiceIdentifier) o; + return Objects.equals(service, that.service) && + Objects.equals(namespace, that.namespace) && + Objects.equals(labels, that.labels); + } + + @Override + public int hashCode() { + return Objects.hash(service, namespace, labels); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/StreamCounterSet.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/StreamCounterSet.java new file mode 100644 index 000000000..f00b46bbc --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/StreamCounterSet.java @@ -0,0 +1,365 @@ +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.plugin.ratelimiter.RemoteQuotaInfo; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.ratelimit.client.flow.RateLimitWindow.WindowStatus; +import com.tencent.polaris.ratelimit.client.pb.RateLimitGRPCV2Grpc; +import com.tencent.polaris.ratelimit.client.pb.RateLimitGRPCV2Grpc.RateLimitGRPCV2BlockingStub; +import com.tencent.polaris.ratelimit.client.pb.RateLimitGRPCV2Grpc.RateLimitGRPCV2Stub; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.LimitTarget; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaCounter; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaLeft; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitCmd; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitInitResponse; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitReportResponse; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitRequest; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitResponse; +import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.stub.StreamObserver; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 计数器对象 + */ +public class StreamCounterSet { + + private static final Logger LOG = LoggerFactory.getLogger(StreamCounterSet.class); + + /** + * 引用的KEY + */ + private final AtomicInteger reference = new AtomicInteger(); + + /** + * 异步通信器 + */ + private final AsyncRateLimitConnector asyncConnector; + + /** + * 节点唯一标识 + */ + private final HostIdentifier identifier; + + /** + * 当前的资源 + */ + private final AtomicReference currentStreamResource = new AtomicReference<>(); + + /** + * 重连间隔时间 + */ + private final long reconnectInterval; + + /** + * client key + */ + private int clientKey; + + /** + * 初始化记录 + */ + private final Map initRecord = new HashMap<>(); + + + /** + * report使用的 + */ + private final Map counters = new HashMap<>(); + + /** + * 最近更新时间 + */ + private final AtomicLong lastRecvTime = new AtomicLong(0); + + public StreamCounterSet(AsyncRateLimitConnector asyncConnector, HostIdentifier identifier, + Configuration configuration) { + this.asyncConnector = asyncConnector; + this.identifier = identifier; + this.reconnectInterval = configuration.getGlobal().getServerConnector().getReconnectInterval(); + } + + public HostIdentifier getIdentifier() { + return identifier; + } + + private class StreamResource implements StreamObserver { + + private final AtomicBoolean endStream = new AtomicBoolean(false); + + /** + * 最后一次链接失败的时间 + */ + private final AtomicLong lastConnectFailTimeMilli = new AtomicLong(0); + + /** + * 连接 + */ + final ManagedChannel channel; + + /** + * GRPC stream客户端 + */ + final StreamObserver streamClient; + + /** + * 同步的客户端 + */ + final RateLimitGRPCV2BlockingStub client; + + public StreamResource(ManagedChannel channel, + StreamObserver streamClient, + RateLimitGRPCV2BlockingStub client) { + this.channel = channel; + this.streamClient = streamClient; + this.client = client; + } + + /** + * 关闭流 + * + * @param closeSend 是否发送EOF + */ + public void closeStream(boolean closeSend) { + if (endStream.compareAndSet(false, true)) { + if (closeSend && null != streamClient) { + LOG.info("[ServerConnector]connection {} start to closeSend", identifier); + streamClient.onCompleted(); + } + if (null != channel) { + channel.shutdown(); + } + } + } + + @Override + public void onNext(RateLimitResponse rateLimitResponse) { + lastRecvTime.set(System.currentTimeMillis()); + if (RateLimitCmd.INIT.equals(rateLimitResponse.getCmd())) { + handleRateLimitInitResponse(rateLimitResponse.getRateLimitInitResponse()); + } else if (RateLimitCmd.ACQUIRE.equals(rateLimitResponse.getCmd())) { + handleRateLimitReportResponse(rateLimitResponse.getRateLimitReportResponse()); + } + } + + @Override + public void onError(Throwable throwable) { + LOG.error("received error from server {}", identifier, throwable); + lastConnectFailTimeMilli.set(System.currentTimeMillis()); + closeStream(false); + } + + @Override + public void onCompleted() { + LOG.error("received EOF from server {}", identifier); + closeStream(true); + } + } + + /** + * 获取流式的异步客户端 + * + * @param serviceIdentifier 服务标识 + * @param rateLimitWindow 限流窗口 + * @return 异步客户端 + */ + public StreamObserver preCheckAsync(ServiceIdentifier serviceIdentifier, + RateLimitWindow rateLimitWindow) { + StreamResource streamResource = checkAndCreateResource(serviceIdentifier, rateLimitWindow); + if (null != streamResource) { + return streamResource.streamClient; + } + return null; + } + + /** + * 获取阻塞的同步客户端 + * + * @param serviceIdentifier 服务标识 + * @param rateLimitWindow 限流窗口 + * @return 同步客户端 + */ + public RateLimitGRPCV2BlockingStub preCheckSync(ServiceIdentifier serviceIdentifier, + RateLimitWindow rateLimitWindow) { + StreamResource streamResource = checkAndCreateResource(serviceIdentifier, rateLimitWindow); + if (null != streamResource) { + return streamResource.client; + } + return null; + } + + /** + * 获取同步阻塞的客户端 + * + * @return 同步阻塞的客户端 + */ + private StreamResource checkAndCreateResource(ServiceIdentifier serviceIdentifier, + RateLimitWindow rateLimitWindow) { + StreamResource streamResource = currentStreamResource.get(); + if (null != streamResource && !streamResource.endStream.get()) { + return streamResource; + } + long lastConnectFailTimeMilli = 0; + if (null != streamResource) { + lastConnectFailTimeMilli = streamResource.lastConnectFailTimeMilli.get(); + } + ManagedChannel channel = createConnection(lastConnectFailTimeMilli); + if (null == channel) { + return null; + } + RateLimitGRPCV2Stub rateLimitGRPCV2Stub = RateLimitGRPCV2Grpc.newStub(channel); + StreamObserver streamClient = rateLimitGRPCV2Stub.service(streamResource); + RateLimitGRPCV2BlockingStub rateLimitGRPCV2BlockingStub = RateLimitGRPCV2Grpc.newBlockingStub(channel); + streamResource = new StreamResource(channel, streamClient, rateLimitGRPCV2BlockingStub); + currentStreamResource.set(streamResource); + if (initRecord.get(serviceIdentifier) == null) { + initRecord.putIfAbsent(serviceIdentifier, new InitializeRecord(rateLimitWindow)); + } + return streamResource; + } + + /** + * 创建连接 + * + * @return Connection对象 + */ + private ManagedChannel createConnection(long lastConnectFailTimeMilli) { + long curTimeMilli = System.currentTimeMillis(); + long timePassed = curTimeMilli - lastConnectFailTimeMilli; + if (lastConnectFailTimeMilli > 0 && timePassed > 0 && timePassed < this.reconnectInterval) { + //未达到重连的时间间隔 + LOG.debug("reconnect interval should exceed {}", this.reconnectInterval); + return null; + } + ManagedChannelBuilder builder = ManagedChannelBuilder.forAddress(identifier.getHost(), identifier.getPort()) + .usePlaintext(); + return builder.build(); + } + + /** + * 是否已经初始哈 + * + * @param serviceIdentifier 服务标识 + * @return hasInit + */ + public boolean hasInit(ServiceIdentifier serviceIdentifier) { + if (!initRecord.containsKey(serviceIdentifier)) { + return false; + } + return initRecord.get(serviceIdentifier).getDurationRecord().size() > 0; + } + + /** + * 处理初始化请求的response + * + * @param rateLimitInitResponse 初始化请求的返回结果 + */ + void handleRateLimitInitResponse(RateLimitInitResponse rateLimitInitResponse) { + LOG.debug("[handleRateLimitInitResponse] response:{}", rateLimitInitResponse); + + if (rateLimitInitResponse.getCode() != RateLimitConstants.SUCCESS) { + LOG.error("[handleRateLimitInitResponse] failed. code is {}", rateLimitInitResponse.getCode()); + return; + } + LimitTarget target = rateLimitInitResponse.getTarget(); + ServiceIdentifier serviceIdentifier = new ServiceIdentifier(target.getService(), target.getNamespace(), + target.getLabels()); + InitializeRecord initializeRecord = initRecord.get(serviceIdentifier); + if (initializeRecord == null) { + LOG.error("[handleRateLimitInitResponse] can not find init record:{}", serviceIdentifier); + return; + } + + //client key + setClientKey(rateLimitInitResponse.getClientKey()); + + List countersList = rateLimitInitResponse.getCountersList(); + if (CollectionUtils.isEmpty(countersList)) { + LOG.error("[handleRateLimitInitResponse] countersList is empty."); + return; + } + //重新初始化后,之前的记录就不要了 + initializeRecord.getDurationRecord().clear(); + long serverTimeMilli = rateLimitInitResponse.getTimestamp() + asyncConnector.getTimeDiff().get(); + countersList.forEach(counter -> { + initializeRecord.getDurationRecord().putIfAbsent(counter.getDuration(), counter.getCounterKey()); + getCounters().putIfAbsent(counter.getCounterKey(), + new DurationBaseCallback(counter.getDuration(), initializeRecord.getRateLimitWindow())); + RemoteQuotaInfo remoteQuotaInfo = new RemoteQuotaInfo(counter.getLeft(), counter.getClientCount(), + serverTimeMilli, counter.getDuration() * 1000); + initializeRecord.getRateLimitWindow().getAllocatingBucket().onRemoteUpdate(remoteQuotaInfo); + }); + + initializeRecord.getRateLimitWindow().setStatus(WindowStatus.INITIALIZED.ordinal()); + } + + /** + * 处理acquire的回包 + * + * @param rateLimitReportResponse report的回包 + */ + void handleRateLimitReportResponse(RateLimitReportResponse rateLimitReportResponse) { + LOG.debug("[handleRateLimitReportRequest] response:{}", rateLimitReportResponse); + if (rateLimitReportResponse.getCode() != RateLimitConstants.SUCCESS) { + LOG.error("[handleRateLimitReportRequest] failed. code is {}", rateLimitReportResponse.getCode()); + return; + } + + long serverTimeMilli = rateLimitReportResponse.getTimestamp(); + List quotaLeftsList = rateLimitReportResponse.getQuotaLeftsList(); + if (CollectionUtils.isEmpty(quotaLeftsList)) { + LOG.error("[handleRateLimitReportRequest] quotaLefts is empty."); + return; + } + quotaLeftsList.forEach(quotaLeft -> { + DurationBaseCallback callback = getCounters().get(quotaLeft.getCounterKey()); + RemoteQuotaInfo remoteQuotaInfo = new RemoteQuotaInfo(quotaLeft.getLeft(), quotaLeft.getClientCount(), + serverTimeMilli, callback.getDuration() * 1000); + callback.getRateLimitWindow().getAllocatingBucket().onRemoteUpdate(remoteQuotaInfo); + }); + } + + public int getClientKey() { + return clientKey; + } + + public void setClientKey(int clientKey) { + this.clientKey = clientKey; + } + + public Map getCounters() { + return counters; + } + + public Map getInitRecord() { + return initRecord; + } + + public void addReference() { + reference.incrementAndGet(); + } + + public boolean decreaseReference() { + int value = reference.decrementAndGet(); + if (value == 0) { + StreamResource streamResource = currentStreamResource.get(); + if (null != streamResource) { + streamResource.closeStream(true); + } + return true; + } + return false; + } + + +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/WindowContainer.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/WindowContainer.java new file mode 100644 index 000000000..a05904c22 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/flow/WindowContainer.java @@ -0,0 +1,100 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.flow; + +import com.tencent.polaris.api.pojo.ServiceKey; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WindowContainer { + + private static final Logger LOG = LoggerFactory.getLogger(WindowContainer.class); + + private final ServiceKey serviceKey; + + private final RateLimitWindow mainWindow; + + private final Map windowByLabel; + + public WindowContainer(ServiceKey serviceKey, String labelStr, RateLimitWindow window, boolean regexSpread) { + this.serviceKey = serviceKey; + if (!regexSpread) { + mainWindow = window; + windowByLabel = null; + } else { + mainWindow = null; + windowByLabel = new ConcurrentHashMap<>(); + windowByLabel.put(labelStr, window); + } + } + + public RateLimitWindow getLabelWindow(String label) { + if (null != mainWindow) { + return mainWindow; + } + return windowByLabel.get(label); + } + + public RateLimitWindow computeLabelWindow(String label, Function function) { + return windowByLabel.computeIfAbsent(label, function); + } + + public void stopSyncTasks() { + if (null != mainWindow) { + mainWindow.unInit(); + return; + } + for (RateLimitWindow window : windowByLabel.values()) { + window.unInit(); + } + } + + public RateLimitWindow getMainWindow() { + return mainWindow; + } + + /** + * 检查并淘汰窗口 + * + * @return 是否淘汰 + */ + public boolean checkAndExpireWindows() { + if (null != mainWindow) { + return mainWindow.isExpired(); + } + int expiredLabels = 0; + for (Map.Entry entry : windowByLabel.entrySet()) { + String labelKey = entry.getKey(); + RateLimitWindow window = entry.getValue(); + if (window.isExpired()) { + expiredLabels++; + window = windowByLabel.remove(labelKey); + if (null != window) { + window.unInit(); + } + } + } + if (expiredLabels > 0) { + LOG.info("[RateLimit]{} labels has been cleanup by expired, service {}", expiredLabels, serviceKey); + } + return false; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/pojo/CommonQuotaRequest.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/pojo/CommonQuotaRequest.java new file mode 100644 index 000000000..a7a7c55bb --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/pojo/CommonQuotaRequest.java @@ -0,0 +1,117 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.pojo; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.plugin.ratelimiter.InitCriteria; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.FlowControlParam; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import java.util.Map; +import java.util.Set; + +public class CommonQuotaRequest implements ServiceEventKeysProvider { + + private final ServiceEventKey svcEventKey; + + private final String method; + + private final Map labels; + + private final int count; + + //服务规则 + private ServiceRule rateLimitRule; + + //实际命中的规则 + private final InitCriteria initCriteria; + + //规则中含有正则表达式扩散 + private boolean regexSpread; + + private FlowControlParam flowControlParam; + + public CommonQuotaRequest(QuotaRequest quotaRequest, Configuration configuration) { + svcEventKey = new ServiceEventKey(new ServiceKey(quotaRequest.getNamespace(), quotaRequest.getService()), + EventType.RATE_LIMITING); + labels = quotaRequest.getLabels(); + method = quotaRequest.getMethod(); + count = quotaRequest.getCount(); + initCriteria = new InitCriteria(); + this.flowControlParam = new DefaultFlowControlParam(); + BaseFlow.buildFlowControlParam(quotaRequest, configuration, flowControlParam); + } + + public void setRateLimitRule(ServiceRule rateLimitRule) { + this.rateLimitRule = rateLimitRule; + } + + public void setRegexSpread(boolean regexSpread) { + this.regexSpread = regexSpread; + } + + public boolean isRegexSpread() { + return regexSpread; + } + + public void setTargetRule(Rule targetRule) { + this.initCriteria.setRule(targetRule); + } + + @Override + public boolean isUseCache() { + return false; + } + + @Override + public Set getSvcEventKeys() { + return null; + } + + @Override + public ServiceEventKey getSvcEventKey() { + return svcEventKey; + } + + public Map getLabels() { + return labels; + } + + public ServiceRule getRateLimitRule() { + return rateLimitRule; + } + + public InitCriteria getInitCriteria() { + return initCriteria; + } + + public FlowControlParam getFlowControlParam() { + return flowControlParam; + } + + public int getCount() { + return count; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/sync/RemoteSyncTask.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/sync/RemoteSyncTask.java new file mode 100644 index 000000000..ef5d4e4e0 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/sync/RemoteSyncTask.java @@ -0,0 +1,246 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.sync; + +import com.tencent.polaris.api.plugin.ratelimiter.AmountInfo; +import com.tencent.polaris.api.plugin.ratelimiter.LocalQuotaInfo; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaBucket; +import com.tencent.polaris.api.utils.MapUtils; +import com.tencent.polaris.ratelimit.client.flow.AsyncRateLimitConnector; +import com.tencent.polaris.ratelimit.client.flow.RateLimitWindow; +import com.tencent.polaris.ratelimit.client.flow.ServiceIdentifier; +import com.tencent.polaris.ratelimit.client.flow.StreamCounterSet; +import com.tencent.polaris.ratelimit.client.pb.RateLimitGRPCV2Grpc.RateLimitGRPCV2BlockingStub; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.LimitTarget; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaMode; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaSum; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.QuotaTotal; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitCmd; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitInitRequest; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitInitRequest.Builder; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitReportRequest; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.RateLimitRequest; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.TimeAdjustRequest; +import com.tencent.polaris.ratelimit.client.pb.RatelimitV2.TimeAdjustResponse; +import com.tencent.polaris.ratelimit.client.utils.RateLimitConstants; +import io.grpc.stub.StreamObserver; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 1、首次调用需要上报 + * 2、出现后台server切换需要上报 + * 3、定时上报上一次上报到现在已经超过上报周期 + * 4、额使用完毕后上报。配额=amount/实例数/时间片数。 + * 每次上报完后,server会返回服务端的时间戳,客户端会用来校正上报的时间段 + */ +public class RemoteSyncTask implements Runnable { + + private static final Logger LOG = LoggerFactory.getLogger(RemoteSyncTask.class); + + /** + * 限流窗口 + */ + private final RateLimitWindow window; + + /** + * 与限流集群通信的连接类 + */ + private final AsyncRateLimitConnector asyncRateLimitConnector; + + /** + * 被限流的服务信息,一个窗口的唯一标识 + */ + private final ServiceIdentifier serviceIdentifier; + + public RemoteSyncTask(RateLimitWindow window) { + this.window = window; + this.asyncRateLimitConnector = window.getWindowSet().getAsyncRateLimitConnector(); + this.serviceIdentifier = new ServiceIdentifier(window.getSvcKey().getService(), + window.getSvcKey().getNamespace(), window.getLabels()); + } + + public RateLimitWindow getWindow() { + return window; + } + + + @Override + public void run() { + switch (window.getStatus()) { + case CREATED: + case DELETED://todo 已经是删除态了,还有必要在轮询吗? + break; + case INITIALIZING: + doRemoteInit(); + break; + default: + doRemoteAcquire(); + break; + } + } + + /** + * 发送初始化请求 + */ + private void doRemoteInit() { + StreamCounterSet streamCounterSet = asyncRateLimitConnector + .getStreamCounterSet(window.getWindowSet().getRateLimitExtension().getExtensions(), + window.getRemoteCluster(), window.getUniqueKey(), serviceIdentifier); + //拿不到限流集群的实例的时候 + if (streamCounterSet == null) { + LOG.error("[doRemoteInit] failed, stream counter is null. remote cluster:{},", + window.getRemoteCluster()); + return; + } + //调整时间 + if (!adjustTime(streamCounterSet)) { + LOG.error("[doRemoteInit] adjustTime failed.remote cluster:{},svcKey:{}", + window.getRemoteCluster(), window.getSvcKey()); + return; + } + StreamObserver streamClient = streamCounterSet.preCheckAsync(serviceIdentifier, window); + if (streamClient == null) { + LOG.error("[doRemoteInit] failed, stream client is null. remote cluster:{},svcKey:{}", + window.getRemoteCluster(), window.getSvcKey()); + return; + } + //clientId + Builder initRequest = RateLimitInitRequest.newBuilder(); + initRequest.setClientId(window.getWindowSet().getClientId()); + + //target + LimitTarget.Builder target = LimitTarget.newBuilder(); + target.setNamespace(window.getSvcKey().getNamespace()); + target.setService(window.getSvcKey().getService()); + target.setLabels(window.getLabels()); + initRequest.setTarget(target); + + //QuotaTotal + QuotaMode quotaMode = QuotaMode.forNumber(window.getRule().getAmountModeValue()); + QuotaBucket allocatingBucket = window.getAllocatingBucket(); + Map amountInfos = allocatingBucket.getAmountInfo(); + if (MapUtils.isNotEmpty(amountInfos)) { + for (Map.Entry entry : amountInfos.entrySet()) { + QuotaTotal.Builder total = QuotaTotal.newBuilder(); + total.setDuration(entry.getKey()); + total.setMode(quotaMode); + total.setMaxAmount((int) entry.getValue().getMaxAmount()); + initRequest.addTotals(total.build()); + } + } + RateLimitRequest rateLimitInitRequest = RateLimitRequest.newBuilder().setCmd(RateLimitCmd.INIT) + .setRateLimitInitRequest(initRequest).build(); + + streamClient.onNext(rateLimitInitRequest); + } + + + /** + * 上报 + */ + private void doRemoteAcquire() { + StreamCounterSet streamCounterSet = asyncRateLimitConnector + .getStreamCounterSet(window.getWindowSet().getRateLimitExtension().getExtensions(), + window.getRemoteCluster(), window.getUniqueKey(), serviceIdentifier); + if (streamCounterSet == null) { + LOG.error("[doRemoteAcquire] failed, stream counter is null. remote cluster:{},", + window.getRemoteCluster()); + return; + } + if (!streamCounterSet.hasInit(serviceIdentifier)) { + LOG.warn("[doRemoteAcquire] has not inited. serviceKey:{}", window.getSvcKey()); + doRemoteInit(); + return; + } + //调整时间 + if (!adjustTime(streamCounterSet)) { + LOG.error("[doRemoteAcquire] adjustTime failed.remote cluster:{},svcKey:{}", + window.getRemoteCluster(), window.getSvcKey()); + return; + } + StreamObserver streamClient = streamCounterSet.preCheckAsync(serviceIdentifier, window); + if (streamClient == null) { + LOG.error("[doRemoteAcquire] failed, stream client is null. remote cluster:{}", window.getRemoteCluster()); + return; + } + RateLimitReportRequest.Builder rateLimitReportRequest = RateLimitReportRequest.newBuilder(); + //clientKey + rateLimitReportRequest.setClientKey(streamCounterSet.getClientKey()); + //timestamp + long curTimeMilli = System.currentTimeMillis(); + long serverTimeMilli = curTimeMilli + asyncRateLimitConnector.getTimeDiff().get(); + rateLimitReportRequest.setTimestamp(serverTimeMilli); + + //quotaUses + Map localQuotaInfos = window.getAllocatingBucket().fetchLocalUsage(serverTimeMilli); + + for (Map.Entry entry : localQuotaInfos.entrySet()) { + QuotaSum.Builder quotaSum = QuotaSum.newBuilder(); + quotaSum.setUsed((int) entry.getValue().getQuotaUsed()); + quotaSum.setLimited((int) entry.getValue().getQuotaLimited()); + quotaSum.setCounterKey(streamCounterSet.getInitRecord().get(serviceIdentifier).getDurationRecord() + .get(entry.getKey())); + rateLimitReportRequest.addQuotaUses(quotaSum.build()); + } + + RateLimitRequest rateLimitRequest = RateLimitRequest.newBuilder().setCmd(RateLimitCmd.ACQUIRE) + .setRateLimitReportRequest(rateLimitReportRequest).build(); + streamClient.onNext(rateLimitRequest); + } + + /** + * 调整时间 + * + * @param streamCounterSet streamCounterSet + */ + private boolean adjustTime(StreamCounterSet streamCounterSet) { + + long lastSyncTimeMilli = asyncRateLimitConnector.getLastSyncTimeMilli().get(); + long sendTimeMilli = System.currentTimeMillis(); + + //超过间隔时间才需要调整 + if (lastSyncTimeMilli > 0 && sendTimeMilli - lastSyncTimeMilli < RateLimitConstants.STARTUP_DELAY_MS) { + LOG.info("adjustTime need wait.lastSyncTimeMilli:{},sendTimeMilli:{}", lastSyncTimeMilli, sendTimeMilli); + return true; + } + + RateLimitGRPCV2BlockingStub client = streamCounterSet.preCheckSync(serviceIdentifier, window); + if (client == null) { + LOG.error("[adjustTime] can not get connection {}", window.getRemoteCluster()); + return false; + } + + TimeAdjustRequest timeAdjustRequest = TimeAdjustRequest.newBuilder().build(); + TimeAdjustResponse timeAdjustResponse = client.timeAdjust(timeAdjustRequest); + + long receiveClientTimeMilli = System.currentTimeMillis(); + asyncRateLimitConnector.getLastSyncTimeMilli().set(receiveClientTimeMilli); + //服务端时间 + long serverTimestamp = timeAdjustResponse.getServerTimestamp(); + + long latency = receiveClientTimeMilli - sendTimeMilli; + + long timeDiff = serverTimestamp + latency / 2 - receiveClientTimeMilli; + asyncRateLimitConnector.getTimeDiff().set(timeDiff); + LOG.info("[RateLimit]adjust time to server time is {}, latency is {},diff is {}", serverTimestamp, latency, + timeDiff); + return true; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/LimitValidator.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/LimitValidator.java new file mode 100644 index 000000000..2c7e9a831 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/LimitValidator.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.utils; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.client.util.CommonValidator; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; + +public class LimitValidator { + + /** + * 校验限流请求 + * + * @param request 限流请求参数 + * @throws PolarisException 校验失败 + */ + public static void validateQuotaRequest(QuotaRequest request) throws PolarisException { + if (null == request) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "QuotaRequestImpl can not be null"); + } + CommonValidator.validateNamespaceService(request.getNamespace(), request.getService()); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/RateLimitConstants.java b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/RateLimitConstants.java new file mode 100644 index 000000000..837014317 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-client/src/main/java/com/tencent/polaris/ratelimit/client/utils/RateLimitConstants.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.client.utils; + +public interface RateLimitConstants { + + /** + * 默认Map组装str key value分割符 + */ + String DEFAULT_KV_SEPARATOR = ":"; + + /** + * 默认Map组装str (key:value) 二元组分割符 + */ + String DEFAULT_ENTRY_SEPARATOR = "|"; + + /** + * 规则不存在的报错 + */ + String RULE_NOT_EXISTS = "quota rule not exists"; + + /** + * 默认的名字分隔符 + */ + String DEFAULT_NAMES_SEPARATOR = "#"; + + /** + * 淘汰因子,过期时间=MaxDuration + ExpireFactor + */ + long EXPIRE_FACTOR_MS = 1000; + + /** + * 本地模式 + */ + int CONFIG_QUOTA_LOCAL_MODE = 0; + + /** + * 远程分布式模式 + */ + int CONFIG_QUOTA_GLOBAL_MODE = 1; + + long STARTUP_DELAY_MS = 30 * 1000; + + int RANGE_DELAY_MS = 20 * 1000; + + /** + * 服务端的返回code + */ + int SUCCESS = 200; +} diff --git a/polaris-ratelimit/polaris-ratelimit-examples/pom.xml b/polaris-ratelimit/polaris-ratelimit-examples/pom.xml new file mode 100644 index 000000000..5ce7c6072 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-examples/pom.xml @@ -0,0 +1,47 @@ + + + + polaris-ratelimit + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-ratelimit-examples + + + + commons-cli + commons-cli + 1.4 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + com.tencent.nameservice + polaris-ratelimit-factory + ${project.version} + + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/RateLimitExample.java b/polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/RateLimitExample.java new file mode 100644 index 000000000..c7e582c7a --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/RateLimitExample.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.examples; + +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import com.tencent.polaris.ratelimit.examples.utils.LimitExampleUtils; +import com.tencent.polaris.ratelimit.examples.utils.LimitExampleUtils.InitResult; +import com.tencent.polaris.ratelimit.factory.LimitAPIFactory; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class RateLimitExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = LimitExampleUtils.initRateLimitConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + int concurrency = initResult.getConcurrency(); + ScheduledExecutorService executorService = Executors.newScheduledThreadPool(concurrency); + //注意:使用本地限流时,限流阈值计数器会存放在LimitAPI实例内部,无法跨实例共享,因此LimitAPI建议通过进程单例模式使用 + try (LimitAPI limitAPI = LimitAPIFactory.createLimitAPI()) { + Runnable runnable = new Runnable() { + @Override + public void run() { + QuotaRequest quotaRequest = new QuotaRequest(); + quotaRequest.setNamespace(namespace); + quotaRequest.setService(service); + QuotaResponse quotaResponse = limitAPI.getQuota(quotaRequest); + System.out.println("quotaResponse is " + quotaResponse.getCode()); + } + }; + List> futures = new ArrayList<>(); + for (int i = 0; i < concurrency; i++) { + ScheduledFuture scheduledFuture = executorService + .scheduleWithFixedDelay(runnable, 10 + i, 500, TimeUnit.MILLISECONDS); + futures.add(scheduledFuture); + } + Thread.sleep(5000); + for (ScheduledFuture future : futures) { + future.cancel(true); + } + } + executorService.shutdown(); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/utils/LimitExampleUtils.java b/polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/utils/LimitExampleUtils.java new file mode 100644 index 000000000..c3f2c473a --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-examples/src/main/java/com/tencent/polaris/ratelimit/examples/utils/LimitExampleUtils.java @@ -0,0 +1,89 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.examples.utils; + +import com.tencent.polaris.api.utils.StringUtils; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +public class LimitExampleUtils { + + public static class InitResult { + + private final String namespace; + private final String service; + private final int concurrency; + + public InitResult(String namespace, String service, int concurrency) { + this.namespace = namespace; + this.service = service; + this.concurrency = concurrency; + } + + public String getNamespace() { + return namespace; + } + + public String getService() { + return service; + } + + public int getConcurrency() { + return concurrency; + } + } + + /** + * 初始化配置对象 + * + * @param args 命名行参数 + * @return 配置对象 + * @throws ParseException 解析异常 + */ + public static InitResult initRateLimitConfiguration(String[] args) throws ParseException { + CommandLineParser parser = new DefaultParser(); + Options options = new Options(); + options.addOption("namespace", "service namespace", true, "namespace for service"); + options.addOption("service", "service name", true, "service name"); + options.addOption("concurrency", "concurrency", true, "concurrency"); + + CommandLine commandLine = parser.parse(options, args); + String namespace = commandLine.getOptionValue("namespace"); + String service = commandLine.getOptionValue("service"); + String concurrencyStr = commandLine.getOptionValue("concurrency"); + int concurrency; + if (StringUtils.isNotBlank(concurrencyStr)) { + try { + concurrency = Integer.parseInt(concurrencyStr); + } catch (NumberFormatException e) { + e.printStackTrace(); + concurrency = 1; + } + } else { + concurrency = 1; + } + if (StringUtils.isBlank(namespace) || StringUtils.isBlank(service)) { + System.out.println("namespace or service is required"); + System.exit(1); + } + return new InitResult(namespace, service, concurrency); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/log4j2.xml b/polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/polaris.yml b/polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/polaris.yml new file mode 100644 index 000000000..ce1db469a --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-examples/src/main/resources/polaris.yml @@ -0,0 +1,6 @@ +global: + # configuration for connecting the polaris server + serverConnector: + # target server address + addresses: + - 127.0.0.1:8091 \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-factory/pom.xml b/polaris-ratelimit/polaris-ratelimit-factory/pom.xml new file mode 100644 index 000000000..8f18d53cb --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-factory/pom.xml @@ -0,0 +1,91 @@ + + + + polaris-ratelimit + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-ratelimit-factory + + + + com.tencent.nameservice + polaris-ratelimit-client + ${project.version} + + + + com.tencent.nameservice + connector-polaris-grpc + ${project.version} + + + + com.tencent.nameservice + resource-cache-memory + ${project.version} + + + + com.tencent.nameservice + flow-cache-expired + ${project.version} + + + + com.tencent.nameservice + router-isolated + ${project.version} + + + com.tencent.nameservice + router-healthy + ${project.version} + + + com.tencent.nameservice + router-nearby + ${project.version} + + + com.tencent.nameservice + router-rule + ${project.version} + + + com.tencent.nameservice + router-metadata + ${project.version} + + + + com.tencent.nameservice + loadbalancer-random + ${project.version} + + + + com.tencent.nameservice + ratelimiter-reject + ${project.version} + + + + com.tencent.nameservice + polaris-test-common + ${project.version} + test + + + com.tencent.nameservice + polaris-test-mock-discovery + ${project.version} + test + + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-factory/src/main/java/com/tencent/polaris/ratelimit/factory/LimitAPIFactory.java b/polaris-ratelimit/polaris-ratelimit-factory/src/main/java/com/tencent/polaris/ratelimit/factory/LimitAPIFactory.java new file mode 100644 index 000000000..f3a67fe5d --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-factory/src/main/java/com/tencent/polaris/ratelimit/factory/LimitAPIFactory.java @@ -0,0 +1,61 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.factory; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.ConfigAPIFactory; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.client.api.DefaultLimitAPI; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LimitAPIFactory { + + private static final Logger LOG = LoggerFactory.getLogger(LimitAPIFactory.class); + + /** + * 通过默认配置创建LimitAPI + * + * @return LimitAPI对象 + * @throws PolarisException 初始化过程异常 + */ + public static LimitAPI createLimitAPI() throws PolarisException { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + return createLimitAPIByConfig(configuration); + } + + public static LimitAPI createLimitAPIByContext(SDKContext sdkContext) throws PolarisException { + DefaultLimitAPI defaultLimitAPI = new DefaultLimitAPI(sdkContext); + defaultLimitAPI.init(); + return defaultLimitAPI; + } + + /** + * 通过配置对象创建LimitAPI + * + * @param config 配置对象 + * @return ConsumerAPI对象 + * @throws PolarisException 初始化过程的异常 + */ + public static LimitAPI createLimitAPIByConfig(Configuration config) throws PolarisException { + SDKContext context = SDKContext.initContextByConfig(config); + return createLimitAPIByContext(context); + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java b/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java new file mode 100644 index 000000000..a3032dc4b --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.test.core; + +import java.util.HashMap; +import java.util.Map; + +public interface Consts { + + String NAMESPACE_TEST = "Test"; + + String LOCAL_LIMIT_SERVICE = "java_local_limit"; + + String LABEL_METHOD = "method"; + + String METHOD_PAY = "pay"; + + String METHOD_CASH = "cash"; + + int MAX_PAY_COUNT = 1000; + + int MAX_CASH_COUNT = 2000; + + int MAX_SERVICE_COUNT = 10000; + + /** + * 通过数组创建Map + * + * @param keys key数组 + * @param values value数组 + * @return map对象 + */ + static Map createSingleValueMap(String[] keys, String[] values) { + Map result = new HashMap<>(); + for (int i = 0; i < keys.length; i++) { + result.put(keys[i], values[i]); + } + return result; + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/LocalTest.java b/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/LocalTest.java new file mode 100644 index 000000000..699ac69c6 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/LocalTest.java @@ -0,0 +1,159 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.ratelimit.test.core; + +import com.google.protobuf.Duration; +import com.google.protobuf.StringValue; +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.pb.ModelProto.MatchString; +import com.tencent.polaris.client.pb.ModelProto.MatchString.MatchStringType; +import com.tencent.polaris.client.pb.RateLimitProto.Amount; +import com.tencent.polaris.client.pb.RateLimitProto.RateLimit; +import com.tencent.polaris.client.pb.RateLimitProto.RateLimit.Builder; +import com.tencent.polaris.client.pb.RateLimitProto.Rule; +import com.tencent.polaris.client.pb.RateLimitProto.Rule.AmountMode; +import com.tencent.polaris.client.pb.RateLimitProto.Rule.Type; +import com.tencent.polaris.client.util.Utils; +import com.tencent.polaris.plugins.ratelimiter.common.slide.SlidingWindow; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; +import com.tencent.polaris.ratelimit.factory.LimitAPIFactory; +import com.tencent.polaris.test.common.TestUtils; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import java.io.IOException; +import java.util.Map; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class LocalTest { + + private NamingServer namingServer; + + @Before + public void before() { + try { + namingServer = NamingServer.startNamingServer(10081); + } catch (IOException e) { + Assert.fail(e.getMessage()); + } + ServiceKey serviceKey = new ServiceKey(Consts.NAMESPACE_TEST, Consts.LOCAL_LIMIT_SERVICE); + namingServer.getNamingService().addService(serviceKey); + Builder rateLimitBuilder = RateLimit.newBuilder(); + Rule.Builder ruleBuilder1 = Rule.newBuilder(); + ruleBuilder1.setType(Type.LOCAL); + ruleBuilder1.setPriority(UInt32Value.newBuilder().setValue(1).build()); + ruleBuilder1.setAction(StringValue.newBuilder().setValue("reject").build()); + ruleBuilder1.setAmountMode(AmountMode.GLOBAL_TOTAL); + ruleBuilder1.addAmounts( + Amount.newBuilder().setMaxAmount(UInt32Value.newBuilder().setValue(9998).build()).setValidDuration( + Duration.newBuilder().setSeconds(1).build())); + ruleBuilder1.setRevision(StringValue.newBuilder().setValue("11111").build()); + rateLimitBuilder.addRules(ruleBuilder1.build()); + + Rule.Builder ruleBuilder2 = Rule.newBuilder(); + ruleBuilder2.setType(Type.LOCAL); + ruleBuilder2.setPriority(UInt32Value.newBuilder().setValue(0).build()); + ruleBuilder2.setAction(StringValue.newBuilder().setValue("reject").build()); + ruleBuilder2.setAmountMode(AmountMode.GLOBAL_TOTAL); + ruleBuilder2.putLabels(Consts.LABEL_METHOD, MatchString.newBuilder().setType(MatchStringType.EXACT).setValue( + StringValue.newBuilder().setValue(Consts.METHOD_CASH).build()).build()); + ruleBuilder2.addAmounts( + Amount.newBuilder().setMaxAmount(UInt32Value.newBuilder().setValue(1998).build()).setValidDuration( + Duration.newBuilder().setSeconds(1).build())); + ruleBuilder2.setRevision(StringValue.newBuilder().setValue("22222").build()); + rateLimitBuilder.addRules(ruleBuilder2.build()); + + Rule.Builder ruleBuilder3 = Rule.newBuilder(); + ruleBuilder3.setType(Type.LOCAL); + ruleBuilder3.setPriority(UInt32Value.newBuilder().setValue(0).build()); + ruleBuilder3.setAction(StringValue.newBuilder().setValue("reject").build()); + ruleBuilder3.setAmountMode(AmountMode.GLOBAL_TOTAL); + ruleBuilder3.putLabels(Consts.LABEL_METHOD, MatchString.newBuilder().setType(MatchStringType.EXACT).setValue( + StringValue.newBuilder().setValue(Consts.METHOD_PAY).build()).build()); + ruleBuilder3.addAmounts( + Amount.newBuilder().setMaxAmount(UInt32Value.newBuilder().setValue(998).build()).setValidDuration( + Duration.newBuilder().setSeconds(1).build())); + ruleBuilder3.setRevision(StringValue.newBuilder().setValue("33333").build()); + rateLimitBuilder.addRules(ruleBuilder3.build()); + rateLimitBuilder.setRevision(StringValue.newBuilder().setValue("xxxxxxx").build()); + namingServer.getNamingService().setRateLimit(serviceKey, rateLimitBuilder.build()); + } + + @After + public void after() { + if (null != namingServer) { + namingServer.terminate(); + } + } + + private static void testQuotaAcquire(LimitAPI limitAPI, Map labels, int maxCount) { + QuotaRequest payRequest = new QuotaRequest(); + payRequest.setNamespace(Consts.NAMESPACE_TEST); + payRequest.setService(Consts.LOCAL_LIMIT_SERVICE); + payRequest.setCount(1); + payRequest.setLabels(labels); + boolean payLimit = false; + boolean payPass = false; + for (int i = 0; i < maxCount; i++) { + QuotaResponse response = limitAPI.getQuota(payRequest); + if (response.getCode() == QuotaResultCode.QuotaResultOk) { + payPass = true; + } else if (response.getCode() == QuotaResultCode.QuotaResultLimited) { + payLimit = true; + } + } + Assert.assertTrue(payPass); + Assert.assertTrue(payLimit); + } + + private static void adjustTime() { + long step = 20; + while (true) { + long curTimeMs = System.currentTimeMillis(); + long startTimeMs = SlidingWindow.calculateStartTimeMs(curTimeMs, 1000); + if (curTimeMs - startTimeMs + step > 1000) { + Utils.sleepUninterrupted(step); + continue; + } + break; + } + } + + @Test + public void testSingleThreadLimit() { + Configuration configuration = TestUtils.configWithEnvAddress(); + try (LimitAPI limitAPI = LimitAPIFactory.createLimitAPIByConfig(configuration)) { + adjustTime(); + testQuotaAcquire(limitAPI, + Consts.createSingleValueMap(new String[]{Consts.LABEL_METHOD}, new String[]{Consts.METHOD_PAY}), + Consts.MAX_PAY_COUNT); + testQuotaAcquire(limitAPI, + Consts.createSingleValueMap(new String[]{Consts.LABEL_METHOD}, new String[]{Consts.METHOD_CASH}), + Consts.MAX_CASH_COUNT); + testQuotaAcquire(limitAPI, null, Consts.MAX_SERVICE_COUNT); + System.out.println("start to wait expired"); + Utils.sleepUninterrupted(10 * 1000); + } + } +} diff --git a/polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/log4j2.xml b/polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/polaris.yml b/polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/polaris.yml new file mode 100644 index 000000000..554a2e297 --- /dev/null +++ b/polaris-ratelimit/polaris-ratelimit-factory/src/test/resources/polaris.yml @@ -0,0 +1,13 @@ +global: + #描述:对接polaris server的相关配置 + serverConnector: + addresses: + - 127.0.0.1:10081 +#描述:主调端配置 +consumer: + localCache: + persistEnable: false + #描述:节点熔断相关配置 + circuitBreaker: + #描述:是否启用节点熔断功能 + enable: false \ No newline at end of file diff --git a/polaris-ratelimit/pom.xml b/polaris-ratelimit/pom.xml new file mode 100644 index 000000000..5407994cc --- /dev/null +++ b/polaris-ratelimit/pom.xml @@ -0,0 +1,21 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-ratelimit + pom + + polaris-ratelimit-api + polaris-ratelimit-client + polaris-ratelimit-factory + polaris-ratelimit-examples + + \ No newline at end of file diff --git a/polaris-router/polaris-router-api/pom.xml b/polaris-router/polaris-router-api/pom.xml new file mode 100644 index 000000000..87fdd1a40 --- /dev/null +++ b/polaris-router/polaris-router-api/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router-api + + + + com.tencent.nameservice + polaris-model + ${project.version} + + + \ No newline at end of file diff --git a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/core/RouterAPI.java b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/core/RouterAPI.java new file mode 100644 index 000000000..4bc7baff3 --- /dev/null +++ b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/core/RouterAPI.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.api.core; + +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; + +/** + * 单独操作插件方法的路由相关API,供其他框架集成使用 + */ +public interface RouterAPI { + + /** + * 执行路由链 + * + * @param request 路由链列表,包含前置路由链,主链,以及后置路由链 + * @return 服务实例列表 + * @throws PolarisException 错误码及错误信息 + */ + ProcessRoutersResponse processRouters(ProcessRoutersRequest request) throws PolarisException; + + /** + * 执行负载均衡器 + * + * @param request 负载均衡策略,以及服务列表 + * @return 负载均衡后的实例信息 + * @throws PolarisException 错误码及错误信息 + */ + ProcessLoadBalanceResponse processLoadBalance(ProcessLoadBalanceRequest request) throws PolarisException; + +} diff --git a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceRequest.java b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceRequest.java new file mode 100644 index 000000000..5cf26cc22 --- /dev/null +++ b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceRequest.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.Criteria; + +public class ProcessLoadBalanceRequest { + + private ServiceInstances dstInstances; + + private Criteria criteria; + + private String lbPolicy; + + public ServiceInstances getDstInstances() { + return dstInstances; + } + + public void setDstInstances(ServiceInstances dstInstances) { + this.dstInstances = dstInstances; + } + + public Criteria getCriteria() { + return criteria; + } + + public void setCriteria(Criteria criteria) { + this.criteria = criteria; + } + + public String getLbPolicy() { + return lbPolicy; + } + + public void setLbPolicy(String lbPolicy) { + this.lbPolicy = lbPolicy; + } +} diff --git a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceResponse.java b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceResponse.java new file mode 100644 index 000000000..094cd067f --- /dev/null +++ b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessLoadBalanceResponse.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.api.rpc; + +import com.tencent.polaris.api.pojo.Instance; + +public class ProcessLoadBalanceResponse { + + private final Instance targetInstance; + + public ProcessLoadBalanceResponse(Instance targetInstance) { + this.targetInstance = targetInstance; + } + + public Instance getTargetInstance() { + return targetInstance; + } +} diff --git a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java new file mode 100644 index 000000000..567690925 --- /dev/null +++ b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersRequest.java @@ -0,0 +1,102 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.RequestBaseEntity; +import java.util.List; + +/** + * 路由处理请求 + */ +public class ProcessRoutersRequest extends RequestBaseEntity { + + private ServiceInfo sourceService; + + private RouterNamesGroup routers; + + private ServiceInstances dstInstances; + + private String method; + + public ServiceInfo getSourceService() { + return sourceService; + } + + public void setSourceService(ServiceInfo sourceService) { + this.sourceService = sourceService; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public RouterNamesGroup getRouters() { + return routers; + } + + public void setRouters(RouterNamesGroup routers) { + this.routers = routers; + } + + public ServiceInstances getDstInstances() { + return dstInstances; + } + + public void setDstInstances(ServiceInstances dstInstances) { + this.dstInstances = dstInstances; + } + + public static class RouterNamesGroup { + + private List beforeRouters; + + private List coreRouters; + + private List afterRouters; + + public List getBeforeRouters() { + return beforeRouters; + } + + public void setBeforeRouters(List beforeRouters) { + this.beforeRouters = beforeRouters; + } + + public List getCoreRouters() { + return coreRouters; + } + + public void setCoreRouters(List coreRouters) { + this.coreRouters = coreRouters; + } + + public List getAfterRouters() { + return afterRouters; + } + + public void setAfterRouters(List afterRouters) { + this.afterRouters = afterRouters; + } + } +} diff --git a/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersResponse.java b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersResponse.java new file mode 100644 index 000000000..ff828448e --- /dev/null +++ b/polaris-router/polaris-router-api/src/main/java/com/tencent/polaris/router/api/rpc/ProcessRoutersResponse.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.api.rpc; + +import com.tencent.polaris.api.pojo.ServiceInstances; + +/** + * 路由请求处理的应答 + */ +public class ProcessRoutersResponse { + + private final ServiceInstances serviceInstances; + + public ProcessRoutersResponse(ServiceInstances serviceInstances) { + + this.serviceInstances = serviceInstances; + } + + public ServiceInstances getServiceInstances() { + return serviceInstances; + } +} diff --git a/polaris-router/polaris-router-client/pom.xml b/polaris-router/polaris-router-client/pom.xml new file mode 100644 index 000000000..8997719c6 --- /dev/null +++ b/polaris-router/polaris-router-client/pom.xml @@ -0,0 +1,27 @@ + + + + polaris-router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router-client + + + + com.tencent.nameservice + polaris-router-api + ${project.version} + + + com.tencent.nameservice + polaris-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/api/DefaultRouterAPI.java b/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/api/DefaultRouterAPI.java new file mode 100644 index 000000000..75fb3457f --- /dev/null +++ b/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/api/DefaultRouterAPI.java @@ -0,0 +1,146 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.client.api; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.plugin.common.PluginTypes; +import com.tencent.polaris.api.plugin.compose.DefaultRouterChainGroup; +import com.tencent.polaris.api.plugin.compose.Extensions; +import com.tencent.polaris.api.plugin.loadbalance.LoadBalancer; +import com.tencent.polaris.api.plugin.route.RouteInfo; +import com.tencent.polaris.api.plugin.route.ServiceRouter; +import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceEventKey.EventType; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.api.BaseEngine; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.ResourcesResponse; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest.RouterNamesGroup; +import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; +import com.tencent.polaris.router.client.util.RouterValidator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 默认引擎API + */ +public class DefaultRouterAPI extends BaseEngine implements RouterAPI { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultRouterAPI.class); + + private final Configuration config; + + private final Extensions extensions; + + public DefaultRouterAPI(SDKContext context) { + super(context); + extensions = context.getExtensions(); + config = context.getConfig(); + } + + @Override + protected void subInit() { + + } + + @Override + public ProcessRoutersResponse processRouters(ProcessRoutersRequest request) throws PolarisException { + checkAvailable("EngineAPI"); + RouterValidator.validateProcessRouterRequest(request); + RouterNamesGroup requestRouterGroup = request.getRouters(); + List beforeRouters; + if (null != requestRouterGroup && CollectionUtils.isNotEmpty(requestRouterGroup.getBeforeRouters())) { + beforeRouters = Extensions.loadServiceRouters( + requestRouterGroup.getBeforeRouters(), extensions.getPlugins()); + } else { + beforeRouters = extensions.getConfigRouterChainGroup().getBeforeRouters(); + } + List afterRouters; + if (null != requestRouterGroup && CollectionUtils.isNotEmpty(requestRouterGroup.getAfterRouters())) { + afterRouters = Extensions.loadServiceRouters( + requestRouterGroup.getAfterRouters(), extensions.getPlugins()); + } else { + afterRouters = extensions.getConfigRouterChainGroup().getAfterRouters(); + } + List coreRouters; + if (null != requestRouterGroup && CollectionUtils.isNotEmpty(requestRouterGroup.getCoreRouters())) { + coreRouters = Extensions.loadServiceRouters( + requestRouterGroup.getCoreRouters(), extensions.getPlugins()); + } else { + coreRouters = extensions.getConfigRouterChainGroup().getCoreRouters(); + } + ServiceInstances dstInstances = request.getDstInstances(); + RouteInfo routeInfo = new RouteInfo(request.getSourceService(), dstInstances, request.getMethod()); + //获取路由规则 + DefaultFlowControlParam engineFlowControlParam = new DefaultFlowControlParam(); + BaseFlow.buildFlowControlParam(request, config, engineFlowControlParam); + Set routerKeys = new HashSet<>(); + ServiceEventKey dstSvcEventKey = new ServiceEventKey( + new ServiceKey(dstInstances.getNamespace(), dstInstances.getService()), + EventType.ROUTING); + routerKeys.add(dstSvcEventKey); + ServiceEventKey srcSvcEventKey = null; + if (null != routeInfo.getSourceService() && StringUtils.isNotBlank(routeInfo.getSourceService().getNamespace()) + && StringUtils.isNotBlank(routeInfo.getSourceService().getService())) { + srcSvcEventKey = new ServiceEventKey(new ServiceKey(routeInfo.getSourceService().getNamespace(), + routeInfo.getSourceService().getService()), + EventType.ROUTING); + routerKeys.add(srcSvcEventKey); + } + DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider(); + svcKeysProvider.setSvcEventKeys(routerKeys); + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(extensions, false, svcKeysProvider, engineFlowControlParam); + routeInfo.setDestRouteRule(resourcesResponse.getServiceRule(dstSvcEventKey)); + if (null != srcSvcEventKey) { + routeInfo.setSourceRouteRule(resourcesResponse.getServiceRule(srcSvcEventKey)); + } + //执行路由 + DefaultRouterChainGroup routerChainGroup = new DefaultRouterChainGroup(beforeRouters, coreRouters, + afterRouters); + ServiceInstances svcInstances = BaseFlow + .processServiceRouters(routeInfo, dstInstances, routerChainGroup); + return new ProcessRoutersResponse(svcInstances); + } + + @Override + public ProcessLoadBalanceResponse processLoadBalance(ProcessLoadBalanceRequest request) throws PolarisException { + checkAvailable("EngineAPI"); + RouterValidator.validateProcessLoadBalanceRequest(request); + LoadBalancer loadBalancer = (LoadBalancer) extensions.getPlugins() + .getPlugin(PluginTypes.LOAD_BALANCER.getBaseType(), request.getLbPolicy()); + Instance instance = BaseFlow.processLoadBalance(loadBalancer, request.getCriteria(), request.getDstInstances()); + return new ProcessLoadBalanceResponse(instance); + } + +} diff --git a/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/util/RouterValidator.java b/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/util/RouterValidator.java new file mode 100644 index 000000000..241181875 --- /dev/null +++ b/polaris-router/polaris-router-client/src/main/java/com/tencent/polaris/router/client/util/RouterValidator.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.client.util; + +import com.tencent.polaris.api.exception.ErrorCode; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; + +public class RouterValidator { + + /** + * 校验路由链处理请求 + * + * @param request 路由处理请求 + * @throws PolarisException 参数校验异常 + */ + public static void validateProcessRouterRequest(ProcessRoutersRequest request) throws PolarisException { + if (null == request.getDstInstances()) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "dstInstances is null"); + } + } + + /** + * 校验负载均处理请求 + * + * @param request 负载均衡请求 + * @throws PolarisException 参数校验异常 + */ + public static void validateProcessLoadBalanceRequest(ProcessLoadBalanceRequest request) throws PolarisException { + if (StringUtils.isBlank(request.getLbPolicy())) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "lbPolicy is null"); + } + if (null == request.getDstInstances()) { + throw new PolarisException(ErrorCode.API_INVALID_ARGUMENT, "dstInstances is null"); + } + } +} diff --git a/polaris-router/polaris-router-examples/pom.xml b/polaris-router/polaris-router-examples/pom.xml new file mode 100644 index 000000000..8d37905f0 --- /dev/null +++ b/polaris-router/polaris-router-examples/pom.xml @@ -0,0 +1,52 @@ + + + + polaris-router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router-examples + + + + com.tencent.nameservice + polaris-discovery-factory + ${project.version} + + + com.tencent.nameservice + polaris-router-factory + ${project.version} + + + commons-cli + commons-cli + 1.4 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + \ No newline at end of file diff --git a/polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/ExampleUtils.java b/polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/ExampleUtils.java new file mode 100644 index 000000000..d3ce579f9 --- /dev/null +++ b/polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/ExampleUtils.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples; + +import com.tencent.polaris.api.utils.StringUtils; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; + +public class ExampleUtils { + + public static class InitResult { + + private final String namespace; + private final String service; + + public InitResult(String namespace, String service) { + this.namespace = namespace; + this.service = service; + } + + public String getNamespace() { + return namespace; + } + + public String getService() { + return service; + } + } + + /** + * 初始化配置对象 + * + * @param args 命名行参数 + * @return 配置对象 + * @throws ParseException 解析异常 + */ + public static InitResult initConsumerConfiguration(String[] args) throws ParseException { + CommandLineParser parser = new DefaultParser(); + Options options = new Options(); + options.addOption("namespace", "service namespace", true, "namespace for service"); + options.addOption("service", "service name", true, "service name"); + + CommandLine commandLine = parser.parse(options, args); + String namespace = commandLine.getOptionValue("namespace"); + String service = commandLine.getOptionValue("service"); + if (StringUtils.isBlank(namespace) || StringUtils.isBlank(service)) { + System.out.println("namespace or service is required"); + System.exit(1); + } + return new InitResult(namespace, service); + } + +} diff --git a/polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/RouterExample.java b/polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/RouterExample.java new file mode 100644 index 000000000..726be3e64 --- /dev/null +++ b/polaris-router/polaris-router-examples/src/main/java/com/tencent/polaris/router/examples/RouterExample.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.router.examples; + +import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.GetAllInstancesRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.router.examples.ExampleUtils.InitResult; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.polaris.factory.api.RouterAPIFactory; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest.RouterNamesGroup; +import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 获取资源的样例 + */ +public class RouterExample { + + public static void main(String[] args) throws Exception { + InitResult initResult = ExampleUtils.initConsumerConfiguration(args); + String namespace = initResult.getNamespace(); + String service = initResult.getService(); + try (SDKContext sdkContext = SDKContext.initContext()) { + ConsumerAPI consumerAPI = DiscoveryAPIFactory.createConsumerAPIByContext(sdkContext); + RouterAPI routerAPI = RouterAPIFactory.createRouterAPIByContext(sdkContext); + //1. 拉取全量服务实例 + GetAllInstancesRequest getAllInstancesRequest = new GetAllInstancesRequest(); + getAllInstancesRequest.setNamespace(namespace); + getAllInstancesRequest.setService(service); + InstancesResponse allInstanceResp = consumerAPI.getAllInstance(getAllInstancesRequest); + ServiceInstances dstInstances = allInstanceResp.toServiceInstances(); + //2. 执行服务路由 + ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest(); + //被调服务 + System.out.printf("instances count before routing is %s%n", dstInstances.getInstances().size()); + //主调方信息 + ServiceInfo srcSourceInfo = new ServiceInfo(); + Map labels = new HashMap<>(); + labels.put("env", "test"); + srcSourceInfo.setMetadata(labels); + RouterNamesGroup routerNamesGroup = new RouterNamesGroup(); + List coreRouters = new ArrayList<>(); + coreRouters.add(ServiceRouterConfig.DEFAULT_ROUTER_RULE); + coreRouters.add(ServiceRouterConfig.DEFAULT_ROUTER_METADATA); + coreRouters.add(ServiceRouterConfig.DEFAULT_ROUTER_NEARBY); + //设置走规则路由 + routerNamesGroup.setCoreRouters(coreRouters); + processRoutersRequest.setDstInstances(dstInstances); + processRoutersRequest.setSourceService(srcSourceInfo); + processRoutersRequest.setRouters(routerNamesGroup); + ProcessRoutersResponse processRoutersResponse = routerAPI.processRouters(processRoutersRequest); + System.out.printf("instances count after routing is %s%n", + processRoutersResponse.getServiceInstances().getInstances().size()); + + //3. 执行负载均衡 + ProcessLoadBalanceRequest processLoadBalanceRequest = new ProcessLoadBalanceRequest(); + processLoadBalanceRequest.setDstInstances(processRoutersResponse.getServiceInstances()); + processLoadBalanceRequest.setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM); + ProcessLoadBalanceResponse processLoadBalanceResponse = routerAPI + .processLoadBalance(processLoadBalanceRequest); + System.out.printf("instances after lb is %s%n", processLoadBalanceResponse.getTargetInstance()); + } + } +} diff --git a/polaris-router/polaris-router-examples/src/main/resources/log4j2.xml b/polaris-router/polaris-router-examples/src/main/resources/log4j2.xml new file mode 100644 index 000000000..3316c94fd --- /dev/null +++ b/polaris-router/polaris-router-examples/src/main/resources/log4j2.xml @@ -0,0 +1,28 @@ + + + + + + + + + %d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polaris-router/polaris-router-examples/src/main/resources/polaris.yml b/polaris-router/polaris-router-examples/src/main/resources/polaris.yml new file mode 100644 index 000000000..b10d277bf --- /dev/null +++ b/polaris-router/polaris-router-examples/src/main/resources/polaris.yml @@ -0,0 +1,12 @@ +global: + # configuration for connecting the polaris server + serverConnector: + # target server address + addresses: + - 9.134.15.118:8091 +#描述:主调端配置 +consumer: + #描述:节点熔断相关配置 + circuitBreaker: + #描述:是否启用节点熔断功能 + enable: false \ No newline at end of file diff --git a/polaris-router/polaris-router-factory/pom.xml b/polaris-router/polaris-router-factory/pom.xml new file mode 100644 index 000000000..5cb6bb26c --- /dev/null +++ b/polaris-router/polaris-router-factory/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-router + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router-factory + + + + com.tencent.nameservice + polaris-router-client + ${project.version} + + + \ No newline at end of file diff --git a/polaris-router/polaris-router-factory/src/main/java/com/tencent/polaris/factory/api/RouterAPIFactory.java b/polaris-router/polaris-router-factory/src/main/java/com/tencent/polaris/factory/api/RouterAPIFactory.java new file mode 100644 index 000000000..8cf6dec19 --- /dev/null +++ b/polaris-router/polaris-router-factory/src/main/java/com/tencent/polaris/factory/api/RouterAPIFactory.java @@ -0,0 +1,78 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.factory.api; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.ConfigAPIFactory; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.client.api.DefaultRouterAPI; +import java.io.InputStream; + +public class RouterAPIFactory { + + + /** + * 使用默认配置创建EngineAPI + * + * @return RouterAPI对象 + * @throws PolarisException 创建过程的初始化异常 + */ + public static RouterAPI createRouterAPI() throws PolarisException { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + return createRouterAPIByConfig(configuration); + } + + /** + * 创建引擎的API对象 + * + * @param sdkContext 上下文信息 + * @return RouterAPI对象 + * @throws PolarisException 创建过程的初始化异常 + */ + public static RouterAPI createRouterAPIByContext(SDKContext sdkContext) throws PolarisException { + DefaultRouterAPI defaultEngineAPI = new DefaultRouterAPI(sdkContext); + defaultEngineAPI.init(); + return defaultEngineAPI; + } + + /** + * 通过配置对象创建EngineAPI + * + * @param config 配置对象 + * @return API对象 + * @throws PolarisException 初始化异常 + */ + public static RouterAPI createRouterAPIByConfig(Configuration config) throws PolarisException { + SDKContext context = SDKContext.initContextByConfig(config); + return createRouterAPIByContext(context); + } + + /** + * 通过配置文件创建EngineAPI对象 + * + * @param stream 文件流 + * @return RouterAPI对象 + * @throws PolarisException 初始化异常 + */ + public static RouterAPI createRouterAPIByFile(InputStream stream) throws PolarisException { + Configuration configuration = ConfigAPIFactory.loadConfig(stream); + return createRouterAPIByConfig(configuration); + } +} diff --git a/polaris-router/pom.xml b/polaris-router/pom.xml new file mode 100644 index 000000000..fd178413b --- /dev/null +++ b/polaris-router/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router + pom + + polaris-router-api + polaris-router-client + polaris-router-factory + polaris-router-examples + + + \ No newline at end of file diff --git a/polaris-test/polaris-test-common/pom.xml b/polaris-test/polaris-test-common/pom.xml new file mode 100644 index 000000000..71725ca79 --- /dev/null +++ b/polaris-test/polaris-test-common/pom.xml @@ -0,0 +1,16 @@ + + + + polaris-test + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-test-common + + + \ No newline at end of file diff --git a/polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/Consts.java b/polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/Consts.java new file mode 100644 index 000000000..443b924c6 --- /dev/null +++ b/polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/Consts.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.test.common; + +public interface Consts { + + int ITERATE_COUNT = 10000; + + String NAMESPACE_TEST = "Test"; + + String NAMESPACE_PRODUCTION = "Production"; + + String SERVICE_PROVIDER = "java_provider_test"; + + String HOST = "127.0.0.1"; + + int PORT = 9091; + + String PROVIDER_TOKEN = "19485a7674294e3c88dba293373c1534"; + + String[] SERVICES = new String[]{"svc1", "svc2", "svc3", "svc4", "svc5"}; + + int MAX_COUNT = 5; + + String SERVICE_CIRCUIT_BREAKER = "circuit_breaker_svc"; +} diff --git a/polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/TestUtils.java b/polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/TestUtils.java new file mode 100644 index 000000000..9edb02435 --- /dev/null +++ b/polaris-test/polaris-test-common/src/main/java/com/tencent/polaris/test/common/TestUtils.java @@ -0,0 +1,62 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.test.common; + +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.factory.ConfigAPIFactory; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import java.util.Arrays; +import java.util.Collections; + +public class TestUtils { + + private static final String SERVER_ADDRESS_ENV = "POLARIS_SEVER_ADDRESS"; + + private static String[] getServerAddressFromEnv() { + String addressStr = System.getenv(SERVER_ADDRESS_ENV); + if (StringUtils.isBlank(addressStr)) { + return null; + } + return addressStr.split(","); + } + + /** + * 从环境变量中获取服务端地址并创建配置 + * + * @return 配置的服务端地址 + */ + public static Configuration configWithEnvAddress() { + Configuration configuration = ConfigAPIFactory.defaultConfig(); + String[] addresses = getServerAddressFromEnv(); + if (null != addresses) { + ConfigurationImpl configurationImpl = (ConfigurationImpl) configuration; + configurationImpl.setDefault(); + configurationImpl.getGlobal().getServerConnector().setAddresses(Arrays.asList(addresses.clone())); + } + return configuration; + } + + public static Configuration createSimpleConfiguration(int port) { + ConfigurationImpl configuration = new ConfigurationImpl(); + configuration.setDefault(); + configuration.getGlobal().getServerConnector().setAddresses( + Collections.singletonList(String.format("127.0.0.1:%d", port))); + return configuration; + } +} diff --git a/polaris-test/polaris-test-mock-discovery/pom.xml b/polaris-test/polaris-test-mock-discovery/pom.xml new file mode 100644 index 000000000..f2c05a192 --- /dev/null +++ b/polaris-test/polaris-test-mock-discovery/pom.xml @@ -0,0 +1,22 @@ + + + + polaris-test + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-test-mock-discovery + + + + com.tencent.nameservice + polaris-protobuf + ${project.version} + + + \ No newline at end of file diff --git a/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/HeaderInterceptor.java b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/HeaderInterceptor.java new file mode 100644 index 000000000..42b9f932e --- /dev/null +++ b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/HeaderInterceptor.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.test.mock.discovery; + +import io.grpc.ForwardingServerCall; +import io.grpc.Metadata; +import io.grpc.ServerCall; +import io.grpc.ServerCallHandler; +import io.grpc.ServerInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HeaderInterceptor implements ServerInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(HeaderInterceptor.class); + + @Override + public ServerCall.Listener interceptCall( + ServerCall call, Metadata headers, ServerCallHandler next) { + LOG.debug("header received from client:" + headers); + return next.startCall(new ForwardingServerCall.SimpleForwardingServerCall(call) {}, headers); + } +} diff --git a/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/LocationInfo.java b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/LocationInfo.java new file mode 100644 index 000000000..17e858eed --- /dev/null +++ b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/LocationInfo.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.test.mock.discovery; + +public class LocationInfo { + private final String region; + + private final String zone; + + private final String campus; + + public LocationInfo(String region, String zone, String campus) { + this.region = region; + this.zone = zone; + this.campus = campus; + } + + public String getRegion() { + return region; + } + + public String getZone() { + return zone; + } + + public String getCampus() { + return campus; + } +} diff --git a/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingServer.java b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingServer.java new file mode 100644 index 000000000..1d0518cef --- /dev/null +++ b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingServer.java @@ -0,0 +1,80 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.test.mock.discovery; + +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.pojo.Node; +import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.ServerInterceptors; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NamingServer { + + private static final Logger LOG = LoggerFactory.getLogger(NamingServer.class); + + private final Server server; + + private final int port; + + private final NamingService namingService; + + public NamingServer(int port) { + namingService = new NamingService(); + server = ServerBuilder.forPort(port).addService( + ServerInterceptors.intercept(namingService, new HeaderInterceptor())).build(); + this.port = port; + } + + public void start() throws IOException { + server.start(); + LOG.info(String.format("server start listening on %d", port)); + } + + public int getPort() { + return port; + } + + public void terminate() { + server.shutdown(); + } + + public NamingService getNamingService() { + return namingService; + } + + public static NamingServer startNamingServer(int port) throws IOException { + NamingServer namingServer = new NamingServer(port); + namingServer.start(); + Node node = new Node("127.0.0.1", port); + InstanceParameter parameter = new InstanceParameter(); + parameter.setHealthy(true); + parameter.setIsolated(false); + parameter.setProtocol("grpc"); + parameter.setWeight(100); + // 注册系统集群地址 + namingServer.getNamingService().addInstance( + new ServiceKey("Polaris", "polaris.discover"), node, parameter); + namingServer.getNamingService().addInstance( + new ServiceKey("Polaris", "polaris.healthcheck"), node, parameter); + return namingServer; + } +} diff --git a/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingService.java b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingService.java new file mode 100644 index 000000000..6242c1ba6 --- /dev/null +++ b/polaris-test/polaris-test-mock-discovery/src/main/java/com/tencent/polaris/test/mock/discovery/NamingService.java @@ -0,0 +1,441 @@ +/* + * Tencent is pleased to support the open source community by making Polaris available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package com.tencent.polaris.test.mock.discovery; + +import com.google.protobuf.BoolValue; +import com.google.protobuf.StringValue; +import com.google.protobuf.UInt32Value; +import com.tencent.polaris.api.exception.ServerCodes; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.utils.CollectionUtils; +import com.tencent.polaris.api.utils.StringUtils; +import com.tencent.polaris.client.pb.CircuitBreakerProto; +import com.tencent.polaris.client.pb.CircuitBreakerProto.CircuitBreaker; +import com.tencent.polaris.client.pb.ModelProto; +import com.tencent.polaris.client.pb.ModelProto.Location; +import com.tencent.polaris.client.pb.PolarisGRPCGrpc; +import com.tencent.polaris.client.pb.RateLimitProto; +import com.tencent.polaris.client.pb.RateLimitProto.RateLimit; +import com.tencent.polaris.client.pb.RequestProto.DiscoverRequest; +import com.tencent.polaris.client.pb.ResponseProto; +import com.tencent.polaris.client.pb.ResponseProto.DiscoverResponse.DiscoverResponseType; +import com.tencent.polaris.client.pb.RoutingProto; +import com.tencent.polaris.client.pb.ServiceProto; +import com.tencent.polaris.client.pb.ServiceProto.Instance; +import com.tencent.polaris.client.pojo.Node; +import io.grpc.stub.StreamObserver; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NamingService extends PolarisGRPCGrpc.PolarisGRPCImplBase { + + private static final Logger LOG = LoggerFactory.getLogger(NamingService.class); + + private final Map> services = new ConcurrentHashMap<>(); + + private final Map serviceRoutings = new ConcurrentHashMap<>(); + + private final Map serviceCircuitBreakers = new ConcurrentHashMap<>(); + + private final Map serviceRateLimits = new ConcurrentHashMap<>(); + + public void addService(ServiceKey serviceKey) { + services.put(serviceKey, new ArrayList<>()); + } + + public void setRouting(ServiceKey serviceKey, RoutingProto.Routing routing) { + serviceRoutings.put(serviceKey, routing); + } + + public void setCircuitBreaker(ServiceKey serviceKey, CircuitBreaker circuitBreaker) { + serviceCircuitBreakers.put(serviceKey, circuitBreaker); + } + + public void setRateLimit(ServiceKey serviceKey, RateLimit rateLimit) { + serviceRateLimits.put(serviceKey, rateLimit); + } + + public static class InstanceParameter { + + private boolean healthy; + private boolean isolated; + private int weight; + private String protocol; + private String version; + private LocationInfo locationInfo; + private Map metadata; + + public boolean isHealthy() { + return healthy; + } + + public void setHealthy(boolean healthy) { + this.healthy = healthy; + } + + public boolean isIsolated() { + return isolated; + } + + public void setIsolated(boolean isolated) { + this.isolated = isolated; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public LocationInfo getLocationInfo() { + return locationInfo; + } + + public void setLocationInfo(LocationInfo locationInfo) { + this.locationInfo = locationInfo; + } + + public Map getMetadata() { + return metadata; + } + + public void setMetadata(Map metadata) { + this.metadata = metadata; + } + } + + private ServiceProto.Instance buildInstance(ServiceKey svcKey, Node node, InstanceParameter parameter) { + ServiceProto.Instance.Builder builder = ServiceProto.Instance.newBuilder(); + String instId = UUID.randomUUID().toString(); + builder.setId(StringValue.newBuilder().setValue(instId).build()); + builder.setNamespace(StringValue.newBuilder().setValue(svcKey.getNamespace()).build()); + builder.setService(StringValue.newBuilder().setValue(svcKey.getService()).build()); + builder.setHost(StringValue.newBuilder().setValue(node.getHost()).build()); + builder.setPort(UInt32Value.newBuilder().setValue(node.getPort()).build()); + builder.setHealthy(BoolValue.newBuilder().setValue(parameter.isHealthy()).build()); + builder.setIsolate(BoolValue.newBuilder().setValue(parameter.isIsolated()).build()); + if (StringUtils.isNotBlank(parameter.getProtocol())) { + builder.setProtocol(StringValue.newBuilder().setValue(parameter.getProtocol()).build()); + builder.putMetadata("protocol", parameter.getProtocol()); + } + if (StringUtils.isNotBlank(parameter.getVersion())) { + builder.setVersion(StringValue.newBuilder().setValue(parameter.getVersion()).build()); + builder.putMetadata("version", parameter.getVersion()); + } + builder.setWeight(UInt32Value.newBuilder().setValue(parameter.getWeight()).build()); + LocationInfo locationInfo = parameter.getLocationInfo(); + if (null != locationInfo) { + Location.Builder locationBuilder = ModelProto.Location.newBuilder(); + locationBuilder.setRegion(StringValue.newBuilder().setValue(locationInfo.getRegion()).build()); + locationBuilder.setZone(StringValue.newBuilder().setValue(locationInfo.getZone()).build()); + locationBuilder.setCampus(StringValue.newBuilder().setValue(locationInfo.getCampus()).build()); + builder.setLocation(locationBuilder.build()); + } + Map metadata = parameter.getMetadata(); + if (null != metadata) { + builder.putAllMetadata(metadata); + } + return builder.build(); + } + + public void addInstance(ServiceKey svcKey, Node node, InstanceParameter parameter) { + ServiceProto.Instance instance = buildInstance(svcKey, node, parameter); + List existsInstances = services.get(svcKey); + if (null == existsInstances) { + List instances = new ArrayList<>(); + instances.add(instance); + services.put(svcKey, instances); + } else { + existsInstances.add(instance); + } + } + + /** + * 批量增加服务实例 + * + * @param svcKey 服务名 + * @param portStart 起始端口 + * @param instCount 实例数 + * @param parameter 实例参数 + * @return 批量服务实例的IP和端口 + */ + public List batchAddInstances(ServiceKey svcKey, int portStart, int instCount, InstanceParameter parameter) { + List nodes = new ArrayList<>(); + List instances = new ArrayList<>(); + for (int i = 0; i < instCount; i++) { + Node node = new Node("127.0.0.1", portStart + i); + ServiceProto.Instance nextInstance = buildInstance(svcKey, node, parameter); + instances.add(nextInstance); + nodes.add(node); + } + List existsInstances = services.get(svcKey); + if (null == existsInstances) { + services.put(svcKey, instances); + } else { + existsInstances.addAll(instances); + } + return nodes; + } + + public void setInstanceHealthyStatus( + ServiceKey svcKey, Node node, Boolean healthyStatus, Boolean isolated, Integer weight) { + List instances = services.get(svcKey); + if (CollectionUtils.isEmpty(instances)) { + return; + } + List newInstances = new ArrayList<>(); + instances.forEach(instance -> { + if (StringUtils.equals(node.getHost(), instance.getHost().getValue()) + && node.getPort() == instance.getPort().getValue()) { + ServiceProto.Instance.Builder builder = instance.toBuilder(); + if (null != healthyStatus) { + builder.setHealthy(BoolValue.newBuilder().setValue(healthyStatus)); + } + if (null != isolated) { + builder.setIsolate(BoolValue.newBuilder().setValue(isolated).build()); + } + if (null != weight) { + builder.setWeight(UInt32Value.newBuilder().setValue(weight).build()); + } + newInstances.add(builder.build()); + } else { + newInstances.add(instance); + } + }); + services.put(svcKey, newInstances); + } + + @Override + public void registerInstance(ServiceProto.Instance request, + StreamObserver responseObserver) { + ServiceKey serviceKey = new ServiceKey(request.getNamespace().getValue(), request.getService().getValue()); + if (!services.containsKey(serviceKey)) { + responseObserver.onNext( + buildResponse(ServerCodes.NOT_FOUND_RESOURCE, String.format("service %s not found", serviceKey), + request)); + responseObserver.onCompleted(); + return; + } + List instances = services.get(serviceKey); + if (CollectionUtils.isNotEmpty(instances)) { + for (ServiceProto.Instance instance : instances) { + if (instance.getHost().getValue().equals(request.getHost().getValue()) && + instance.getPort().getValue() == request.getPort().getValue()) { + responseObserver.onNext(buildResponse(ServerCodes.EXISTED_RESOURCE, + String.format("instance %s:%d exists", request.getHost().getValue(), + request.getPort().getValue()), instance)); + responseObserver.onCompleted(); + return; + } + } + } + ServiceProto.Instance.Builder builder = ServiceProto.Instance.newBuilder(); + builder.mergeFrom(request); + String instId = UUID.randomUUID().toString(); + builder.setId(StringValue.newBuilder().setValue(instId).build()); + ServiceProto.Instance nextInstance = builder.build(); + instances.add(nextInstance); + + ResponseProto.Response.Builder response = ResponseProto.Response.newBuilder(); + response.setCode(UInt32Value.newBuilder().setValue(ServerCodes.EXECUTE_SUCCESS).build()); + response.setInstance(nextInstance); + responseObserver.onNext(response.build()); + responseObserver.onCompleted(); + } + + + private ResponseProto.Response buildResponse(int code, String info, ServiceProto.Instance instance) { + ResponseProto.Response.Builder response = ResponseProto.Response.newBuilder(); + response.setCode(UInt32Value.newBuilder().setValue(code).build()); + if (StringUtils.isNotBlank(info)) { + response.setInfo(StringValue.newBuilder().setValue(info).build()); + } + if (null != instance) { + response.setInstance(instance); + } + return response.build(); + } + + @Override + public void deregisterInstance(ServiceProto.Instance request, + StreamObserver responseObserver) { + ServiceKey serviceKey = new ServiceKey(request.getNamespace().getValue(), request.getService().getValue()); + if (!services.containsKey(serviceKey)) { + responseObserver.onNext( + buildResponse(ServerCodes.NOT_FOUND_RESOURCE, String.format("service %s not found", serviceKey), + request)); + responseObserver.onCompleted(); + return; + } + int rIndex = -1; + List instances = services.get(serviceKey); + for (int i = 0; i < instances.size(); i++) { + ServiceProto.Instance instance = instances.get(i); + if (StringUtils.isNotBlank(request.getId().getValue())) { + if (StringUtils.equals(request.getId().getValue(), request.getId().getValue())) { + rIndex = i; + break; + } + } else if (StringUtils.equals(request.getHost().getValue(), instance.getHost().getValue()) + && request.getPort().getValue() == instance.getPort().getValue()) { + rIndex = i; + break; + } + } + if (rIndex != -1) { + instances.remove(rIndex); + } + if (CollectionUtils.isEmpty(instances)) { + //实例被删光则删除服务 + services.remove(serviceKey); + } + ResponseProto.Response.Builder response = ResponseProto.Response.newBuilder(); + response.setCode(UInt32Value.newBuilder().setValue(ServerCodes.EXECUTE_SUCCESS).build()); + response.setInstance(request); + responseObserver.onNext(response.build()); + responseObserver.onCompleted(); + } + + @Override + public void heartbeat(ServiceProto.Instance request, StreamObserver responseObserver) { + ServiceKey serviceKey = new ServiceKey(request.getNamespace().getValue(), request.getService().getValue()); + if (!services.containsKey(serviceKey)) { + responseObserver.onNext( + buildResponse(ServerCodes.NOT_FOUND_RESOURCE, String.format("service %s not found", serviceKey), + request)); + responseObserver.onCompleted(); + return; + } + ResponseProto.Response.Builder response = ResponseProto.Response.newBuilder(); + response.setCode(UInt32Value.newBuilder().setValue(ServerCodes.EXECUTE_SUCCESS).build()); + response.setInstance(request); + responseObserver.onNext(response.build()); + responseObserver.onCompleted(); + } + + private ResponseProto.DiscoverResponse buildServiceResponse( + int code, String info, DiscoverRequest req) { + + ResponseProto.DiscoverResponse.Builder builder = ResponseProto.DiscoverResponse.newBuilder(); + builder.setCode(UInt32Value.newBuilder().setValue(code).build()); + + CircuitBreakerProto.CircuitBreaker circuitBreaker; + RateLimitProto.RateLimit rateLimit; + RoutingProto.Routing routing; + List instances; + + ServiceProto.Service service = req.getService(); + ServiceKey serviceKey = new ServiceKey(service.getNamespace().getValue(), service.getName().getValue()); + + switch (req.getType()) { + case INSTANCE: + instances = services.get(serviceKey); + if (CollectionUtils.isNotEmpty(instances)) { + builder.addAllInstances(instances); + } + builder.setType(DiscoverResponseType.INSTANCE); + break; + case ROUTING: + routing = serviceRoutings.get(serviceKey); + if (null != routing) { + builder.setRouting(routing); + } + builder.setType(DiscoverResponseType.ROUTING); + break; + case CIRCUIT_BREAKER: + circuitBreaker = serviceCircuitBreakers.get(serviceKey); + if (null != circuitBreaker) { + builder.setCircuitBreaker(circuitBreaker); + } + builder.setType(DiscoverResponseType.CIRCUIT_BREAKER); + break; + case RATE_LIMIT: + rateLimit = serviceRateLimits.get(serviceKey); + if (null != rateLimit) { + builder.setRateLimit(rateLimit); + } + builder.setType(DiscoverResponseType.RATE_LIMIT); + break; + default: + break; + } + if (StringUtils.isNotBlank(info)) { + builder.setInfo(StringValue.newBuilder().setValue(info).build()); + } + builder.setService(service); + + return builder.build(); + } + + + @Override + public StreamObserver discover(StreamObserver responseObserver) { + return new StreamObserver() { + + @Override + public void onNext(DiscoverRequest req) { + ServiceProto.Service service = req.getService(); + ServiceKey serviceKey = new ServiceKey(service.getNamespace().getValue(), service.getName().getValue()); + if (!services.containsKey(serviceKey)) { + responseObserver.onNext( + buildServiceResponse(ServerCodes.NOT_FOUND_RESOURCE, + String.format("service %s not found", serviceKey), req)); + return; + } + responseObserver.onNext( + buildServiceResponse(ServerCodes.EXECUTE_SUCCESS, + "", req)); + } + + @Override + public void onError(Throwable t) { + LOG.error("receive client error", t); + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } + + +} diff --git a/polaris-test/pom.xml b/polaris-test/pom.xml new file mode 100644 index 000000000..c6cfbd0c5 --- /dev/null +++ b/polaris-test/pom.xml @@ -0,0 +1,28 @@ + + + + polaris-parent + com.tencent.nameservice + ${revision} + ../pom.xml + + 4.0.0 + + polaris-test + pom + + polaris-test-common + polaris-test-mock-discovery + + + + + com.tencent.nameservice + polaris-config + ${project.version} + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..a4ac86bb0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,185 @@ + + + + com.tencent.nameservice + polaris-parent + pom + ${revision} + + polaris-client + polaris-factory + polaris-dependencies + polaris-common + polaris-plugins + polaris-discovery + polaris-circuitbreaker + polaris-ratelimit + polaris-test + polaris-router + polaris-distribution + + 4.0.0 + + + + 2020.0.0-SNAPSHOT + ${maven.build.timestamp} + yyyy-MM-dd HH:mm + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 2.9.9 + 1.3.2 + 1.33.0 + 3.1.0 + 2.6 + 29.0-android + 1.7.25 + 2.12.0 + 4.12 + -Xmx2048m + 0.11.0 + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + test + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + test + + + junit + junit + ${junit.version} + test + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.0 + + checkstyle/checkstyle.xml + checkstyle/suppressions.xml + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12.4 + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + + attach-javadocs + + jar + + + + + + com/tencent/polaris/client/pb/* + com/tencent/polaris/ratelimit/client/pb/* + + + + + date + + + + + + org.codehaus.mojo + flatten-maven-plugin + 1.1.0 + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + + + + + + tencent_public_snapshots + tencent_public_snapshot + https://mirrors.tencent.com/repository/maven/tencent_public_snapshots/ + + + + tencent_public + tencent_public_release + https://mirrors.tencent.com/repository/maven/tencent_public/ + + + + + \ No newline at end of file From d15173d4b988cf4db25ffdc9f64fa915dc7f6497 Mon Sep 17 00:00:00 2001 From: andrewshan Date: Thu, 29 Jul 2021 17:47:14 +0800 Subject: [PATCH 2/3] Delete RetryConnectTest.java --- .../discovery/test/core/RetryConnectTest.java | 115 ------------------ 1 file changed, 115 deletions(-) delete mode 100644 polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java diff --git a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java b/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java deleted file mode 100644 index 119f5e196..000000000 --- a/polaris-discovery/polaris-discovery-factory/src/test/java/com/tencent/polaris/discovery/test/core/RetryConnectTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Polaris available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * Unless required by applicable law or agreed to in writing, software distributed - * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - * CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package com.tencent.polaris.discovery.test.core; - -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -import static com.tencent.polaris.test.common.Consts.SERVICES; - -import com.tencent.polaris.api.config.Configuration; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.exception.PolarisException; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.api.rpc.GetInstancesRequest; -import com.tencent.polaris.client.util.Utils; -import com.tencent.polaris.factory.api.DiscoveryAPIFactory; -import com.tencent.polaris.factory.config.ConfigurationImpl; -import com.tencent.polaris.test.common.TestUtils; -import com.tencent.polaris.test.mock.discovery.NamingServer; -import com.tencent.polaris.test.mock.discovery.NamingService.InstanceParameter; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * RetryConnectTest.java - * - * @author andrewshan - * @date 2019/9/7 - */ -public class RetryConnectTest { - - private static final Logger LOG = LoggerFactory.getLogger(RetryConnectTest.class); - - private NamingServer namingServer; - - @Before - public void before() { - Thread startThread = new Thread(new Runnable() { - @Override - public void run() { - Utils.sleepUninterrupted(1000); - LOG.info("now start the naming server mock"); - try { - namingServer = NamingServer.startNamingServer(10081); - } catch (Exception e) { - Assert.fail(e.getMessage()); - } - LOG.info("finish starting the naming server mock"); - InstanceParameter parameter = new InstanceParameter(); - parameter.setHealthy(true); - parameter.setIsolated(false); - parameter.setWeight(100); - namingServer.getNamingService().batchAddInstances( - new ServiceKey(NAMESPACE_TEST, SERVICES[0]), 10100, 10, parameter); - } - }); - startThread.start(); - } - - @After - public void after() { - if (null != namingServer) { - namingServer.terminate(); - } - } - - public static Configuration createMaxRetryConfiguration() { - ConfigurationImpl configuration = (ConfigurationImpl) TestUtils.configWithEnvAddress(); - configuration.setDefault(); - configuration.getGlobal().getAPI().setMaxRetryTimes(50); - configuration.getGlobal().getAPI().setRetryInterval(100); - return configuration; - } - - @Test - public void testRetryConsumer() { - ConsumerAPI consumerAPI = null; - try { - Configuration simpleConfiguration = createMaxRetryConfiguration(); - consumerAPI = DiscoveryAPIFactory.createConsumerAPIByConfig(simpleConfiguration); - Assert.assertNotNull(consumerAPI); - GetInstancesRequest req = new GetInstancesRequest(); - req.setNamespace(NAMESPACE_TEST); - req.setService(SERVICES[0]); - req.setTimeoutMs(5000); - long startTime = System.currentTimeMillis(); - consumerAPI.getInstances(req); - long endTime = System.currentTimeMillis(); - LOG.info(String.format("testGetInstances time consumer is %dms", endTime - startTime)); - } catch (PolarisException e) { - Assert.fail(e.getMessage()); - } finally { - if (null != consumerAPI) { - consumerAPI.destroy(); - } - } - } -} \ No newline at end of file From eb21e010a0dcf4a5778eba83d2df3ee5a468d3c6 Mon Sep 17 00:00:00 2001 From: andrewshan Date: Thu, 29 Jul 2021 18:00:49 +0800 Subject: [PATCH 3/3] update precise for ratelimit --- .../com/tencent/polaris/ratelimit/test/core/Consts.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java b/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java index a3032dc4b..f7e7b3e59 100644 --- a/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java +++ b/polaris-ratelimit/polaris-ratelimit-factory/src/test/java/com/tencent/polaris/ratelimit/test/core/Consts.java @@ -32,11 +32,11 @@ public interface Consts { String METHOD_CASH = "cash"; - int MAX_PAY_COUNT = 1000; + int MAX_PAY_COUNT = 2000; - int MAX_CASH_COUNT = 2000; + int MAX_CASH_COUNT = 4000; - int MAX_SERVICE_COUNT = 10000; + int MAX_SERVICE_COUNT = 15000; /** * 通过数组创建Map